diff --git a/DEPS b/DEPS
index ddee0bc4..feb40e2b 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'df669811b16e317b307b335822c4c7c881c1d163',
+  'skia_revision': 'fb49909acafba5e031b90a265a6ce059cda85019',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '71c20abb86b91696bf5d96eadc5290d4c0c566f0',
+  'v8_revision': '65ef03f8a0fb9bd4f2cfa9afe60ba312cbb3ea08',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'd4f2d777c7aa4d9454a74afe92ff17e13da62380',
+  'catapult_revision': 'b4eb70c319cb0f38d1a7764e7d9fb56dc96f8bcf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/ash/accelerators/exit_warning_handler.cc b/ash/accelerators/exit_warning_handler.cc
index e240ec0..6e25bbe 100644
--- a/ash/accelerators/exit_warning_handler.cc
+++ b/ash/accelerators/exit_warning_handler.cc
@@ -4,11 +4,11 @@
 
 #include "ash/accelerators/exit_warning_handler.h"
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -100,13 +100,13 @@
       state_ = WAIT_FOR_DOUBLE_PRESS;
       Show();
       StartTimer();
-      ShellPort::Get()->RecordUserMetricsAction(UMA_ACCEL_EXIT_FIRST_Q);
+      Shell::Get()->metrics()->RecordUserMetricsAction(UMA_ACCEL_EXIT_FIRST_Q);
       break;
     case WAIT_FOR_DOUBLE_PRESS:
       state_ = EXITING;
       CancelTimer();
       Hide();
-      ShellPort::Get()->RecordUserMetricsAction(UMA_ACCEL_EXIT_SECOND_Q);
+      Shell::Get()->metrics()->RecordUserMetricsAction(UMA_ACCEL_EXIT_SECOND_Q);
       Shell::Get()->shell_delegate()->Exit();
       break;
     case EXITING:
diff --git a/ash/aura/shell_port_classic.cc b/ash/aura/shell_port_classic.cc
index f669e5a..4772034 100644
--- a/ash/aura/shell_port_classic.cc
+++ b/ash/aura/shell_port_classic.cc
@@ -15,11 +15,9 @@
 #include "ash/keyboard/keyboard_ui.h"
 #include "ash/laser/laser_pointer_controller.h"
 #include "ash/magnifier/partial_magnification_controller.h"
-#include "ash/metrics/task_switch_metrics_recorder.h"
 #include "ash/public/cpp/config.h"
 #include "ash/shared/immersive_fullscreen_controller.h"
 #include "ash/shell.h"
-#include "ash/touch/touch_uma.h"
 #include "ash/virtual_keyboard_controller.h"
 #include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/maximize_mode/maximize_mode_event_handler_aura.h"
@@ -101,18 +99,6 @@
   return Shell::Get()->cursor_manager()->IsMouseEventsEnabled();
 }
 
-void ShellPortClassic::RecordUserMetricsAction(UserMetricsAction action) {
-  Shell::Get()->metrics()->RecordUserMetricsAction(action);
-}
-
-void ShellPortClassic::RecordGestureAction(GestureActionType action) {
-  TouchUMA::GetInstance()->RecordGestureAction(action);
-}
-
-void ShellPortClassic::RecordTaskSwitchMetric(TaskSwitchSource source) {
-  Shell::Get()->metrics()->task_switch_metrics_recorder().OnTaskSwitch(source);
-}
-
 std::unique_ptr<WindowResizer> ShellPortClassic::CreateDragWindowResizer(
     std::unique_ptr<WindowResizer> next_window_resizer,
     wm::WindowState* window_state) {
diff --git a/ash/aura/shell_port_classic.h b/ash/aura/shell_port_classic.h
index c007a69..2299cff1 100644
--- a/ash/aura/shell_port_classic.h
+++ b/ash/aura/shell_port_classic.h
@@ -42,9 +42,6 @@
   void SetCursorSize(ui::CursorSize cursor_size) override;
   void SetGlobalOverrideCursor(base::Optional<ui::CursorData> cursor) override;
   bool IsMouseEventsEnabled() override;
-  void RecordGestureAction(GestureActionType action) override;
-  void RecordUserMetricsAction(UserMetricsAction action) override;
-  void RecordTaskSwitchMetric(TaskSwitchSource source) override;
   std::unique_ptr<WindowResizer> CreateDragWindowResizer(
       std::unique_ptr<WindowResizer> next_window_resizer,
       wm::WindowState* window_state) override;
diff --git a/ash/frame/caption_buttons/frame_caption_button_container_view.cc b/ash/frame/caption_buttons/frame_caption_button_container_view.cc
index 7f529cd..730cee9 100644
--- a/ash/frame/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/frame/caption_buttons/frame_caption_button_container_view.cc
@@ -9,8 +9,9 @@
 
 #include "ash/frame/caption_buttons/frame_caption_button.h"
 #include "ash/frame/caption_buttons/frame_size_button.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
+#include "ash/touch/touch_uma.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -334,7 +335,7 @@
     }
 
     if (event.IsGestureEvent())
-      ShellPort::Get()->RecordGestureAction(GESTURE_FRAMEMAXIMIZE_TAP);
+      TouchUMA::GetInstance()->RecordGestureAction(GESTURE_FRAMEMAXIMIZE_TAP);
   } else if (sender == close_button_) {
     frame_->Close();
     action = UMA_WINDOW_CLOSE_BUTTON_CLICK;
@@ -346,7 +347,7 @@
   } else {
     return;
   }
-  ShellPort::Get()->RecordUserMetricsAction(action);
+  Shell::Get()->metrics()->RecordUserMetricsAction(action);
 }
 
 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const {
diff --git a/ash/frame/caption_buttons/frame_size_button.cc b/ash/frame/caption_buttons/frame_size_button.cc
index 5da8501..071a6c0 100644
--- a/ash/frame/caption_buttons/frame_size_button.cc
+++ b/ash/frame/caption_buttons/frame_size_button.cc
@@ -4,7 +4,8 @@
 
 #include "ash/frame/caption_buttons/frame_size_button.h"
 
-#include "ash/shell_port.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
@@ -251,7 +252,7 @@
                                      ? wm::WM_EVENT_SNAP_LEFT
                                      : wm::WM_EVENT_SNAP_RIGHT);
     window_state->OnWMEvent(&snap_event);
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         snap_type_ == SNAP_LEFT ? UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT
                                 : UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT);
     SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_NO);
diff --git a/ash/metrics/desktop_task_switch_metric_recorder.cc b/ash/metrics/desktop_task_switch_metric_recorder.cc
index e93f51e..4d9cda0 100644
--- a/ash/metrics/desktop_task_switch_metric_recorder.cc
+++ b/ash/metrics/desktop_task_switch_metric_recorder.cc
@@ -4,8 +4,8 @@
 
 #include "ash/metrics/desktop_task_switch_metric_recorder.h"
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/wm/window_util.h"
 #include "ui/wm/public/activation_client.h"
 
@@ -28,7 +28,7 @@
     if (last_active_task_window_ != gained_active &&
         reason ==
             ::wm::ActivationChangeObserver::ActivationReason::INPUT_EVENT) {
-      ShellPort::Get()->RecordUserMetricsAction(UMA_DESKTOP_SWITCH_TASK);
+      Shell::Get()->metrics()->RecordUserMetricsAction(UMA_DESKTOP_SWITCH_TASK);
     }
     last_active_task_window_ = gained_active;
   }
diff --git a/ash/mus/bridge/shell_port_mash.cc b/ash/mus/bridge/shell_port_mash.cc
index 965c70f..1e74a19 100644
--- a/ash/mus/bridge/shell_port_mash.cc
+++ b/ash/mus/bridge/shell_port_mash.cc
@@ -31,7 +31,6 @@
 #include "ash/shared/immersive_fullscreen_controller.h"
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_delegate.h"
-#include "ash/touch/touch_uma.h"
 #include "ash/virtual_keyboard_controller.h"
 #include "ash/wallpaper/wallpaper_delegate.h"
 #include "ash/wm/drag_window_resizer.h"
@@ -163,34 +162,6 @@
   return true;
 }
 
-void ShellPortMash::RecordGestureAction(GestureActionType action) {
-  if (GetAshConfig() == Config::MUS) {
-    TouchUMA::GetInstance()->RecordGestureAction(action);
-    return;
-  }
-  // TODO: http://crbug.com/616581.
-  NOTIMPLEMENTED();
-}
-
-void ShellPortMash::RecordUserMetricsAction(UserMetricsAction action) {
-  if (GetAshConfig() == Config::MUS) {
-    Shell::Get()->metrics()->RecordUserMetricsAction(action);
-    return;
-  }
-  // TODO: http://crbug.com/616581.
-  NOTIMPLEMENTED();
-}
-
-void ShellPortMash::RecordTaskSwitchMetric(TaskSwitchSource source) {
-  if (GetAshConfig() == Config::MUS) {
-    Shell::Get()->metrics()->task_switch_metrics_recorder().OnTaskSwitch(
-        source);
-    return;
-  }
-  // TODO: http://crbug.com/616581.
-  NOTIMPLEMENTED();
-}
-
 std::unique_ptr<WindowResizer> ShellPortMash::CreateDragWindowResizer(
     std::unique_ptr<WindowResizer> next_window_resizer,
     wm::WindowState* window_state) {
diff --git a/ash/mus/bridge/shell_port_mash.h b/ash/mus/bridge/shell_port_mash.h
index 83028f1..7f8160a 100644
--- a/ash/mus/bridge/shell_port_mash.h
+++ b/ash/mus/bridge/shell_port_mash.h
@@ -67,9 +67,6 @@
   void SetCursorSize(ui::CursorSize cursor_size) override;
   void SetGlobalOverrideCursor(base::Optional<ui::CursorData> cursor) override;
   bool IsMouseEventsEnabled() override;
-  void RecordGestureAction(GestureActionType action) override;
-  void RecordUserMetricsAction(UserMetricsAction action) override;
-  void RecordTaskSwitchMetric(TaskSwitchSource source) override;
   std::unique_ptr<WindowResizer> CreateDragWindowResizer(
       std::unique_ptr<WindowResizer> next_window_resizer,
       wm::WindowState* window_state) override;
diff --git a/ash/shelf/shelf_alignment_menu.cc b/ash/shelf/shelf_alignment_menu.cc
index ccd8130..a5c6d07 100644
--- a/ash/shelf/shelf_alignment_menu.cc
+++ b/ash/shelf/shelf_alignment_menu.cc
@@ -5,9 +5,10 @@
 #include "ash/shelf/shelf_alignment_menu.h"
 
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/shelf/shelf.h"
-#include "ash/shell_port.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 
 namespace ash {
@@ -48,15 +49,18 @@
 void ShelfAlignmentMenu::ExecuteCommand(int command_id, int event_flags) {
   switch (static_cast<MenuItem>(command_id)) {
     case MENU_ALIGN_LEFT:
-      ShellPort::Get()->RecordUserMetricsAction(UMA_SHELF_ALIGNMENT_SET_LEFT);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_SHELF_ALIGNMENT_SET_LEFT);
       shelf_->SetAlignment(SHELF_ALIGNMENT_LEFT);
       break;
     case MENU_ALIGN_BOTTOM:
-      ShellPort::Get()->RecordUserMetricsAction(UMA_SHELF_ALIGNMENT_SET_BOTTOM);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_SHELF_ALIGNMENT_SET_BOTTOM);
       shelf_->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
       break;
     case MENU_ALIGN_RIGHT:
-      ShellPort::Get()->RecordUserMetricsAction(UMA_SHELF_ALIGNMENT_SET_RIGHT);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_SHELF_ALIGNMENT_SET_RIGHT);
       shelf_->SetAlignment(SHELF_ALIGNMENT_RIGHT);
       break;
   }
diff --git a/ash/shelf/shelf_button_pressed_metric_tracker.cc b/ash/shelf/shelf_button_pressed_metric_tracker.cc
index 08a93e1..d7f29ff7 100644
--- a/ash/shelf/shelf_button_pressed_metric_tracker.cc
+++ b/ash/shelf/shelf_button_pressed_metric_tracker.cc
@@ -4,7 +4,8 @@
 
 #include "ash/shelf/shelf_button_pressed_metric_tracker.h"
 
-#include "ash/shell_port.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_tick_clock.h"
 #include "ui/views/controls/button/button.h"
@@ -48,10 +49,10 @@
 void ShelfButtonPressedMetricTracker::RecordButtonPressedSource(
     const ui::Event& event) {
   if (event.IsMouseEvent()) {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_LAUNCHER_BUTTON_PRESSED_WITH_MOUSE);
   } else if (event.IsGestureEvent()) {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_LAUNCHER_BUTTON_PRESSED_WITH_TOUCH);
   }
 }
@@ -63,13 +64,16 @@
     case SHELF_ACTION_APP_LIST_SHOWN:
       break;
     case SHELF_ACTION_NEW_WINDOW_CREATED:
-      ShellPort::Get()->RecordUserMetricsAction(UMA_LAUNCHER_LAUNCH_TASK);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_LAUNCHER_LAUNCH_TASK);
       break;
     case SHELF_ACTION_WINDOW_ACTIVATED:
-      ShellPort::Get()->RecordUserMetricsAction(UMA_LAUNCHER_SWITCH_TASK);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_LAUNCHER_SWITCH_TASK);
       break;
     case SHELF_ACTION_WINDOW_MINIMIZED:
-      ShellPort::Get()->RecordUserMetricsAction(UMA_LAUNCHER_MINIMIZE_TASK);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_LAUNCHER_MINIMIZE_TASK);
       break;
   }
 }
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index c11b9eb..acbef66d 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -9,6 +9,7 @@
 
 #include "ash/ash_constants.h"
 #include "ash/drag_drop/drag_image_view.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_model.h"
@@ -442,11 +443,12 @@
     case TYPE_PINNED_APP:
     case TYPE_BROWSER_SHORTCUT:
     case TYPE_APP:
-      ShellPort::Get()->RecordUserMetricsAction(UMA_LAUNCHER_CLICK_ON_APP);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_LAUNCHER_CLICK_ON_APP);
       break;
 
     case TYPE_APP_LIST:
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);
       break;
 
diff --git a/ash/shell.cc b/ash/shell.cc
index 97aa2e7..f647df2 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -628,8 +628,7 @@
 
   const Config config = shell_port_->GetAshConfig();
 
-  if (config != Config::MASH)
-    user_metrics_recorder_->OnShellShuttingDown();
+  user_metrics_recorder_->OnShellShuttingDown();
 
   shell_delegate_->PreShutdown();
 
@@ -1112,8 +1111,7 @@
   for (auto& observer : shell_observers_)
     observer.OnShellInitialized();
 
-  if (config != Config::MASH)
-    user_metrics_recorder_->OnShellInitialized();
+  user_metrics_recorder_->OnShellInitialized();
 }
 
 void Shell::InitRootWindow(aura::Window* root_window) {
diff --git a/ash/shell_port.h b/ash/shell_port.h
index a4632615..c029491 100644
--- a/ash/shell_port.h
+++ b/ash/shell_port.h
@@ -11,8 +11,6 @@
 #include <vector>
 
 #include "ash/ash_export.h"
-#include "ash/metrics/gesture_action_type.h"
-#include "ash/metrics/user_metrics_action.h"
 #include "ash/wm/lock_state_observer.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
@@ -111,10 +109,6 @@
       base::Optional<ui::CursorData> cursor) = 0;
   virtual bool IsMouseEventsEnabled() = 0;
 
-  virtual void RecordGestureAction(GestureActionType action) = 0;
-  virtual void RecordUserMetricsAction(UserMetricsAction action) = 0;
-  virtual void RecordTaskSwitchMetric(TaskSwitchSource source) = 0;
-
   // Shows the context menu for the wallpaper or shelf at |location_in_screen|.
   void ShowContextMenu(const gfx::Point& location_in_screen,
                        ui::MenuSourceType source_type);
diff --git a/ash/system/audio/tray_audio.cc b/ash/system/audio/tray_audio.cc
index 0f3d880..ea3cd95 100644
--- a/ash/system/audio/tray_audio.cc
+++ b/ash/system/audio/tray_audio.cc
@@ -4,10 +4,10 @@
 
 #include "ash/system/audio/tray_audio.h"
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/system/audio/audio_detailed_view.h"
 #include "ash/system/audio/volume_view.h"
 #include "ash/system/tray/system_tray.h"
@@ -69,7 +69,7 @@
     volume_view_ = new tray::VolumeView(this, false);
     return volume_view_;
   } else {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_STATUS_AREA_DETAILED_AUDIO_VIEW);
     audio_detail_view_ = new tray::AudioDetailedView(this);
     return audio_detail_view_;
diff --git a/ash/system/audio/volume_view.cc b/ash/system/audio/volume_view.cc
index 1a7fad8..9dd9d15 100644
--- a/ash/system/audio/volume_view.cc
+++ b/ash/system/audio/volume_view.cc
@@ -7,8 +7,9 @@
 #include <algorithm>
 
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/shell_port.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/actionable_view.h"
 #include "ash/system/tray/system_tray_item.h"
@@ -267,7 +268,7 @@
     int current_volume = CrasAudioHandler::Get()->GetOutputVolumePercent();
     if (new_volume == current_volume)
       return;
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         is_default_view_ ? UMA_STATUS_AREA_CHANGED_VOLUME_MENU
                          : UMA_STATUS_AREA_CHANGED_VOLUME_POPUP);
     if (new_volume > current_volume)
diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc
index b665240..3a49c98 100644
--- a/ash/system/bluetooth/tray_bluetooth.cc
+++ b/ash/system/bluetooth/tray_bluetooth.cc
@@ -9,9 +9,9 @@
 #include <set>
 #include <string>
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/bluetooth/tray_bluetooth_helper.h"
 #include "ash/system/tray/hover_highlight_view.h"
@@ -388,7 +388,7 @@
                            const ui::Event& event) override {
     if (sender == toggle_) {
       TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           helper->GetBluetoothEnabled() ? UMA_STATUS_AREA_BLUETOOTH_DISABLED
                                         : UMA_STATUS_AREA_BLUETOOTH_ENABLED);
       helper->ToggleBluetoothEnabled();
@@ -545,7 +545,7 @@
 views::View* TrayBluetooth::CreateDetailedView(LoginStatus status) {
   if (!Shell::Get()->tray_bluetooth_helper()->GetBluetoothAvailable())
     return nullptr;
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_DETAILED_BLUETOOTH_VIEW);
   CHECK(detailed_ == nullptr);
   detailed_ = new tray::BluetoothDetailedView(this, status);
diff --git a/ash/system/brightness/tray_brightness.cc b/ash/system/brightness/tray_brightness.cc
index b4ad50f..b933437 100644
--- a/ash/system/brightness/tray_brightness.cc
+++ b/ash/system/brightness/tray_brightness.cc
@@ -6,10 +6,10 @@
 
 #include <algorithm>
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/shell_observer.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/brightness_control_delegate.h"
 #include "ash/system/tray/tray_constants.h"
@@ -216,7 +216,7 @@
 
 views::View* TrayBrightness::CreateDetailedView(LoginStatus status) {
   CHECK(brightness_view_ == nullptr);
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_DETAILED_BRIGHTNESS_VIEW);
   brightness_view_ = new tray::BrightnessView(false, current_percent_);
   return brightness_view_;
@@ -239,7 +239,8 @@
 }
 
 void TrayBrightness::BrightnessChanged(int level, bool user_initiated) {
-  ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_BRIGHTNESS_CHANGED);
+  Shell::Get()->metrics()->RecordUserMetricsAction(
+      UMA_STATUS_AREA_BRIGHTNESS_CHANGED);
   double percent = static_cast<double>(level);
   HandleBrightnessChanged(percent, user_initiated);
 }
diff --git a/ash/system/cast/tray_cast.cc b/ash/system/cast/tray_cast.cc
index f0ea1cc..348bec8e 100644
--- a/ash/system/cast/tray_cast.cc
+++ b/ash/system/cast/tray_cast.cc
@@ -9,10 +9,10 @@
 #include <utility>
 #include <vector>
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/interfaces/cast_config.mojom.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/screen_security/screen_tray_item.h"
 #include "ash/system/tray/hover_highlight_view.h"
@@ -129,7 +129,8 @@
 
 void CastCastView::StopCasting() {
   Shell::Get()->cast_config()->StopCasting(displayed_route_.Clone());
-  ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_CAST_STOP_CAST);
+  Shell::Get()->metrics()->RecordUserMetricsAction(
+      UMA_STATUS_AREA_CAST_STOP_CAST);
 }
 
 void CastCastView::UpdateLabel(
@@ -401,7 +402,7 @@
   auto it = view_to_sink_map_.find(view);
   if (it != view_to_sink_map_.end()) {
     Shell::Get()->cast_config()->CastToSink(it->second.Clone());
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_STATUS_AREA_DETAILED_CAST_VIEW_LAUNCH_CAST);
   }
 }
@@ -458,7 +459,8 @@
 }
 
 views::View* TrayCast::CreateDetailedView(LoginStatus status) {
-  ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_DETAILED_CAST_VIEW);
+  Shell::Get()->metrics()->RecordUserMetricsAction(
+      UMA_STATUS_AREA_DETAILED_CAST_VIEW);
   CHECK(detailed_ == nullptr);
   detailed_ = new tray::CastDetailedView(this, sinks_and_routes_);
   return detailed_;
diff --git a/ash/system/ime/tray_ime_chromeos.cc b/ash/system/ime/tray_ime_chromeos.cc
index 26baa3b..a5b3e82 100644
--- a/ash/system/ime/tray_ime_chromeos.cc
+++ b/ash/system/ime/tray_ime_chromeos.cc
@@ -8,9 +8,9 @@
 #include <vector>
 
 #include "ash/ime/ime_controller.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_controller.h"
@@ -128,7 +128,7 @@
   }
 
   void ShowSettings() {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_STATUS_AREA_IME_SHOW_DETAILED);
     Shell::Get()->system_tray_controller()->ShowIMESettings();
     if (owner()->system_tray())
diff --git a/ash/system/ime_menu/ime_list_view.cc b/ash/system/ime_menu/ime_list_view.cc
index 51f46ed..282e734f 100644
--- a/ash/system/ime_menu/ime_list_view.cc
+++ b/ash/system/ime_menu/ime_list_view.cc
@@ -6,6 +6,7 @@
 
 #include "ash/ime/ime_controller.h"
 #include "ash/ime/ime_switch_type.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/interfaces/ime_info.mojom.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
@@ -297,7 +298,8 @@
 void ImeListView::HandleViewClicked(views::View* view) {
   std::map<views::View*, std::string>::const_iterator ime = ime_map_.find(view);
   if (ime != ime_map_.end()) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_IME_SWITCH_MODE);
+    Shell::Get()->metrics()->RecordUserMetricsAction(
+        UMA_STATUS_AREA_IME_SWITCH_MODE);
     std::string ime_id = ime->second;
     last_selected_item_id_ = ime_id;
     InputMethodManager::Get()->GetActiveIMEState()->ChangeInputMethod(
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index d41378ac..53c376e 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -7,11 +7,11 @@
 #include "ash/accessibility_delegate.h"
 #include "ash/ash_constants.h"
 #include "ash/ime/ime_controller.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/ime_menu/ime_list_view.h"
 #include "ash/system/tray/system_menu_button.h"
@@ -56,7 +56,8 @@
 
 // Shows language and input settings page.
 void ShowIMESettings() {
-  ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_IME_SHOW_DETAILED);
+  Shell::Get()->metrics()->RecordUserMetricsAction(
+      UMA_STATUS_AREA_IME_SHOW_DETAILED);
   Shell::Get()->system_tray_controller()->ShowIMESettings();
 }
 
diff --git a/ash/system/keyboard_brightness_controller.cc b/ash/system/keyboard_brightness_controller.cc
index 95af101..7fc3b16 100644
--- a/ash/system/keyboard_brightness_controller.cc
+++ b/ash/system/keyboard_brightness_controller.cc
@@ -4,7 +4,8 @@
 
 #include "ash/system/keyboard_brightness_controller.h"
 
-#include "ash/shell_port.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_manager_client.h"
 #include "ui/base/accelerators/accelerator.h"
@@ -14,7 +15,7 @@
 void KeyboardBrightnessController::HandleKeyboardBrightnessDown(
     const ui::Accelerator& accelerator) {
   if (accelerator.key_code() == ui::VKEY_BRIGHTNESS_DOWN) {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_ACCEL_KEYBOARD_BRIGHTNESS_DOWN_F6);
   }
 
@@ -26,7 +27,7 @@
 void KeyboardBrightnessController::HandleKeyboardBrightnessUp(
     const ui::Accelerator& accelerator) {
   if (accelerator.key_code() == ui::VKEY_BRIGHTNESS_UP) {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_ACCEL_KEYBOARD_BRIGHTNESS_UP_F7);
   }
 
diff --git a/ash/system/network/network_list.cc b/ash/system/network/network_list.cc
index c87091d..34d04ff 100644
--- a/ash/system/network/network_list.cc
+++ b/ash/system/network/network_list.cc
@@ -7,9 +7,9 @@
 #include <memory>
 #include <utility>
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/network/network_icon.h"
 #include "ash/system/network/network_icon_animation.h"
@@ -262,7 +262,7 @@
 
   void ButtonPressed(views::Button* sender, const ui::Event& event) override {
     if (sender == join_) {
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           UMA_STATUS_AREA_NETWORK_JOIN_OTHER_CLICKED);
       Shell::Get()->system_tray_controller()->ShowNetworkCreate(
           shill::kTypeWifi);
diff --git a/ash/system/network/network_state_list_detailed_view.cc b/ash/system/network/network_state_list_detailed_view.cc
index 4aba214..2b103a59 100644
--- a/ash/system/network/network_state_list_detailed_view.cc
+++ b/ash/system/network/network_state_list_detailed_view.cc
@@ -6,9 +6,9 @@
 
 #include <algorithm>
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/system_menu_button.h"
 #include "ash/system/tray/system_tray.h"
@@ -222,14 +222,14 @@
       NetworkHandler::Get()->network_state_handler()->GetNetworkStateFromGuid(
           guid);
   if (!network || network->IsConnectedState() || network->IsConnectingState()) {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         list_type_ == LIST_TYPE_VPN
             ? UMA_STATUS_AREA_SHOW_VPN_CONNECTION_DETAILS
             : UMA_STATUS_AREA_SHOW_NETWORK_CONNECTION_DETAILS);
     Shell::Get()->system_tray_controller()->ShowNetworkSettings(
         network ? network->guid() : std::string());
   } else {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         list_type_ == LIST_TYPE_VPN
             ? UMA_STATUS_AREA_CONNECT_TO_VPN
             : UMA_STATUS_AREA_CONNECT_TO_CONFIGURED_NETWORK);
@@ -272,7 +272,7 @@
 }
 
 void NetworkStateListDetailedView::ShowSettings() {
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       list_type_ == LIST_TYPE_VPN ? UMA_STATUS_AREA_VPN_SETTINGS_OPENED
                                   : UMA_STATUS_AREA_NETWORK_SETTINGS_OPENED);
   Shell::Get()->system_tray_controller()->ShowNetworkSettings(std::string());
diff --git a/ash/system/network/tray_network.cc b/ash/system/network/tray_network.cc
index 6455d7357..b780785 100644
--- a/ash/system/network/tray_network.cc
+++ b/ash/system/network/tray_network.cc
@@ -4,8 +4,8 @@
 
 #include "ash/system/network/tray_network.h"
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/network/network_icon.h"
 #include "ash/system/network/network_icon_animation.h"
@@ -242,7 +242,7 @@
 
 views::View* TrayNetwork::CreateDetailedView(LoginStatus status) {
   CHECK(detailed_ == nullptr);
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_DETAILED_NETWORK_VIEW);
   if (!chromeos::NetworkHandler::IsInitialized())
     return nullptr;
@@ -267,7 +267,7 @@
   // This will always be triggered by a user action (e.g. keyboard shortcut)
   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
   bool enabled = handler->IsTechnologyEnabled(NetworkTypePattern::WiFi());
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       enabled ? UMA_STATUS_AREA_DISABLE_WIFI : UMA_STATUS_AREA_ENABLE_WIFI);
   handler->SetTechnologyEnabled(NetworkTypePattern::WiFi(), !enabled,
                                 chromeos::network_handler::ErrorCallback());
diff --git a/ash/system/network/tray_vpn.cc b/ash/system/network/tray_vpn.cc
index 8eadaf2a..6d9d5b4 100644
--- a/ash/system/network/tray_vpn.cc
+++ b/ash/system/network/tray_vpn.cc
@@ -4,10 +4,10 @@
 
 #include "ash/system/network/tray_vpn.h"
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/network/network_icon.h"
 #include "ash/system/network/network_icon_animation.h"
@@ -172,7 +172,8 @@
   if (!chromeos::NetworkHandler::IsInitialized())
     return nullptr;
 
-  ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_DETAILED_VPN_VIEW);
+  Shell::Get()->metrics()->RecordUserMetricsAction(
+      UMA_STATUS_AREA_DETAILED_VPN_VIEW);
   detailed_ = new tray::VPNListView(this, status);
   detailed_->Init();
   return detailed_;
diff --git a/ash/system/network/vpn_list_view.cc b/ash/system/network/vpn_list_view.cc
index 1a1625e..db283e2 100644
--- a/ash/system/network/vpn_list_view.cc
+++ b/ash/system/network/vpn_list_view.cc
@@ -7,9 +7,9 @@
 #include <memory>
 #include <vector>
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/network/network_icon.h"
 #include "ash/system/network/network_icon_animation.h"
@@ -95,12 +95,12 @@
     // If the user clicks on a provider entry, request that the "add network"
     // dialog for this provider be shown.
     if (vpn_provider_.third_party) {
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           UMA_STATUS_AREA_VPN_ADD_THIRD_PARTY_CLICKED);
       Shell::Get()->system_tray_controller()->ShowThirdPartyVpnCreate(
           vpn_provider_.extension_id);
     } else {
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           UMA_STATUS_AREA_VPN_ADD_BUILT_IN_CLICKED);
       Shell::Get()->system_tray_controller()->ShowNetworkCreate(
           shill::kTypeVPN);
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index fe543b53..cf3d55b 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -4,11 +4,11 @@
 
 #include "ash/system/overview/overview_button_tray.h"
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_container.h"
@@ -81,7 +81,7 @@
   // screen is locked, a modal dialog is open or is running in kiosk app
   // session.
   bool performed = controller->ToggleOverview();
-  ShellPort::Get()->RecordUserMetricsAction(UMA_TRAY_OVERVIEW);
+  Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_OVERVIEW);
   return performed;
 }
 
diff --git a/ash/system/screen_layout_observer.cc b/ash/system/screen_layout_observer.cc
index f4ddd45..5420a5a 100644
--- a/ash/system/screen_layout_observer.cc
+++ b/ash/system/screen_layout_observer.cc
@@ -10,10 +10,10 @@
 
 #include "ash/display/screen_orientation_controller_chromeos.h"
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/grit/ash_resources.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/devicetype_utils.h"
 #include "ash/system/system_notifier.h"
@@ -66,12 +66,12 @@
 
 // Callback to handle a user selecting the notification view.
 void OpenSettingsFromNotification() {
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_DISPLAY_NOTIFICATION_SELECTED);
   // Settings may be blocked, e.g. at the lock screen.
   if (Shell::Get()->session_controller()->ShouldEnableSettings()) {
     Shell::Get()->system_tray_controller()->ShowDisplaySettings();
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_STATUS_AREA_DISPLAY_NOTIFICATION_SHOW_SETTINGS);
   }
 }
@@ -345,7 +345,7 @@
       new message_center::HandleNotificationClickedDelegate(
           base::Bind(&OpenSettingsFromNotification))));
 
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_DISPLAY_NOTIFICATION_CREATED);
   message_center::MessageCenter::Get()->AddNotification(
       std::move(notification));
diff --git a/ash/system/screen_security/screen_capture_tray_item.cc b/ash/system/screen_security/screen_capture_tray_item.cc
index bdc3599..0ab2707 100644
--- a/ash/system/screen_security/screen_capture_tray_item.cc
+++ b/ash/system/screen_security/screen_capture_tray_item.cc
@@ -7,9 +7,9 @@
 #include <utility>
 
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/grit/ash_resources.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/system_notifier.h"
 #include "ash/system/tray/system_tray_notifier.h"
@@ -68,12 +68,12 @@
 }
 
 void ScreenCaptureTrayItem::RecordStoppedFromDefaultViewMetric() {
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_SCREEN_CAPTURE_DEFAULT_STOP);
 }
 
 void ScreenCaptureTrayItem::RecordStoppedFromNotificationViewMetric() {
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_SCREEN_CAPTURE_NOTIFICATION_STOP);
 }
 
diff --git a/ash/system/tiles/tiles_default_view.cc b/ash/system/tiles/tiles_default_view.cc
index 4cfe1d2..d5cfdc40 100644
--- a/ash/system/tiles/tiles_default_view.cc
+++ b/ash/system/tiles/tiles_default_view.cc
@@ -5,10 +5,10 @@
 #include "ash/system/tiles/tiles_default_view.h"
 
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/shutdown_controller.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/night_light/night_light_controller.h"
@@ -117,23 +117,23 @@
                                      const ui::Event& event) {
   DCHECK(sender);
   if (sender == settings_button_) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_TRAY_SETTINGS);
+    Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_SETTINGS);
     Shell::Get()->system_tray_controller()->ShowSettings();
   } else if (sender == help_button_) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_TRAY_HELP);
+    Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_HELP);
     Shell::Get()->system_tray_controller()->ShowHelp();
   } else if (NightLightController::IsFeatureEnabled() &&
              sender == night_light_button_) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_TRAY_NIGHT_LIGHT);
+    Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_NIGHT_LIGHT);
     Shell::Get()->night_light_controller()->Toggle();
     night_light_button_->Update();
   } else if (sender == lock_button_) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_TRAY_LOCK_SCREEN);
+    Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_LOCK_SCREEN);
     chromeos::DBusThreadManager::Get()
         ->GetSessionManagerClient()
         ->RequestLockScreen();
   } else if (sender == power_button_) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_TRAY_SHUT_DOWN);
+    Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TRAY_SHUT_DOWN);
     Shell::Get()->lock_state_controller()->RequestShutdown();
   }
 }
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 78f29291..35798e8 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -10,6 +10,7 @@
 
 #include "ash/key_event_watcher.h"
 #include "ash/login_status.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
@@ -307,7 +308,8 @@
 
 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) {
   if (creation_type != BUBBLE_USE_EXISTING)
-    ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_MENU_OPENED);
+    Shell::Get()->metrics()->RecordUserMetricsAction(
+        UMA_STATUS_AREA_MENU_OPENED);
   ShowItems(GetTrayItems(), false, true, creation_type, false);
 }
 
diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc
index 6b17e76..331c990 100644
--- a/ash/system/tray_accessibility.cc
+++ b/ash/system/tray_accessibility.cc
@@ -9,10 +9,10 @@
 
 #include "ash/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/system_notifier.h"
 #include "ash/system/tray/hover_highlight_view.h"
@@ -298,7 +298,7 @@
   } else {
     return;
   }
-  ShellPort::Get()->RecordUserMetricsAction(user_action);
+  Shell::Get()->metrics()->RecordUserMetricsAction(user_action);
 }
 
 void AccessibilityDetailedView::HandleButtonPressed(views::Button* sender,
@@ -399,7 +399,7 @@
 views::View* TrayAccessibility::CreateDetailedView(LoginStatus status) {
   CHECK(detailed_menu_ == nullptr);
 
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_DETAILED_ACCESSIBILITY);
   detailed_menu_ = CreateDetailedMenu();
   return detailed_menu_;
diff --git a/ash/system/tray_caps_lock.cc b/ash/system/tray_caps_lock.cc
index d3cdc26..eb635096 100644
--- a/ash/system/tray_caps_lock.cc
+++ b/ash/system/tray_caps_lock.cc
@@ -5,9 +5,9 @@
 #include "ash/system/tray_caps_lock.h"
 
 #include "ash/accessibility_delegate.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/system_notifier.h"
 #include "ash/system/tray/actionable_view.h"
@@ -146,7 +146,7 @@
     chromeos::input_method::ImeKeyboard* keyboard =
         chromeos::input_method::InputMethodManager::Get()->GetImeKeyboard();
     if (keyboard) {
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           keyboard->CapsLockIsEnabled()
               ? UMA_STATUS_AREA_CAPS_LOCK_DISABLED_BY_CLICK
               : UMA_STATUS_AREA_CAPS_LOCK_ENABLED_BY_CLICK);
@@ -199,7 +199,7 @@
         message_center::MessageCenter::Get();
     if (caps_lock_enabled_) {
       if (!message_shown_) {
-        ShellPort::Get()->RecordUserMetricsAction(
+        Shell::Get()->metrics()->RecordUserMetricsAction(
             UMA_STATUS_AREA_CAPS_LOCK_POPUP);
 
         message_center->AddNotification(CreateNotification());
diff --git a/ash/system/tray_tracing.cc b/ash/system/tray_tracing.cc
index 8eeaf78..5a353e21 100644
--- a/ash/system/tray_tracing.cc
+++ b/ash/system/tray_tracing.cc
@@ -5,9 +5,9 @@
 #include "ash/system/tray_tracing.h"
 
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/actionable_view.h"
 #include "ash/system/tray/system_tray.h"
@@ -55,7 +55,7 @@
 
  private:
   bool PerformAction(const ui::Event& event) override {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_STATUS_AREA_TRACING_DEFAULT_SELECTED);
     Shell::Get()->system_tray_controller()->ShowChromeSlow();
     CloseSystemBubble();
diff --git a/ash/system/update/tray_update.cc b/ash/system/update/tray_update.cc
index 1f2ca3d..c9ba3a9 100644
--- a/ash/system/update/tray_update.cc
+++ b/ash/system/update/tray_update.cc
@@ -5,11 +5,11 @@
 #include "ash/system/update/tray_update.h"
 
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/interfaces/update.mojom.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_controller.h"
@@ -124,7 +124,7 @@
     DCHECK(update_required_ || update_over_cellular_available_);
     if (update_required_) {
       Shell::Get()->system_tray_controller()->RequestRestartForUpdate();
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           UMA_STATUS_AREA_OS_UPDATE_DEFAULT_SELECTED);
     } else {
       // Shows the about chrome OS page and checks for update after the page is
diff --git a/ash/system/user/user_view.cc b/ash/system/user/user_view.cc
index b4cac927..a4ab4ba1 100644
--- a/ash/system/user/user_view.cc
+++ b/ash/system/user/user_view.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/multi_profile_uma.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
@@ -247,7 +248,7 @@
 
 void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
   if (sender == logout_button_) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_SIGN_OUT);
+    Shell::Get()->metrics()->RecordUserMetricsAction(UMA_STATUS_AREA_SIGN_OUT);
     HideUserDropdownWidget();
     Shell::Get()->system_tray_controller()->SignOut();
   } else if (sender == user_card_container_ &&
diff --git a/ash/touch/touch_uma.cc b/ash/touch/touch_uma.cc
index c3187fe64..0939094 100644
--- a/ash/touch/touch_uma.cc
+++ b/ash/touch/touch_uma.cc
@@ -4,7 +4,8 @@
 
 #include "ash/touch/touch_uma.h"
 
-#include "ash/shell_port.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
 #include "ui/aura/env.h"
@@ -113,7 +114,7 @@
       kBucketCountForLocation, kBucketCountForLocation + 1);
 
   if (event.type() == ui::ET_TOUCH_PRESSED) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_TOUCHSCREEN_TAP_DOWN);
+    Shell::Get()->metrics()->RecordUserMetricsAction(UMA_TOUCHSCREEN_TAP_DOWN);
 
     if (details->last_release_time_.ToInternalValue()) {
       // Measuring the interval between a touch-release and the next
diff --git a/ash/wm/gestures/overview_gesture_handler.cc b/ash/wm/gestures/overview_gesture_handler.cc
index d00475d8..938bb97c 100644
--- a/ash/wm/gestures/overview_gesture_handler.cc
+++ b/ash/wm/gestures/overview_gesture_handler.cc
@@ -4,8 +4,8 @@
 
 #include "ash/wm/gestures/overview_gesture_handler.h"
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
@@ -66,7 +66,8 @@
 
   // Reset scroll amount on toggling.
   scroll_x_ = scroll_y_ = 0;
-  ShellPort::Get()->RecordUserMetricsAction(UMA_TOUCHPAD_GESTURE_OVERVIEW);
+  Shell::Get()->metrics()->RecordUserMetricsAction(
+      UMA_TOUCHPAD_GESTURE_OVERVIEW);
   if (window_selector_controller->IsSelecting() &&
       window_selector_controller->AcceptSelection()) {
     return true;
diff --git a/ash/wm/lock_state_controller.cc b/ash/wm/lock_state_controller.cc
index afc90816..215776d 100644
--- a/ash/wm/lock_state_controller.cc
+++ b/ash/wm/lock_state_controller.cc
@@ -10,6 +10,7 @@
 
 #include "ash/accessibility_delegate.h"
 #include "ash/cancel_mode.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/interfaces/shutdown.mojom.h"
 #include "ash/shell.h"
@@ -300,7 +301,8 @@
 void LockStateController::OnRealPowerTimeout() {
   VLOG(1) << "OnRealPowerTimeout";
   DCHECK(shutting_down_);
-  ShellPort::Get()->RecordUserMetricsAction(UMA_ACCEL_SHUT_DOWN_POWER_BUTTON);
+  Shell::Get()->metrics()->RecordUserMetricsAction(
+      UMA_ACCEL_SHUT_DOWN_POWER_BUTTON);
   // Shut down or reboot based on device policy.
   shutdown_controller_->ShutDownOrReboot();
 }
@@ -465,7 +467,7 @@
   }
 
   if (request_lock) {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         shutdown_after_lock_ ? UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON
                              : UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON);
     chromeos::DBusThreadManager::Get()
diff --git a/ash/wm/maximize_mode/maximize_mode_controller.cc b/ash/wm/maximize_mode/maximize_mode_controller.cc
index e373eec..b8be143 100644
--- a/ash/wm/maximize_mode/maximize_mode_controller.cc
+++ b/ash/wm/maximize_mode/maximize_mode_controller.cc
@@ -7,9 +7,9 @@
 #include <utility>
 
 #include "ash/ash_switches.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
-#include "ash/shell_port.h"
 #include "ash/wm/maximize_mode/maximize_mode_window_manager.h"
 #include "ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard.h"
 #include "base/bind.h"
@@ -131,7 +131,7 @@
       scoped_session_observer_(this),
       weak_factory_(this) {
   Shell::Get()->AddShellObserver(this);
-  ShellPort::Get()->RecordUserMetricsAction(
+  Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_MAXIMIZE_MODE_INITIALLY_DISABLED);
 
   // TODO(jonross): Do not create MaximizeModeController if the flag is
@@ -179,7 +179,7 @@
     maximize_mode_window_manager_.reset(new MaximizeModeWindowManager());
     // TODO(jonross): Move the maximize mode notifications from ShellObserver
     // to MaximizeModeController::Observer
-    ShellPort::Get()->RecordUserMetricsAction(UMA_MAXIMIZE_MODE_ENABLED);
+    Shell::Get()->metrics()->RecordUserMetricsAction(UMA_MAXIMIZE_MODE_ENABLED);
     Shell::Get()->NotifyMaximizeModeStarted();
 
     observers_.ForAllPtrs([](mojom::TouchViewObserver* observer) {
@@ -190,7 +190,8 @@
     maximize_mode_window_manager_->SetIgnoreWmEventsForExit();
     Shell::Get()->NotifyMaximizeModeEnding();
     maximize_mode_window_manager_.reset();
-    ShellPort::Get()->RecordUserMetricsAction(UMA_MAXIMIZE_MODE_DISABLED);
+    Shell::Get()->metrics()->RecordUserMetricsAction(
+        UMA_MAXIMIZE_MODE_DISABLED);
     Shell::Get()->NotifyMaximizeModeEnded();
 
     observers_.ForAllPtrs([](mojom::TouchViewObserver* observer) {
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 76bdee5..0f078e0 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -13,12 +13,12 @@
 #include "ash/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/window_grid.h"
 #include "ash/wm/overview/window_selector_delegate.h"
@@ -310,7 +310,7 @@
   Shell::Get()->activation_client()->AddObserver(this);
 
   display::Screen::GetScreen()->AddObserver(this);
-  ShellPort::Get()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW);
+  Shell::Get()->metrics()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW);
   // Send an a11y alert.
   Shell::Get()->accessibility_delegate()->TriggerAccessibilityAlert(
       A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED);
@@ -428,7 +428,7 @@
     // a window other than the window that was active prior to entering overview
     // mode (i.e., the window at the front of the MRU list).
     if (window_list[0] != window) {
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           UMA_WINDOW_OVERVIEW_ACTIVE_WINDOW_CHANGED);
     }
     const auto it = std::find(window_list.begin(), window_list.end(), window);
@@ -481,7 +481,8 @@
         // Allow the textfield to handle 'W' key when not used with Ctrl.
         return false;
       }
-      ShellPort::Get()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW_CLOSE_KEY);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_WINDOW_OVERVIEW_CLOSE_KEY);
       grid_list_[selected_grid_index_]->SelectedWindow()->CloseWindow();
       break;
     case ui::VKEY_RETURN:
@@ -493,7 +494,8 @@
       UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.WindowSelector.KeyPressesOverItemsRatio",
                                   (num_key_presses_ * 100) / num_items_, 1, 300,
                                   30);
-      ShellPort::Get()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW_ENTER_KEY);
+      Shell::Get()->metrics()->RecordUserMetricsAction(
+          UMA_WINDOW_OVERVIEW_ENTER_KEY);
       SelectWindow(grid_list_[selected_grid_index_]->SelectedWindow());
       break;
     default:
diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc
index 723e54b..78406fc 100644
--- a/ash/wm/overview/window_selector_item.cc
+++ b/ash/wm/overview/window_selector_item.cc
@@ -8,11 +8,11 @@
 #include <vector>
 
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/wm/overview/cleanup_animation_observer.h"
 #include "ash/wm/overview/overview_animation_type.h"
@@ -531,11 +531,12 @@
 void WindowSelectorItem::ButtonPressed(views::Button* sender,
                                        const ui::Event& event) {
   if (sender == close_button_) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW_CLOSE_BUTTON);
+    Shell::Get()->metrics()->RecordUserMetricsAction(
+        UMA_WINDOW_OVERVIEW_CLOSE_BUTTON);
     if (ash::Shell::Get()
             ->maximize_mode_controller()
             ->IsMaximizeModeWindowManagerEnabled()) {
-      ash::ShellPort::Get()->RecordUserMetricsAction(
+      ash::Shell::Get()->metrics()->RecordUserMetricsAction(
           ash::UMA_TABLET_WINDOW_CLOSE_THROUGH_OVERVIEW_CLOSE_BUTTON);
     }
     CloseWindow();
diff --git a/ash/wm/panels/panel_window_event_handler.cc b/ash/wm/panels/panel_window_event_handler.cc
index 4de0b0c..bb129a89a 100644
--- a/ash/wm/panels/panel_window_event_handler.cc
+++ b/ash/wm/panels/panel_window_event_handler.cc
@@ -4,7 +4,8 @@
 
 #include "ash/wm/panels/panel_window_event_handler.h"
 
-#include "ash/shell_port.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/window.h"
@@ -24,7 +25,8 @@
       event->flags() & ui::EF_IS_DOUBLE_CLICK &&
       event->IsOnlyLeftMouseButton() &&
       wm::GetNonClientComponent(target, event->location()) == HTCAPTION) {
-    ShellPort::Get()->RecordUserMetricsAction(UMA_PANEL_MINIMIZE_CAPTION_CLICK);
+    Shell::Get()->metrics()->RecordUserMetricsAction(
+        UMA_PANEL_MINIMIZE_CAPTION_CLICK);
     wm::GetWindowState(target)->Minimize();
     event->StopPropagation();
     return;
@@ -36,7 +38,7 @@
   if (!event->handled() && event->type() == ui::ET_GESTURE_TAP &&
       event->details().tap_count() == 2 &&
       wm::GetNonClientComponent(target, event->location()) == HTCAPTION) {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_PANEL_MINIMIZE_CAPTION_GESTURE);
     wm::GetWindowState(target)->Minimize();
     event->StopPropagation();
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc
index 4ef3d16..567d5e62 100644
--- a/ash/wm/window_cycle_controller.cc
+++ b/ash/wm/window_cycle_controller.cc
@@ -4,7 +4,9 @@
 
 #include "ash/wm/window_cycle_controller.h"
 
+#include "ash/metrics/task_switch_metrics_recorder.h"
 #include "ash/metrics/task_switch_source.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
@@ -81,7 +83,7 @@
   window_cycle_list_.reset(new WindowCycleList(window_list));
   event_filter_ = ShellPort::Get()->CreateWindowCycleEventFilter();
   cycle_start_time_ = base::Time::Now();
-  ShellPort::Get()->RecordUserMetricsAction(UMA_WINDOW_CYCLE);
+  Shell::Get()->metrics()->RecordUserMetricsAction(UMA_WINDOW_CYCLE);
   UMA_HISTOGRAM_COUNTS_100("Ash.WindowCycleController.Items",
                            window_list.size());
 }
@@ -118,7 +120,7 @@
 
   if (active_window_after_window_cycle != nullptr &&
       active_window_before_window_cycle_ != active_window_after_window_cycle) {
-    ShellPort::Get()->RecordTaskSwitchMetric(
+    Shell::Get()->metrics()->task_switch_metrics_recorder().OnTaskSwitch(
         TaskSwitchSource::WINDOW_CYCLE_CONTROLLER);
   }
   active_window_before_window_cycle_ = nullptr;
diff --git a/ash/wm/workspace/workspace_event_handler.cc b/ash/wm/workspace/workspace_event_handler.cc
index 5db36d9..ebbbc037 100644
--- a/ash/wm/workspace/workspace_event_handler.cc
+++ b/ash/wm/workspace/workspace_event_handler.cc
@@ -4,7 +4,9 @@
 
 #include "ash/wm/workspace/workspace_event_handler.h"
 
-#include "ash/shell_port.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
+#include "ash/touch/touch_uma.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_event.h"
@@ -49,7 +51,7 @@
         if (event->flags() & ui::EF_IS_DOUBLE_CLICK) {
           int component = wm::GetNonClientComponent(target, event->location());
           if (component == HTCAPTION && component == click_component_) {
-            ShellPort::Get()->RecordUserMetricsAction(
+            Shell::Get()->metrics()->RecordUserMetricsAction(
                 UMA_TOGGLE_MAXIMIZE_CAPTION_CLICK);
             const wm::WMEvent wm_event(wm::WM_EVENT_TOGGLE_MAXIMIZE_CAPTION);
             target_state->OnWMEvent(&wm_event);
@@ -81,14 +83,14 @@
     return;
 
   if (event->details().tap_count() != 2) {
-    ShellPort::Get()->RecordGestureAction(GESTURE_FRAMEVIEW_TAP);
+    TouchUMA::GetInstance()->RecordGestureAction(GESTURE_FRAMEVIEW_TAP);
     return;
   }
 
   if (click_component_ == previous_target_component) {
-    ShellPort::Get()->RecordUserMetricsAction(
+    Shell::Get()->metrics()->RecordUserMetricsAction(
         UMA_TOGGLE_MAXIMIZE_CAPTION_GESTURE);
-    ShellPort::Get()->RecordGestureAction(GESTURE_MAXIMIZE_DOUBLETAP);
+    TouchUMA::GetInstance()->RecordGestureAction(GESTURE_MAXIMIZE_DOUBLETAP);
     const wm::WMEvent wm_event(wm::WM_EVENT_TOGGLE_MAXIMIZE_CAPTION);
     wm::GetWindowState(target)->OnWMEvent(&wm_event);
     event->StopPropagation();
@@ -103,13 +105,13 @@
   if ((event->flags() & ui::EF_IS_DOUBLE_CLICK) != 0 && target->delegate()) {
     const int component = wm::GetNonClientComponent(target, event->location());
     if (component == HTBOTTOM || component == HTTOP) {
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           UMA_TOGGLE_SINGLE_AXIS_MAXIMIZE_BORDER_CLICK);
       const wm::WMEvent wm_event(wm::WM_EVENT_TOGGLE_VERTICAL_MAXIMIZE);
       target_state->OnWMEvent(&wm_event);
       event->StopPropagation();
     } else if (component == HTLEFT || component == HTRIGHT) {
-      ShellPort::Get()->RecordUserMetricsAction(
+      Shell::Get()->metrics()->RecordUserMetricsAction(
           UMA_TOGGLE_SINGLE_AXIS_MAXIMIZE_BORDER_CLICK);
       const wm::WMEvent wm_event(wm::WM_EVENT_TOGGLE_HORIZONTAL_MAXIMIZE);
       target_state->OnWMEvent(&wm_event);
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 477d15ae..47f52d01 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "ash/metrics/user_metrics_action.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
@@ -408,9 +409,9 @@
     const wm::WMEvent event(snap_type_ == SNAP_LEFT ? wm::WM_EVENT_SNAP_LEFT
                                                     : wm::WM_EVENT_SNAP_RIGHT);
     window_state()->OnWMEvent(&event);
-    ShellPort::Get()->RecordUserMetricsAction(snap_type_ == SNAP_LEFT
-                                                  ? UMA_DRAG_MAXIMIZE_LEFT
-                                                  : UMA_DRAG_MAXIMIZE_RIGHT);
+    Shell::Get()->metrics()->RecordUserMetricsAction(
+        snap_type_ == SNAP_LEFT ? UMA_DRAG_MAXIMIZE_LEFT
+                                : UMA_DRAG_MAXIMIZE_RIGHT);
     snapped = true;
   }
 
diff --git a/build/android/gradle/root.jinja b/build/android/gradle/root.jinja
index 0d92ba5..ba418b4 100644
--- a/build/android/gradle/root.jinja
+++ b/build/android/gradle/root.jinja
@@ -8,6 +8,6 @@
         jcenter()
     }
     dependencies {
-        classpath "com.android.tools.build:gradle:2.3.2"
+        classpath "com.android.tools.build:gradle:2.3.3"
     }
 }
diff --git a/build_overrides/pdfium.gni b/build_overrides/pdfium.gni
index 377cdbeb..fb37646 100644
--- a/build_overrides/pdfium.gni
+++ b/build_overrides/pdfium.gni
@@ -15,8 +15,8 @@
 # Disable use of Skia backend.
 pdf_use_skia_override = false
 
-# Enable use of Skia backend, paths only.
-pdf_use_skia_paths_override = true
+# Disable use of Skia backend, paths only (experimental)
+pdf_use_skia_paths_override = false
 
 # Enable experimental win32 GDI APIs.
 pdf_use_win32_gdi_override = true
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
index 2dc9ce5..70892c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
@@ -29,6 +29,10 @@
  * Stores info for WebAPK.
  */
 public class WebApkInfo extends WebappInfo {
+    public static final String RESOURCE_NAME = "name";
+    public static final String RESOURCE_SHORT_NAME = "short_name";
+    public static final String RESOURCE_STRING_TYPE = "string";
+
     private static final String TAG = "WebApkInfo";
 
     private boolean mForceNavigation;
@@ -89,8 +93,24 @@
             return null;
         }
 
-        String name = IntentUtils.safeGetString(bundle, WebApkMetaDataKeys.NAME);
-        String shortName = IntentUtils.safeGetString(bundle, WebApkMetaDataKeys.SHORT_NAME);
+        Resources res = null;
+        try {
+            res = ContextUtils.getApplicationContext()
+                          .getPackageManager()
+                          .getResourcesForApplication(webApkPackageName);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+
+        int nameId = res.getIdentifier(RESOURCE_NAME, RESOURCE_STRING_TYPE, webApkPackageName);
+        int shortNameId =
+                res.getIdentifier(RESOURCE_SHORT_NAME, RESOURCE_STRING_TYPE, webApkPackageName);
+        String name = nameId != 0 ? res.getString(nameId)
+                                  : IntentUtils.safeGetString(bundle, WebApkMetaDataKeys.NAME);
+        String shortName = shortNameId != 0
+                ? res.getString(shortNameId)
+                : IntentUtils.safeGetString(bundle, WebApkMetaDataKeys.SHORT_NAME);
+
         String scope = IntentUtils.safeGetString(bundle, WebApkMetaDataKeys.SCOPE);
 
         int displayMode = displayModeFromString(
@@ -110,10 +130,10 @@
         Map<String, String> iconUrlToMurmur2HashMap = getIconUrlAndIconMurmur2HashMap(bundle);
 
         int primaryIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.ICON_ID, 0);
-        Bitmap primaryIcon = decodeImageResource(webApkPackageName, primaryIconId);
+        Bitmap primaryIcon = decodeImageResource(res, primaryIconId);
 
         int badgeIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.BADGE_ICON_ID, 0);
-        Bitmap badgeIcon = decodeImageResource(webApkPackageName, badgeIconId);
+        Bitmap badgeIcon = decodeImageResource(res, badgeIconId);
 
         return create(WebApkConstants.WEBAPK_ID_PREFIX + webApkPackageName, url, forceNavigation,
                 scope, new Icon(primaryIcon), new Icon(badgeIcon), name, shortName, displayMode,
@@ -250,16 +270,10 @@
     }
 
     /**
-     * Decodes bitmap from WebAPK's resources. Returns null when resource is not found.
+     * Decodes bitmap from WebAPK's resources.
      */
-    private static Bitmap decodeImageResource(String webApkPackageName, int resourceId) {
-        PackageManager packageManager = ContextUtils.getApplicationContext().getPackageManager();
-        try {
-            Resources resources = packageManager.getResourcesForApplication(webApkPackageName);
-            return BitmapFactory.decodeResource(resources, resourceId);
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
+    private static Bitmap decodeImageResource(Resources webApkResources, int resourceId) {
+        return BitmapFactory.decodeResource(webApkResources, resourceId);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
index 0758d2a..f55a895 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappRegistry.java
@@ -15,6 +15,7 @@
 import org.chromium.chrome.browser.banners.InstallerDelegate;
 import org.chromium.chrome.browser.browsing_data.UrlFilter;
 import org.chromium.chrome.browser.browsing_data.UrlFilterBridge;
+import org.chromium.webapk.lib.common.WebApkConstants;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -193,7 +194,11 @@
             WebappDataStorage storage = entry.getValue();
             String webApkPackage = storage.getWebApkPackageName();
             if (webApkPackage != null) {
-                if (isWebApkInstalled(webApkPackage)) {
+                // Prefix check that the key matches the current scheme instead of an old
+                // deprecated naming scheme and that the WebApk is still installed. The former is
+                // necessary as we migrate away from the old naming scheme and garbage collect.
+                if (entry.getKey().startsWith(WebApkConstants.WEBAPK_ID_PREFIX)
+                        && isWebApkInstalled(webApkPackage)) {
                     continue;
                 }
             } else if ((currentTime - storage.getLastUsedTime())
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java
index 798d5f6..c69d727 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappDirectoryManagerTest.java
@@ -25,6 +25,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.webapk.lib.common.WebApkConstants;
 
 import java.io.File;
 import java.util.HashSet;
@@ -41,6 +42,9 @@
     private static final String WEBAPP_ID_1 = "webapp_1";
     private static final String WEBAPP_ID_2 = "webapp_2";
     private static final String WEBAPP_ID_3 = "webapp_3";
+    private static final String WEBAPK_ID_1 = WebApkConstants.WEBAPK_ID_PREFIX + "webapp_1";
+    private static final String WEBAPK_ID_2 = WebApkConstants.WEBAPK_ID_PREFIX + "webapp_2";
+    private static final String WEBAPK_ID_3 = WebApkConstants.WEBAPK_ID_PREFIX + "webapp_3";
 
     private static class TestWebappDirectoryManager extends WebappDirectoryManager {
         private Set<Intent> mBaseIntents = new HashSet<Intent>();
@@ -148,6 +152,41 @@
         Assert.assertFalse(directory3.exists());
     }
 
+    /**
+     * On Lollipop and higher, the {@link WebappDirectoryManager} also deletes directories for
+     * *WebApks* that no longer correspond to tasks in Recents.
+     */
+    @Test
+    @SmallTest
+    @Feature({"Webapps"})
+    public void testDeletesDirectoriesForDeadWebApkTasks() throws Exception {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
+
+        // Track the three web app directories.
+        File directory1 =
+                new File(mWebappDirectoryManager.getBaseWebappDirectory(mMockContext), WEBAPK_ID_1);
+        File directory2 =
+                new File(mWebappDirectoryManager.getBaseWebappDirectory(mMockContext), WEBAPK_ID_2);
+        File directory3 =
+                new File(mWebappDirectoryManager.getBaseWebappDirectory(mMockContext), WEBAPK_ID_3);
+
+        // Seed the directory with folders for web apps.
+        Assert.assertTrue(directory1.mkdirs());
+        Assert.assertTrue(directory2.mkdirs());
+        Assert.assertTrue(directory3.mkdirs());
+
+        // Indicate that another of the web apps is listed in Recents; in real usage this web app
+        // would not be in the foreground and would have persisted its state.
+        mWebappDirectoryManager.mBaseIntents.add(
+                new Intent(Intent.ACTION_VIEW, Uri.parse("webapp://webapk-webapp_2")));
+
+        // Only the directory for the background web app should survive.
+        runCleanup();
+        Assert.assertFalse(directory1.exists());
+        Assert.assertTrue(directory2.exists());
+        Assert.assertFalse(directory3.exists());
+    }
+
     @Test
     @SmallTest
     @Feature({"Webapps"})
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
index ecaf1ae..d7c6f1ca 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkInfoTest.java
@@ -5,6 +5,8 @@
 package org.chromium.chrome.browser.webapps;
 
 import android.content.Intent;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
 import android.os.Bundle;
 
 import org.junit.Assert;
@@ -24,6 +26,7 @@
 import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
 import org.chromium.webapk.test.WebApkTestHelper;
 
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -49,6 +52,48 @@
     private static final String ICON_MURMUR2_HASH = "5";
     private static final int SOURCE = ShortcutSource.NOTIFICATION;
 
+    /** Fakes the Resources object, allowing lookup of String value. */
+    private static class FakeResources extends Resources {
+        private final Map<String, Integer> mStringIdMap;
+        private final Map<Integer, String> mIdValueMap;
+
+        // Do not warn about deprecated call to Resources(); the documentation says code is not
+        // supposed to create its own Resources object, but we are using it to fake out the
+        // Resources, and there is no other way to do that.
+        @SuppressWarnings("deprecation")
+        public FakeResources() {
+            super(new AssetManager(), null, null);
+            mStringIdMap = new HashMap<>();
+            mIdValueMap = new HashMap<>();
+        }
+
+        @Override
+        public int getIdentifier(String name, String defType, String defPackage) {
+            String key = getKey(name, defType, defPackage);
+            return mStringIdMap.containsKey(key) ? mStringIdMap.get(key) : 0;
+        }
+
+        @Override
+        public String getString(int id) {
+            if (!mIdValueMap.containsKey(id)) {
+                throw new Resources.NotFoundException("id 0x" + Integer.toHexString(id));
+            }
+
+            return mIdValueMap.get(id);
+        }
+
+        public void addStringForTesting(
+                String name, String defType, String defPackage, int identifier, String value) {
+            String key = getKey(name, defType, defPackage);
+            mStringIdMap.put(key, identifier);
+            mIdValueMap.put(identifier, value);
+        }
+
+        private String getKey(String name, String defType, String defPackage) {
+            return defPackage + ":" + defType + "/" + name;
+        }
+    }
+
     @Before
     public void setUp() {
         ContextUtils.initApplicationContextForTests(RuntimeEnvironment.application);
@@ -248,4 +293,55 @@
         WebApkInfo info = WebApkInfo.create(intent);
         Assert.assertEquals(ShortcutSource.UNKNOWN, info.source());
     }
+
+    /**
+     * Test that {@link WebApkInfo#name()} and {@link WebApkInfo#shortName()} return the name and
+     * short name from the meta data before they are moved to strings in resources.
+     */
+    @Test
+    public void testNameAndShortNameFromMetadataWhenStringResourcesDoNotExist() {
+        String name = "WebAPK name";
+        String shortName = "WebAPK short name";
+        Bundle bundle = new Bundle();
+        bundle.putString(WebApkMetaDataKeys.START_URL, START_URL);
+        bundle.putString(WebApkMetaDataKeys.NAME, name);
+        bundle.putString(WebApkMetaDataKeys.SHORT_NAME, shortName);
+        WebApkTestHelper.registerWebApkWithMetaData(WEBAPK_PACKAGE_NAME, bundle);
+
+        Intent intent = new Intent();
+        intent.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, WEBAPK_PACKAGE_NAME);
+        intent.putExtra(ShortcutHelper.EXTRA_URL, START_URL);
+
+        WebApkInfo info = WebApkInfo.create(intent);
+        Assert.assertEquals(name, info.name());
+        Assert.assertEquals(shortName, info.shortName());
+    }
+
+    /**
+     * Test that {@link WebApkInfo#name()} and {@link WebApkInfo#shortName()} return the string
+     * values from the WebAPK resources if exist.
+     */
+    @Test
+    public void testNameAndShortNameFromWebApkStrings() {
+        Bundle bundle = new Bundle();
+        bundle.putString(WebApkMetaDataKeys.START_URL, START_URL);
+        WebApkTestHelper.registerWebApkWithMetaData(WEBAPK_PACKAGE_NAME, bundle);
+
+        String name = "WebAPK name";
+        String shortName = "WebAPK short name";
+        FakeResources res = new FakeResources();
+        res.addStringForTesting(WebApkInfo.RESOURCE_NAME, WebApkInfo.RESOURCE_STRING_TYPE,
+                WEBAPK_PACKAGE_NAME, 1, name);
+        res.addStringForTesting(WebApkInfo.RESOURCE_SHORT_NAME, WebApkInfo.RESOURCE_STRING_TYPE,
+                WEBAPK_PACKAGE_NAME, 2, shortName);
+        WebApkTestHelper.setResource(WEBAPK_PACKAGE_NAME, res);
+
+        Intent intent = new Intent();
+        intent.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, WEBAPK_PACKAGE_NAME);
+        intent.putExtra(ShortcutHelper.EXTRA_URL, START_URL);
+
+        WebApkInfo info = WebApkInfo.create(intent);
+        Assert.assertEquals(name, info.name());
+        Assert.assertEquals(shortName, info.shortName());
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
index f52d40af..8e23358 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java
@@ -321,9 +321,9 @@
     @Test
     @Feature({"WebApk"})
     public void testCleanupRemovesUninstalledWebApks() throws Exception {
-        String webappId1 = "webapk:uninstalledWebApk1";
+        String webappId1 = WebApkConstants.WEBAPK_ID_PREFIX + "uninstalledWebApk1";
         String webApkPackage1 = "uninstalledWebApk1";
-        String webappId2 = "webapk:uninstalledWebApk2";
+        String webappId2 = WebApkConstants.WEBAPK_ID_PREFIX + "uninstalledWebApk2";
         String webApkPackage2 = "uninstalledWebApk2";
 
         FetchStorageCallback storageCallback1 = new FetchStorageCallback(
@@ -359,9 +359,9 @@
     @Test
     @Feature({"WebApk"})
     public void testCleanupDoesNotRemoveInstalledWebApks() throws Exception {
-        String webappId = "webapk:installedWebApk";
+        String webappId = WebApkConstants.WEBAPK_ID_PREFIX + "installedWebApk";
         String webApkPackage = "installedWebApk";
-        String uninstalledWebappId = "webapk:uninstalledWebApk";
+        String uninstalledWebappId = WebApkConstants.WEBAPK_ID_PREFIX + "uninstalledWebApk";
         String uninstalledWebApkPackage = "uninstalledWebApk";
 
         FetchStorageCallback storageCallback = new FetchStorageCallback(
@@ -399,6 +399,46 @@
     }
 
     @Test
+    @Feature({"WebApk"})
+    public void testCleanupDoesRemoveOldInstalledWebApks() throws Exception {
+        String deprecatedWebappId = "webapk:installedWebApk";
+        String webApkPackage = "installedWebApk";
+        String installedWebappId = WebApkConstants.WEBAPK_ID_PREFIX + "installedWebApk";
+
+        FetchStorageCallback storageCallback =
+                new FetchStorageCallback(createWebApkIntent(deprecatedWebappId, webApkPackage));
+        registerWebapp(deprecatedWebappId, storageCallback);
+        assertTrue(storageCallback.getCallbackCalled());
+
+        FetchStorageCallback storageCallback2 =
+                new FetchStorageCallback(createWebApkIntent(installedWebappId, webApkPackage));
+        registerWebapp(installedWebappId, storageCallback2);
+        assertTrue(storageCallback2.getCallbackCalled());
+
+        // Verify that both WebAPKs are registered.
+        Set<String> actual = getRegisteredWebapps();
+        assertEquals(2, actual.size());
+        assertTrue(actual.contains(deprecatedWebappId));
+        assertTrue(actual.contains(installedWebappId));
+
+        RuntimeEnvironment.getRobolectricPackageManager().addPackage(webApkPackage);
+
+        // Set the current time such that the task runs.
+        long currentTime = System.currentTimeMillis() + WebappRegistry.FULL_CLEANUP_DURATION;
+        // Because the time is just inside the window, there should be a cleanup of
+        // uninstalled WebAPKs and the last cleaned up time should be set to the
+        // current time.
+        WebappRegistry.getInstance().unregisterOldWebapps(currentTime);
+
+        actual = getRegisteredWebapps();
+        assertEquals(1, actual.size());
+        assertTrue(actual.contains(installedWebappId));
+
+        long lastCleanup = mSharedPreferences.getLong(WebappRegistry.KEY_LAST_CLEANUP, -1);
+        assertEquals(currentTime, lastCleanup);
+    }
+
+    @Test
     @Feature({"Webapp"})
     public void testClearWebappHistory() throws Exception {
         final String webapp1Url = "https://www.google.com";
diff --git a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkConstants.java b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkConstants.java
index 45b6a2c3..7749f979 100644
--- a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkConstants.java
+++ b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkConstants.java
@@ -11,7 +11,7 @@
     public static final String WEBAPK_PACKAGE_PREFIX = "org.chromium.webapk";
 
     // WebAPK id prefix. The id is used for storing WebAPK data in Chrome's SharedPreferences.
-    public static final String WEBAPK_ID_PREFIX = "webapk:";
+    public static final String WEBAPK_ID_PREFIX = "webapk-";
 
     // These EXTRA_* values must stay in sync with
     // {@link org.chromium.chrome.browser.ShortcutHelper}.
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml
index 44f4572..9aa46e77 100644
--- a/chrome/android/webapk/shell_apk/AndroidManifest.xml
+++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -15,7 +15,7 @@
 
     <application
         android:icon="@mipmap/app_icon"
-        android:label="{{{short_name}}}"
+        android:label="@string/short_name"
         android:allowBackup="false">
         <activity android:name="org.chromium.webapk.shell_apk.MainActivity"
                   android:theme="@android:style/Theme.Translucent.NoTitleBar"
@@ -35,8 +35,6 @@
         {{#runtime_host}}<meta-data android:name="org.chromium.webapk.shell_apk.runtimeHost" android:value="{{{runtime_host}}}" />{{/runtime_host}}
         {{#runtime_host_application_name}}<meta-data android:name="org.chromium.webapk.shell_apk.runtimeHostApplicationName" android:value="{{{runtime_host_application_name}}}" />{{/runtime_host_application_name}}
         <meta-data android:name="org.chromium.webapk.shell_apk.startUrl" android:value="{{{start_url}}}" />
-        <meta-data android:name="org.chromium.webapk.shell_apk.name" android:value="{{{name}}}" />
-        <meta-data android:name="org.chromium.webapk.shell_apk.shortName" android:value="{{{short_name}}}" />
         <meta-data android:name="org.chromium.webapk.shell_apk.scope" android:value="{{{scope_url}}}" />
         <meta-data android:name="org.chromium.webapk.shell_apk.displayMode" android:value="{{{display_mode}}}" />
         <meta-data android:name="org.chromium.webapk.shell_apk.orientation" android:value="{{{orientation}}}" />
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index b87470ce..6a93ecc 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -23,8 +23,6 @@
 
   # Attributes from Web Manifest.
   webapk_start_url = "https://pwa.rocks/"
-  webapk_name = "Progressive Web Apps"
-  webapk_short_name = "PWA List"
   webapk_scope_url = "https://pwa.rocks/"
   webapk_display_mode = "standalone"
   webapk_orientation = "portrait"
@@ -64,8 +62,6 @@
     "runtime_host=$webapk_runtime_host",
     "runtime_host_application_name=$webapk_runtime_host_application_name",
     "start_url=$webapk_start_url",
-    "name=$webapk_name",
-    "short_name=$webapk_short_name",
     "scope_url=$webapk_scope_url",
     "display_mode=$webapk_display_mode",
     "orientation=$webapk_orientation",
@@ -92,8 +88,6 @@
     # Intentionally omitted: "runtime_host=org.chromium.chrome",
     "include_label=true",
     "start_url=$webapk_start_url",
-    "name=$webapk_name",
-    "short_name=$webapk_short_name",
     "scope_url=$webapk_scope_url",
     "display_mode=$webapk_display_mode",
     "orientation=$webapk_orientation",
@@ -119,8 +113,6 @@
     "manifest_package=org.chromium.webapk.test",
     "runtime_host=org.chromium.chrome",
     "start_url=https://pwa.rocks",
-    "name=Test",
-    "short_name=Test",
     "scope_url=https://pwa.rocks",
     "display_mode=standalone",
     "orientation=portrait",
diff --git a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/WebApkUtilsTest.java b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/WebApkUtilsTest.java
index f33a9c8..548f7cbc 100644
--- a/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/WebApkUtilsTest.java
+++ b/chrome/android/webapk/shell_apk/junit/src/org/chromium/webapk/shell_apk/WebApkUtilsTest.java
@@ -53,8 +53,9 @@
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
-        mPackageManager =
-                Mockito.spy((RobolectricPackageManager) RuntimeEnvironment.getPackageManager());
+        WebApkTestHelper.setUpPackageManager();
+
+        mPackageManager = Mockito.spy(RuntimeEnvironment.getRobolectricPackageManager());
         RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
 
         WebApkUtils.resetCachedHostPackageForTesting();
@@ -81,7 +82,7 @@
         setHostBrowserInSharedPreferences(expectedHostBrowser);
 
         String hostBrowser = WebApkUtils.getHostBrowserPackageName(mContext);
-        Assert.assertEquals(hostBrowser, expectedHostBrowser);
+        Assert.assertEquals(expectedHostBrowser, hostBrowser);
     }
 
     /**
@@ -108,7 +109,7 @@
         // Simulates there is a host browser stored in the SharedPreference but uninstalled.
         setHostBrowserInSharedPreferences(BROWSER_UNINSTALLED_SUPPORTING_WEBAPKS);
         hostBrowser = WebApkUtils.getHostBrowserPackageName(mContext);
-        Assert.assertEquals(hostBrowser, expectedHostBrowser);
+        Assert.assertEquals(expectedHostBrowser, hostBrowser);
     }
 
     /**
@@ -148,7 +149,7 @@
         setHostBrowserInSharedPreferences(null);
 
         String hostBrowser = WebApkUtils.getHostBrowserPackageName(mContext);
-        Assert.assertEquals(hostBrowser, defaultBrowser);
+        Assert.assertEquals(defaultBrowser, hostBrowser);
     }
 
     /**
diff --git a/chrome/android/webapk/shell_apk/shell_apk_version.gni b/chrome/android/webapk/shell_apk/shell_apk_version.gni
index 3bf44efd..cdbf4e0 100644
--- a/chrome/android/webapk/shell_apk/shell_apk_version.gni
+++ b/chrome/android/webapk/shell_apk/shell_apk_version.gni
@@ -6,7 +6,7 @@
 # (including AndroidManifest.xml) is updated. This version should be incremented
 # prior to uploading a new ShellAPK to the WebAPK Minting Server.
 # Does not affect Chrome.apk
-template_shell_apk_version = 8
+template_shell_apk_version = 9
 
 # The ShellAPK version expected by Chrome. Chrome will try to update the WebAPK
 # if the WebAPK's ShellAPK version is less than |expected_shell_apk_version|.
diff --git a/chrome/android/webapk/strings/android_webapk_strings.grd b/chrome/android/webapk/strings/android_webapk_strings.grd
index 1ca3d61f..4dfa9d7 100644
--- a/chrome/android/webapk/strings/android_webapk_strings.grd
+++ b/chrome/android/webapk/strings/android_webapk_strings.grd
@@ -112,6 +112,10 @@
       <message name="IDS_INSTALL_HOST_BROWSER_DIALOG_INSTALL_BUTTON" desc="Text for the install button on the dialog. ">
         INSTALL
       </message>
+      <message name="IDS_NAME" desc="The name of the WebAPK." translateable="false">
+      </message>
+      <message name="IDS_SHORT_NAME" desc="The short name of the WebAPK." translateable="false">
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/android/webapk/test/BUILD.gn b/chrome/android/webapk/test/BUILD.gn
index 8a594cd1..a50cfc1 100644
--- a/chrome/android/webapk/test/BUILD.gn
+++ b/chrome/android/webapk/test/BUILD.gn
@@ -10,6 +10,7 @@
   testonly = true
   java_files = [ "src/org/chromium/webapk/test/WebApkTestHelper.java" ]
   deps = [
+    "//third_party/mockito:mockito_java",
     "//third_party/robolectric:robolectric_all_java",
   ]
 }
diff --git a/chrome/android/webapk/test/src/org/chromium/webapk/test/WebApkTestHelper.java b/chrome/android/webapk/test/src/org/chromium/webapk/test/WebApkTestHelper.java
index d8fe09b..1818995 100644
--- a/chrome/android/webapk/test/src/org/chromium/webapk/test/WebApkTestHelper.java
+++ b/chrome/android/webapk/test/src/org/chromium/webapk/test/WebApkTestHelper.java
@@ -6,26 +6,76 @@
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.content.res.Resources;
 import android.os.Bundle;
 
+import org.mockito.Mockito;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.res.builder.DefaultPackageManager;
 import org.robolectric.res.builder.RobolectricPackageManager;
 
+import java.util.HashMap;
+
 /**
  * Helper class for WebAPK JUnit tests.
  */
 public class WebApkTestHelper {
+    /** FakePackageManager allows setting up Resources for installed packages. */
+    private static class FakePackageManager extends DefaultPackageManager {
+        private final HashMap<String, Resources> mResourceMap;
+
+        public FakePackageManager() {
+            mResourceMap = new HashMap<>();
+        }
+
+        @Override
+        public Resources getResourcesForApplication(String appPackageName)
+                throws NameNotFoundException {
+            Resources result = mResourceMap.get(appPackageName);
+            if (result == null) throw new NameNotFoundException(appPackageName);
+
+            return result;
+        }
+
+        public void setResourcesForTest(String packageName, Resources resources) {
+            mResourceMap.put(packageName, resources);
+        }
+    }
+
     /**
-     * Registers WebAPK.
+     * Setups a new {@FakePackageManager}.
+     */
+    public static void setUpPackageManager() {
+        FakePackageManager packageManager = new FakePackageManager();
+        RuntimeEnvironment.setRobolectricPackageManager(packageManager);
+    }
+
+    /**
+     * Registers WebAPK. This function also creates an empty resource for the WebAPK.
      * @param packageName The package to register
      * @param metaData Bundle with meta data from WebAPK's Android Manifest.
      */
     public static void registerWebApkWithMetaData(String packageName, Bundle metaData) {
         RobolectricPackageManager packageManager =
-                (RobolectricPackageManager) RuntimeEnvironment.application.getPackageManager();
+                RuntimeEnvironment.getRobolectricPackageManager();
+        if (!(packageManager instanceof FakePackageManager)) {
+            setUpPackageManager();
+        }
+        packageManager = RuntimeEnvironment.getRobolectricPackageManager();
+        Resources res = Mockito.mock(Resources.class);
+        ((FakePackageManager) packageManager).setResourcesForTest(packageName, res);
         packageManager.addPackage(newPackageInfo(packageName, metaData));
     }
 
+    /** Sets the resource for the given package name. */
+    public static void setResource(String packageName, Resources res) {
+        RobolectricPackageManager packageManager =
+                RuntimeEnvironment.getRobolectricPackageManager();
+        if (packageManager instanceof FakePackageManager) {
+            ((FakePackageManager) packageManager).setResourcesForTest(packageName, res);
+        }
+    }
+
     private static PackageInfo newPackageInfo(String packageName, Bundle metaData) {
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.metaData = metaData;
diff --git a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
index 6ec3b8ed..a4b8cd12 100644
--- a/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_interactive_browsertest.cc
@@ -1741,6 +1741,12 @@
 // This test verifies that focusing an input inside a <webview> will put the
 // guest process's render widget into a monitoring mode for composition range
 // changes.
+// Flaky on Mac (crbug.com/736759)
+#if defined(OS_MACOSX)
+#define MAYBE_CompositionRangeUpdates DISABLED_CompositionRangeUpdates
+#else
+#define MAYBE_CompositionRangeUpdates CompositionRangeUpdates
+#endif
 IN_PROC_BROWSER_TEST_P(WebViewImeInteractiveTest, CompositionRangeUpdates) {
   ASSERT_TRUE(StartEmbeddedTestServer());  // For serving guest pages.
   LoadAndLaunchPlatformApp("web_view/ime", "WebViewImeTest.Launched");
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
index 2e9944c6c..37e50251 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -14,11 +14,11 @@
 #include "ash/autoclick/autoclick_controller.h"
 #include "ash/autoclick/mus/public/interfaces/autoclick.mojom.h"
 #include "ash/high_contrast/high_contrast_controller.h"
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/sticky_keys/sticky_keys_controller.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
@@ -473,7 +473,7 @@
     ash::AccessibilityNotificationVisibility notify) {
   if (!profile_)
     return;
-  ash::ShellPort::Get()->RecordUserMetricsAction(
+  ash::Shell::Get()->metrics()->RecordUserMetricsAction(
       enabled ? ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK
               : ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK);
 
diff --git a/chrome/browser/chromeos/attestation/attestation_policy_browsertest.cc b/chrome/browser/chromeos/attestation/attestation_policy_browsertest.cc
index c04bcc3..89df91a 100644
--- a/chrome/browser/chromeos/attestation/attestation_policy_browsertest.cc
+++ b/chrome/browser/chromeos/attestation/attestation_policy_browsertest.cc
@@ -31,7 +31,7 @@
       const chromeos::BoolDBusMethodCallback& callback) override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::Bind(callback, chromeos::DBUS_METHOD_CALL_FAILURE, false));
+        base::BindOnce(callback, chromeos::DBUS_METHOD_CALL_FAILURE, false));
   }
 };
 
diff --git a/chrome/browser/chromeos/attestation/attestation_policy_observer.cc b/chrome/browser/chromeos/attestation/attestation_policy_observer.cc
index 933996d..5b3b4a78 100644
--- a/chrome/browser/chromeos/attestation/attestation_policy_observer.cc
+++ b/chrome/browser/chromeos/attestation/attestation_policy_observer.cc
@@ -311,8 +311,8 @@
   if (++num_retries_ < kRetryLimit) {
     content::BrowserThread::PostDelayedTask(
         content::BrowserThread::UI, FROM_HERE,
-        base::Bind(&AttestationPolicyObserver::Start,
-                   weak_factory_.GetWeakPtr()),
+        base::BindOnce(&AttestationPolicyObserver::Start,
+                       weak_factory_.GetWeakPtr()),
         base::TimeDelta::FromSeconds(retry_delay_));
   } else {
     LOG(WARNING) << "AttestationPolicyObserver: Retry limit exceeded.";
diff --git a/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc b/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc
index 9f09b4d..2ac6fed9 100644
--- a/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc
+++ b/chrome/browser/chromeos/attestation/attestation_policy_observer_unittest.cc
@@ -38,28 +38,28 @@
 
 void DBusCallbackFalse(const BoolDBusMethodCallback& callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false));
+      FROM_HERE, base::BindOnce(callback, DBUS_METHOD_CALL_SUCCESS, false));
 }
 
 void DBusCallbackTrue(const BoolDBusMethodCallback& callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
+      FROM_HERE, base::BindOnce(callback, DBUS_METHOD_CALL_SUCCESS, true));
 }
 
 void DBusCallbackError(const BoolDBusMethodCallback& callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_FAILURE, false));
+      FROM_HERE, base::BindOnce(callback, DBUS_METHOD_CALL_FAILURE, false));
 }
 
 void CertCallbackSuccess(const AttestationFlow::CertificateCallback& callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, true, "fake_cert"));
+      FROM_HERE, base::BindOnce(callback, true, "fake_cert"));
 }
 
 void StatusCallbackSuccess(
     const policy::CloudPolicyClient::StatusCallback& callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                base::Bind(callback, true));
+                                                base::BindOnce(callback, true));
 }
 
 class FakeDBusData {
@@ -68,7 +68,8 @@
 
   void operator() (const CryptohomeClient::DataMethodCallback& callback) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true, data_));
+        FROM_HERE,
+        base::BindOnce(callback, DBUS_METHOD_CALL_SUCCESS, true, data_));
   }
 
  private:
diff --git a/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc b/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc
index f16d9d3..9e888a69 100644
--- a/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc
+++ b/chrome/browser/chromeos/attestation/platform_verification_flow_unittest.cc
@@ -104,13 +104,15 @@
   void TpmAttestationIsEnrolled(
       const BoolDBusMethodCallback& callback) override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, call_status_, attestation_enrolled_));
+        FROM_HERE,
+        base::BindOnce(callback, call_status_, attestation_enrolled_));
   }
 
   void TpmAttestationIsPrepared(
       const BoolDBusMethodCallback& callback) override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, call_status_, attestation_prepared_));
+        FROM_HERE,
+        base::BindOnce(callback, call_status_, attestation_prepared_));
   }
 
   void set_call_status(DBusMethodCallStatus call_status) {
@@ -186,15 +188,15 @@
         (fake_certificate_index_ < fake_certificate_list_.size()) ?
             fake_certificate_list_[fake_certificate_index_] : kTestCertificate;
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, certificate_success_, certificate));
+        FROM_HERE, base::BindOnce(callback, certificate_success_, certificate));
     ++fake_certificate_index_;
   }
 
   void FakeSignChallenge(
       const cryptohome::AsyncMethodCaller::DataCallback& callback) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, sign_challenge_success_,
-                              CreateFakeResponseProto()));
+        FROM_HERE, base::BindOnce(callback, sign_challenge_success_,
+                                  CreateFakeResponseProto()));
   }
 
   void FakeChallengeCallback(PlatformVerificationFlow::Result result,
diff --git a/chrome/browser/chromeos/policy/active_directory_policy_manager.cc b/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
index 23c19e9..195140d 100644
--- a/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
+++ b/chrome/browser/chromeos/policy/active_directory_policy_manager.cc
@@ -17,9 +17,9 @@
 
 namespace {
 
-// Refresh policy every 90 minutes which matches the Windows default:
+// Fetch policy every 90 minutes which matches the Windows default:
 // https://technet.microsoft.com/en-us/library/cc940895.aspx
-constexpr base::TimeDelta kRefreshInterval = base::TimeDelta::FromMinutes(90);
+constexpr base::TimeDelta kFetchInterval = base::TimeDelta::FromMinutes(90);
 
 }  // namespace
 
@@ -56,11 +56,11 @@
   PublishPolicy();
 
   scheduler_ = base::MakeUnique<PolicyScheduler>(
-      base::BindRepeating(&ActiveDirectoryPolicyManager::DoRefresh,
+      base::BindRepeating(&ActiveDirectoryPolicyManager::DoFetch,
                           weak_ptr_factory_.GetWeakPtr()),
-      base::BindRepeating(&ActiveDirectoryPolicyManager::OnPolicyRefreshed,
+      base::BindRepeating(&ActiveDirectoryPolicyManager::OnPolicyFetched,
                           weak_ptr_factory_.GetWeakPtr()),
-      kRefreshInterval);
+      kFetchInterval);
 }
 
 void ActiveDirectoryPolicyManager::Shutdown() {
@@ -117,7 +117,7 @@
   UpdatePolicy(std::move(bundle));
 }
 
-void ActiveDirectoryPolicyManager::DoRefresh(
+void ActiveDirectoryPolicyManager::DoFetch(
     base::OnceCallback<void(bool success)> callback) {
   chromeos::DBusThreadManager* thread_manager =
       chromeos::DBusThreadManager::Get();
@@ -132,9 +132,9 @@
   }
 }
 
-void ActiveDirectoryPolicyManager::OnPolicyRefreshed(bool success) {
+void ActiveDirectoryPolicyManager::OnPolicyFetched(bool success) {
   if (!success) {
-    LOG(ERROR) << "Active Directory policy refresh failed.";
+    LOG(ERROR) << "Active Directory policy fetch failed.";
   }
   // Load independently of success or failure to keep up to date with whatever
   // has happened on the authpolicyd / session manager side.
diff --git a/chrome/browser/chromeos/policy/active_directory_policy_manager.h b/chrome/browser/chromeos/policy/active_directory_policy_manager.h
index 598c545..46cf742 100644
--- a/chrome/browser/chromeos/policy/active_directory_policy_manager.h
+++ b/chrome/browser/chromeos/policy/active_directory_policy_manager.h
@@ -58,10 +58,10 @@
 
   // Calls into authpolicyd to fetch policy. Reports success or failure via
   // |callback|.
-  void DoRefresh(PolicyScheduler::TaskCallback callback);
+  void DoFetch(PolicyScheduler::TaskCallback callback);
 
   // Called by scheduler with result of policy fetch.
-  void OnPolicyRefreshed(bool success);
+  void OnPolicyFetched(bool success);
 
   const AccountId account_id_;
   std::unique_ptr<CloudPolicyStore> store_;
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
index 728adb2..ce71a60 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
+++ b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
@@ -319,8 +319,8 @@
 void CloudExternalDataManagerBase::Backend::RunCallback(
     const ExternalDataFetcher::FetchCallback& callback,
     std::unique_ptr<std::string> data) const {
-  callback_task_runner_->PostTask(FROM_HERE,
-                                  base::Bind(callback, base::Passed(&data)));
+  callback_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(callback, base::Passed(&data)));
 }
 
 void CloudExternalDataManagerBase::Backend::StartDownload(
@@ -362,10 +362,10 @@
 void CloudExternalDataManagerBase::SetExternalDataStore(
     std::unique_ptr<CloudExternalDataStore> external_data_store) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  backend_task_runner_->PostTask(FROM_HERE, base::Bind(
-      &Backend::SetExternalDataStore,
-      base::Unretained(backend_.get()),
-      base::Passed(&external_data_store)));
+  backend_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&Backend::SetExternalDataStore,
+                                base::Unretained(backend_.get()),
+                                base::Passed(&external_data_store)));
 }
 
 void CloudExternalDataManagerBase::SetPolicyStore(
@@ -404,10 +404,10 @@
     }
   }
 
-  backend_task_runner_->PostTask(FROM_HERE, base::Bind(
-      &Backend::OnMetadataUpdated,
-      base::Unretained(backend_.get()),
-      base::Passed(&metadata)));
+  backend_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&Backend::OnMetadataUpdated,
+                                base::Unretained(backend_.get()),
+                                base::Passed(&metadata)));
 }
 
 void CloudExternalDataManagerBase::Connect(
@@ -417,27 +417,31 @@
   external_policy_data_fetcher_backend_.reset(
       new ExternalPolicyDataFetcherBackend(io_task_runner_,
                                            request_context));
-  backend_task_runner_->PostTask(FROM_HERE, base::Bind(
-      &Backend::Connect,
-      base::Unretained(backend_.get()),
-      base::Passed(external_policy_data_fetcher_backend_->CreateFrontend(
-          backend_task_runner_))));
+  backend_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &Backend::Connect, base::Unretained(backend_.get()),
+          base::Passed(external_policy_data_fetcher_backend_->CreateFrontend(
+              backend_task_runner_))));
 }
 
 void CloudExternalDataManagerBase::Disconnect() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   io_task_runner_->DeleteSoon(FROM_HERE,
                               external_policy_data_fetcher_backend_.release());
-  backend_task_runner_->PostTask(FROM_HERE, base::Bind(
-      &Backend::Disconnect, base::Unretained(backend_.get())));
+  backend_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&Backend::Disconnect, base::Unretained(backend_.get())));
 }
 
 void CloudExternalDataManagerBase::Fetch(
     const std::string& policy,
     const ExternalDataFetcher::FetchCallback& callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  backend_task_runner_->PostTask(FROM_HERE, base::Bind(
-      &Backend::Fetch, base::Unretained(backend_.get()), policy, callback));
+  backend_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&Backend::Fetch, base::Unretained(backend_.get()), policy,
+                     callback));
 }
 
 // static
@@ -448,8 +452,9 @@
 
 void CloudExternalDataManagerBase::FetchAll() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  backend_task_runner_->PostTask(FROM_HERE, base::Bind(
-      &Backend::FetchAll, base::Unretained(backend_.get())));
+  backend_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&Backend::FetchAll, base::Unretained(backend_.get())));
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
index 97308740..bf385518 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos_unittest.cc
@@ -90,7 +90,7 @@
     const chromeos::attestation::AttestationFlow::CertificateCallback&
         callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, true, "fake_cert"));
+      FROM_HERE, base::BindOnce(callback, true, "fake_cert"));
 }
 
 class TestingDeviceCloudPolicyManagerChromeOS
diff --git a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
index 38a541ba..cef7441 100644
--- a/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_browsertest.cc
@@ -526,7 +526,7 @@
 
     // This shuts down the login UI.
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&chrome::AttemptExit));
+        FROM_HERE, base::BindOnce(&chrome::AttemptExit));
     base::RunLoop().RunUntilIdle();
   }
 
diff --git a/chrome/browser/chromeos/policy/device_local_account_external_data_service.cc b/chrome/browser/chromeos/policy/device_local_account_external_data_service.cc
index 3e38624a..92eb02e 100644
--- a/chrome/browser/chromeos/policy/device_local_account_external_data_service.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_external_data_service.cc
@@ -61,10 +61,10 @@
       ++it;
     }
   }
-  backend_task_runner_->PostTask(FROM_HERE, base::Bind(
-      &ResourceCache::PurgeOtherKeys,
-      base::Unretained(resource_cache_.get()),
-      account_ids));
+  backend_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&ResourceCache::PurgeOtherKeys,
+                     base::Unretained(resource_cache_.get()), account_ids));
 }
 
 scoped_refptr<DeviceLocalAccountExternalDataManager>
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
index 26c8188..a20dbfab 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
@@ -391,12 +391,10 @@
   // be removed. The directory must stay marked as busy while the removal is in
   // progress.
   extension_cache_task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::Bind(&DeleteObsoleteExtensionCache, account_id),
-      base::Bind(&DeviceLocalAccountPolicyService::
-                     OnObsoleteExtensionCacheDeleted,
-                 weak_factory_.GetWeakPtr(),
-                 account_id));
+      FROM_HERE, base::BindOnce(&DeleteObsoleteExtensionCache, account_id),
+      base::BindOnce(
+          &DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheDeleted,
+          weak_factory_.GetWeakPtr(), account_id));
 }
 
 void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheDeleted(
@@ -509,9 +507,9 @@
                            &cache_root_dir));
     extension_cache_task_runner_->PostTaskAndReply(
         FROM_HERE,
-        base::Bind(
-            &DeleteOrphanedCaches, cache_root_dir, subdirectories_to_keep),
-        base::Bind(
+        base::BindOnce(&DeleteOrphanedCaches, cache_root_dir,
+                       subdirectories_to_keep),
+        base::BindOnce(
             &DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted,
             weak_factory_.GetWeakPtr()));
 
@@ -536,10 +534,10 @@
   // TODO(joaodasilva): for now this must be posted to the FILE thread,
   // to avoid racing with the ComponentCloudPolicyStore. Use a task runner
   // once that class supports another background thread too.
-  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
-                                   base::Bind(&DeleteOrphanedCaches,
-                                              component_policy_cache_root_,
-                                              subdirectories_to_keep));
+  content::BrowserThread::PostTask(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::BindOnce(&DeleteOrphanedCaches, component_policy_cache_root_,
+                     subdirectories_to_keep));
 
   for (auto& observer : observers_)
     observer.OnDeviceLocalAccountsChanged();
diff --git a/chrome/browser/chromeos/policy/device_status_collector.cc b/chrome/browser/chromeos/policy/device_status_collector.cc
index 3c7445e..fab14678 100644
--- a/chrome/browser/chromeos/policy/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector.cc
@@ -393,9 +393,9 @@
   // async query, the query holds a reference to us, so the destructor is
   // not called.
   ~GetStatusState() {
-    task_runner_->PostTask(FROM_HERE,
-                           base::Bind(response_, base::Passed(&device_status_),
-                                      base::Passed(&session_status_)));
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(response_, base::Passed(&device_status_),
+                                  base::Passed(&session_status_)));
   }
 
   void OnVolumeInfoReceived(const std::vector<em::VolumeInfo>& volume_info) {
diff --git a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
index 0afa872e..e408233 100644
--- a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
@@ -245,8 +245,8 @@
     const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver) {
   // Post it to the thread because this call is expected to be asynchronous.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&CallAndroidStatusReceiver, receiver,
-          status, droid_guard_info));
+      FROM_HERE, base::BindOnce(&CallAndroidStatusReceiver, receiver, status,
+                                droid_guard_info));
   return true;
 }
 
@@ -254,7 +254,7 @@
     const policy::DeviceStatusCollector::AndroidStatusReceiver& receiver) {
   // Post it to the thread because this call is expected to be asynchronous.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&CallAndroidStatusReceiver, receiver, "", ""));
+      FROM_HERE, base::BindOnce(&CallAndroidStatusReceiver, receiver, "", ""));
   return true;
 }
 
diff --git a/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc b/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
index 233c124..8302e05 100644
--- a/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc
@@ -52,7 +52,7 @@
     // If the login display is still showing, exit gracefully.
     if (LoginDisplayHost::default_host()) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&chrome::AttemptExit));
+          FROM_HERE, base::BindOnce(&chrome::AttemptExit));
       content::RunMessageLoop();
     }
   }
diff --git a/chrome/browser/chromeos/policy/display_rotation_default_handler_browsertest.cc b/chrome/browser/chromeos/policy/display_rotation_default_handler_browsertest.cc
index 140fdce9..c2409019 100644
--- a/chrome/browser/chromeos/policy/display_rotation_default_handler_browsertest.cc
+++ b/chrome/browser/chromeos/policy/display_rotation_default_handler_browsertest.cc
@@ -92,7 +92,7 @@
     // If the login display is still showing, exit gracefully.
     if (chromeos::LoginDisplayHost::default_host()) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&chrome::AttemptExit));
+          FROM_HERE, base::BindOnce(&chrome::AttemptExit));
       content::RunMessageLoop();
     }
   }
diff --git a/chrome/browser/chromeos/policy/dm_token_storage_unittest.cc b/chrome/browser/chromeos/policy/dm_token_storage_unittest.cc
index bce508e..9d753b25 100644
--- a/chrome/browser/chromeos/policy/dm_token_storage_unittest.cc
+++ b/chrome/browser/chromeos/policy/dm_token_storage_unittest.cc
@@ -67,8 +67,8 @@
     base::RunLoop run_loop;
     dm_token_storage_->StoreDMToken(
         "test-token",
-        base::Bind(&DMTokenStorageTest::OnStoreCallback, base::Unretained(this),
-                   run_loop.QuitClosure(), true));
+        base::BindOnce(&DMTokenStorageTest::OnStoreCallback,
+                       base::Unretained(this), run_loop.QuitClosure(), true));
     run_loop.Run();
   }
 
@@ -100,7 +100,7 @@
 
   {
     base::RunLoop run_loop;
-    dm_token_storage_->RetrieveDMToken(base::Bind(
+    dm_token_storage_->RetrieveDMToken(base::BindOnce(
         &DMTokenStorageTest::OnRetrieveCallback, base::Unretained(this),
         run_loop.QuitClosure(), "test-token"));
     run_loop.Run();
@@ -109,7 +109,7 @@
   CreateDMStorage();
   {
     base::RunLoop run_loop;
-    dm_token_storage_->RetrieveDMToken(base::Bind(
+    dm_token_storage_->RetrieveDMToken(base::BindOnce(
         &DMTokenStorageTest::OnRetrieveCallback, base::Unretained(this),
         run_loop.QuitClosure(), "test-token"));
     run_loop.Run();
@@ -117,7 +117,7 @@
   {
     // Subsequent retrieving DM token should succeed.
     base::RunLoop run_loop;
-    dm_token_storage_->RetrieveDMToken(base::Bind(
+    dm_token_storage_->RetrieveDMToken(base::BindOnce(
         &DMTokenStorageTest::OnRetrieveCallback, base::Unretained(this),
         run_loop.QuitClosure(), "test-token"));
     run_loop.Run();
@@ -133,7 +133,7 @@
 
   {
     base::RunLoop run_loop;
-    dm_token_storage_->RetrieveDMToken(base::Bind(
+    dm_token_storage_->RetrieveDMToken(base::BindOnce(
         &DMTokenStorageTest::OnRetrieveCallback, base::Unretained(this),
         run_loop.QuitClosure(), "test-token"));
     SetSaltAvailable();
@@ -147,8 +147,8 @@
   base::RunLoop run_loop;
   dm_token_storage_->StoreDMToken(
       "test-token",
-      base::Bind(&DMTokenStorageTest::OnStoreCallback, base::Unretained(this),
-                 run_loop.QuitClosure(), true));
+      base::BindOnce(&DMTokenStorageTest::OnStoreCallback,
+                     base::Unretained(this), run_loop.QuitClosure(), true));
   SetSaltAvailable();
   run_loop.Run();
 }
@@ -159,7 +159,7 @@
   {
     base::RunLoop run_loop;
     for (int i = 0; i < 3; ++i) {
-      dm_token_storage_->RetrieveDMToken(base::Bind(
+      dm_token_storage_->RetrieveDMToken(base::BindOnce(
           &DMTokenStorageTest::OnRetrieveCallback, base::Unretained(this),
           run_loop.QuitClosure(), "test-token"));
     }
@@ -173,8 +173,8 @@
   base::RunLoop run_loop;
   dm_token_storage_->StoreDMToken(
       "test-token",
-      base::Bind(&DMTokenStorageTest::OnStoreCallback, base::Unretained(this),
-                 run_loop.QuitClosure(), false));
+      base::BindOnce(&DMTokenStorageTest::OnStoreCallback,
+                     base::Unretained(this), run_loop.QuitClosure(), false));
   run_loop.Run();
 }
 
@@ -185,8 +185,8 @@
   CreateDMStorage();
   base::RunLoop run_loop;
   dm_token_storage_->RetrieveDMToken(
-      base::Bind(&DMTokenStorageTest::OnRetrieveCallback,
-                 base::Unretained(this), run_loop.QuitClosure(), ""));
+      base::BindOnce(&DMTokenStorageTest::OnRetrieveCallback,
+                     base::Unretained(this), run_loop.QuitClosure(), ""));
   SetSaltError();
   run_loop.Run();
 }
@@ -195,8 +195,8 @@
   CreateDMStorage();
   base::RunLoop run_loop;
   dm_token_storage_->RetrieveDMToken(
-      base::Bind(&DMTokenStorageTest::OnRetrieveCallback,
-                 base::Unretained(this), run_loop.QuitClosure(), ""));
+      base::BindOnce(&DMTokenStorageTest::OnRetrieveCallback,
+                     base::Unretained(this), run_loop.QuitClosure(), ""));
   run_loop.Run();
 }
 
@@ -206,11 +206,11 @@
   base::RunLoop run_loop;
   dm_token_storage_->StoreDMToken(
       "test-token",
-      base::Bind(&DMTokenStorageTest::OnStoreCallback, base::Unretained(this),
-                 run_loop.QuitClosure(), true));
+      base::BindOnce(&DMTokenStorageTest::OnStoreCallback,
+                     base::Unretained(this), run_loop.QuitClosure(), true));
   dm_token_storage_->RetrieveDMToken(
-      base::Bind(&DMTokenStorageTest::OnRetrieveCallback,
-                 base::Unretained(this), base::Closure(), ""));
+      base::BindOnce(&DMTokenStorageTest::OnRetrieveCallback,
+                     base::Unretained(this), base::Closure(), ""));
   SetSaltAvailable();
   run_loop.Run();
 }
@@ -221,11 +221,12 @@
   base::RunLoop run_loop;
   dm_token_storage_->StoreDMToken(
       "test-token",
-      base::Bind(&DMTokenStorageTest::OnStoreCallback, base::Unretained(this),
-                 run_loop.QuitClosure(), true));
+      base::BindOnce(&DMTokenStorageTest::OnStoreCallback,
+                     base::Unretained(this), run_loop.QuitClosure(), true));
   dm_token_storage_->StoreDMToken(
-      "test-token", base::Bind(&DMTokenStorageTest::OnStoreCallback,
-                               base::Unretained(this), base::Closure(), false));
+      "test-token",
+      base::BindOnce(&DMTokenStorageTest::OnStoreCallback,
+                     base::Unretained(this), base::Closure(), false));
   SetSaltAvailable();
   run_loop.Run();
 }
diff --git a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
index 40fcb27..a7b968e 100644
--- a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
+++ b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
@@ -539,8 +539,9 @@
         LOG(WARNING) << "Install Attributes not ready yet will retry in "
                      << kLockRetryIntervalMs << "ms.";
         base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-            FROM_HERE, base::Bind(&EnrollmentHandlerChromeOS::StartLockDevice,
-                                  weak_ptr_factory_.GetWeakPtr()),
+            FROM_HERE,
+            base::BindOnce(&EnrollmentHandlerChromeOS::StartLockDevice,
+                           weak_ptr_factory_.GetWeakPtr()),
             base::TimeDelta::FromMilliseconds(kLockRetryIntervalMs));
         lockbox_init_duration_ += kLockRetryIntervalMs;
       } else {
@@ -567,8 +568,8 @@
       g_browser_process->local_state());
   dm_token_storage_->StoreDMToken(
       client_->dm_token(),
-      base::Bind(&EnrollmentHandlerChromeOS::HandleDMTokenStoreResult,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&EnrollmentHandlerChromeOS::HandleDMTokenStoreResult,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void EnrollmentHandlerChromeOS::StartStoreRobotAuth() {
@@ -605,7 +606,7 @@
         install_attributes_->GetMode());
     chromeos::DBusThreadManager::Get()
         ->GetAuthPolicyClient()
-        ->RefreshDevicePolicy(base::Bind(
+        ->RefreshDevicePolicy(base::BindOnce(
             &EnrollmentHandlerChromeOS::HandleActiveDirectoryPolicyRefreshed,
             weak_ptr_factory_.GetWeakPtr()));
   } else {
diff --git a/chrome/browser/chromeos/policy/heartbeat_scheduler.cc b/chrome/browser/chromeos/policy/heartbeat_scheduler.cc
index bb831ec..4378701 100644
--- a/chrome/browser/chromeos/policy/heartbeat_scheduler.cc
+++ b/chrome/browser/chromeos/policy/heartbeat_scheduler.cc
@@ -152,8 +152,8 @@
       // Transient error - try again after a delay.
       task_runner_->PostDelayedTask(
           FROM_HERE,
-          base::Bind(&HeartbeatRegistrationHelper::AttemptRegistration,
-                     weak_factory_.GetWeakPtr()),
+          base::BindOnce(&HeartbeatRegistrationHelper::AttemptRegistration,
+                         weak_factory_.GetWeakPtr()),
           base::TimeDelta::FromMilliseconds(kRegistrationRetryDelayMs));
       break;
 
diff --git a/chrome/browser/chromeos/policy/login_screen_default_policy_browsertest.cc b/chrome/browser/chromeos/policy/login_screen_default_policy_browsertest.cc
index a60e978..2fb50b2 100644
--- a/chrome/browser/chromeos/policy/login_screen_default_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/login_screen_default_policy_browsertest.cc
@@ -192,7 +192,7 @@
 
 void LoginScreenDefaultPolicyLoginScreenBrowsertest::TearDownOnMainThread() {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&chrome::AttemptExit));
+      FROM_HERE, base::BindOnce(&chrome::AttemptExit));
   base::RunLoop().RunUntilIdle();
   LoginScreenDefaultPolicyBrowsertestBase::TearDownOnMainThread();
 }
diff --git a/chrome/browser/chromeos/policy/policy_cert_service.cc b/chrome/browser/chromeos/policy/policy_cert_service.cc
index 2d77e53e..85af6d1 100644
--- a/chrome/browser/chromeos/policy/policy_cert_service.cc
+++ b/chrome/browser/chromeos/policy/policy_cert_service.cc
@@ -89,11 +89,9 @@
   // Note: ProfileIOData, which owns the CertVerifier is deleted by a
   // DeleteSoon on IO, i.e. after all pending tasks on IO are finished.
   content::BrowserThread::PostTask(
-      content::BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&PolicyCertVerifier::SetTrustAnchors,
-                 base::Unretained(cert_verifier_),
-                 trust_anchors));
+      content::BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&PolicyCertVerifier::SetTrustAnchors,
+                     base::Unretained(cert_verifier_), trust_anchors));
 }
 
 bool PolicyCertService::UsedPolicyCertificates() const {
diff --git a/chrome/browser/chromeos/policy/power_policy_browsertest.cc b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
index 5726d26..4dfb14a 100644
--- a/chrome/browser/chromeos/policy/power_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/power_policy_browsertest.cc
@@ -302,7 +302,7 @@
 
 void PowerPolicyLoginScreenBrowserTest::TearDownOnMainThread() {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&chrome::AttemptExit));
+      FROM_HERE, base::BindOnce(&chrome::AttemptExit));
   base::RunLoop().RunUntilIdle();
   PowerPolicyBrowserTestBase::TearDownOnMainThread();
 }
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_fetch_status_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_fetch_status_job.cc
index 909175c..ecf3bcc1 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_fetch_status_job.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_fetch_status_job.cc
@@ -62,12 +62,12 @@
     manager->GetStatusUploader()->ScheduleNextStatusUploadImmediately();
     manager->GetSystemLogUploader()->ScheduleNextSystemLogUploadImmediately();
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(succeeded_callback, nullptr));
+        FROM_HERE, base::BindOnce(succeeded_callback, nullptr));
     return;
   }
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(failed_callback, nullptr));
+      FROM_HERE, base::BindOnce(failed_callback, nullptr));
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.cc
index 6e777b0..d195edf 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.cc
@@ -63,7 +63,7 @@
     SYSLOG(WARNING) << "Ignoring reboot command issued " << delta
                     << " before current boot time";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(succeeded_callback, nullptr));
+        FROM_HERE, base::BindOnce(succeeded_callback, nullptr));
     return;
   }
 
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.cc
index 249f3d9..e2e615a 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job.cc
@@ -62,7 +62,7 @@
     scoped_refptr<base::TaskRunner> task_runner,
     scoped_refptr<base::RefCountedMemory> png_data) {
   task_runner->PostTask(FROM_HERE,
-                        base::Bind(store_screenshot_callback, png_data));
+                        base::BindOnce(store_screenshot_callback, png_data));
 }
 
 }  // namespace
@@ -112,8 +112,9 @@
 void DeviceCommandScreenshotJob::OnSuccess() {
   SYSLOG(INFO) << "Upload successful.";
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(succeeded_callback_,
-                            base::Passed(base::MakeUnique<Payload>(SUCCESS))));
+      FROM_HERE,
+      base::BindOnce(succeeded_callback_,
+                     base::Passed(base::MakeUnique<Payload>(SUCCESS))));
 }
 
 void DeviceCommandScreenshotJob::OnFailure(UploadJob::ErrorCode error_code) {
@@ -130,8 +131,8 @@
   }
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      base::Bind(failed_callback_,
-                 base::Passed(base::MakeUnique<Payload>(result_code))));
+      base::BindOnce(failed_callback_,
+                     base::Passed(base::MakeUnique<Payload>(result_code))));
 }
 
 bool DeviceCommandScreenshotJob::IsExpired(base::TimeTicks now) {
@@ -199,8 +200,8 @@
     SYSLOG(ERROR) << "Screenshots are not allowed.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::Bind(failed_callback_, base::Passed(base::MakeUnique<Payload>(
-                                         FAILURE_USER_INPUT))));
+        base::BindOnce(failed_callback_, base::Passed(base::MakeUnique<Payload>(
+                                             FAILURE_USER_INPUT))));
   }
 
   aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
@@ -210,8 +211,8 @@
     SYSLOG(ERROR) << upload_url_ << " is not a valid URL.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::Bind(failed_callback_, base::Passed(base::MakeUnique<Payload>(
-                                         FAILURE_INVALID_URL))));
+        base::BindOnce(failed_callback_, base::Passed(base::MakeUnique<Payload>(
+                                             FAILURE_INVALID_URL))));
     return;
   }
 
@@ -220,8 +221,8 @@
     SYSLOG(ERROR) << "No attached screens.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::Bind(failed_callback_, base::Passed(base::MakeUnique<Payload>(
-                                         FAILURE_SCREENSHOT_ACQUISITION))));
+        base::BindOnce(failed_callback_, base::Passed(base::MakeUnique<Payload>(
+                                             FAILURE_SCREENSHOT_ACQUISITION))));
     return;
   }
 
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job_unittest.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job_unittest.cc
index 72b3b4e5..1fa8b59 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job_unittest.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_screenshot_job_unittest.cc
@@ -106,13 +106,13 @@
   EXPECT_EQ(kMockUploadUrl, upload_url_.spec());
   if (error_code_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&UploadJob::Delegate::OnFailure,
-                              base::Unretained(delegate_), *error_code_));
+        FROM_HERE, base::BindOnce(&UploadJob::Delegate::OnFailure,
+                                  base::Unretained(delegate_), *error_code_));
     return;
   }
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(&UploadJob::Delegate::OnSuccess, base::Unretained(delegate_)));
+      FROM_HERE, base::BindOnce(&UploadJob::Delegate::OnSuccess,
+                                base::Unretained(delegate_)));
 }
 
 scoped_refptr<base::RefCountedBytes> GenerateTestPNG(const int& width,
@@ -177,8 +177,8 @@
   const int height = source_rect.height();
   scoped_refptr<base::RefCountedBytes> test_png =
       GenerateTestPNG(width, height);
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                base::Bind(callback, test_png));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(callback, test_png));
 }
 
 std::unique_ptr<UploadJob> MockScreenshotDelegate::CreateUploadJob(
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_set_volume_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_set_volume_job.cc
index 993f378..7437130 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_set_volume_job.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_set_volume_job.cc
@@ -71,7 +71,7 @@
   audio_handler->SetOutputMute(mute);
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(succeeded_callback, nullptr));
+      FROM_HERE, base::BindOnce(succeeded_callback, nullptr));
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/server_backed_state_keys_broker.cc b/chrome/browser/chromeos/policy/server_backed_state_keys_broker.cc
index db32df02..526dbe8 100644
--- a/chrome/browser/chromeos/policy/server_backed_state_keys_broker.cc
+++ b/chrome/browser/chromeos/policy/server_backed_state_keys_broker.cc
@@ -97,8 +97,8 @@
 
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&ServerBackedStateKeysBroker::FetchStateKeys,
-                 weak_factory_.GetWeakPtr()),
+      base::BindOnce(&ServerBackedStateKeysBroker::FetchStateKeys,
+                     weak_factory_.GetWeakPtr()),
       kPollInterval);
 }
 
diff --git a/chrome/browser/chromeos/policy/system_log_uploader.cc b/chrome/browser/chromeos/policy/system_log_uploader.cc
index 7a97883..f49a4da1 100644
--- a/chrome/browser/chromeos/policy/system_log_uploader.cc
+++ b/chrome/browser/chromeos/policy/system_log_uploader.cc
@@ -341,10 +341,11 @@
   // Ensure that we never have more than one pending delayed task
   // (InvalidateWeakPtrs() will cancel any pending log uploads).
   weak_factory_.InvalidateWeakPtrs();
-  task_runner_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&SystemLogUploader::StartLogUpload,
-                                           weak_factory_.GetWeakPtr()),
-                                delay);
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&SystemLogUploader::StartLogUpload,
+                     weak_factory_.GetWeakPtr()),
+      delay);
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/upload_job_impl.cc b/chrome/browser/chromeos/policy/upload_job_impl.cc
index c74b147..834294e 100644
--- a/chrome/browser/chromeos/policy/upload_job_impl.cc
+++ b/chrome/browser/chromeos/policy/upload_job_impl.cc
@@ -378,8 +378,9 @@
                                             access_token_);
       access_token_.clear();
       task_runner_->PostDelayedTask(
-          FROM_HERE, base::Bind(&UploadJobImpl::RequestAccessToken,
-                                weak_factory_.GetWeakPtr()),
+          FROM_HERE,
+          base::BindOnce(&UploadJobImpl::RequestAccessToken,
+                         weak_factory_.GetWeakPtr()),
           base::TimeDelta::FromMilliseconds(g_retry_delay_ms));
     } else {
       // Retry without a new token.
@@ -387,7 +388,8 @@
       SYSLOG(WARNING) << "Retrying upload with the same token.";
       task_runner_->PostDelayedTask(
           FROM_HERE,
-          base::Bind(&UploadJobImpl::StartUpload, weak_factory_.GetWeakPtr()),
+          base::BindOnce(&UploadJobImpl::StartUpload,
+                         weak_factory_.GetWeakPtr()),
           base::TimeDelta::FromMilliseconds(g_retry_delay_ms));
     }
   }
diff --git a/chrome/browser/chromeos/policy/upload_job_unittest.cc b/chrome/browser/chromeos/policy/upload_job_unittest.cc
index 0e0f9b59..896195ea 100644
--- a/chrome/browser/chromeos/policy/upload_job_unittest.cc
+++ b/chrome/browser/chromeos/policy/upload_job_unittest.cc
@@ -118,9 +118,10 @@
     token_replies_.pop();
   }
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&OAuth2TokenService::RequestImpl::InformConsumer,
-                            request->AsWeakPtr(), response_error, access_token,
-                            response_expiration));
+      FROM_HERE,
+      base::BindOnce(&OAuth2TokenService::RequestImpl::InformConsumer,
+                     request->AsWeakPtr(), response_error, access_token,
+                     response_expiration));
 }
 
 void MockOAuth2TokenService::AddTokenToQueue(const std::string& token) {
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
index 82d1ffa..b2e6fa9f 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
@@ -305,10 +305,11 @@
 
   std::string* key = new std::string();
   background_task_runner()->PostTaskAndReply(
-      FROM_HERE, base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
-                            cached_policy_key_path_, key),
-      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
-                 weak_factory_.GetWeakPtr(), base::Owned(key), callback));
+      FROM_HERE,
+      base::BindOnce(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
+                     cached_policy_key_path_, key),
+      base::BindOnce(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
+                     weak_factory_.GetWeakPtr(), base::Owned(key), callback));
 }
 
 // static
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc
index 7a7d2f5..ca3786cb 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos_unittest.cc
@@ -57,7 +57,7 @@
 
 ACTION_P2(SendSanitizedUsername, call_status, sanitized_username) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(arg1, call_status, sanitized_username));
+      FROM_HERE, base::BindOnce(arg1, call_status, sanitized_username));
 }
 
 class UserCloudPolicyStoreChromeOSTest : public testing::Test {
diff --git a/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.js b/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.js
index 70147ab5..96f7f3d 100644
--- a/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.js
+++ b/chrome/browser/resources/pdf/elements/viewer-page-indicator/viewer-page-indicator.js
@@ -31,14 +31,15 @@
   /** @param {number} displayTime */
   fadeIn: function(displayTime) {
     var percent = window.scrollY /
-        (document.body.scrollHeight - document.documentElement.clientHeight);
+        (document.scrollingElement.scrollHeight -
+         document.documentElement.clientHeight);
     this.style.top =
         percent * (document.documentElement.clientHeight - this.offsetHeight) +
         'px';
     // <if expr="is_macosx">
     // On the Mac, if overlay scrollbars are enabled, prevent them from
     // overlapping the triangle.
-    if (window.innerWidth == document.body.scrollWidth)
+    if (window.innerWidth == document.scrollingElement.scrollWidth)
       this.style.right = '16px';
     else
       this.style.right = '0px';
diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
index 8df12d96..abbb6f40 100644
--- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc
@@ -6,11 +6,11 @@
 
 #include <string>
 
+#include "ash/metrics/user_metrics_recorder.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
-#include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/wallpaper/wallpaper_delegate.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
@@ -125,12 +125,12 @@
         // TODO(simonhong): Use ShelfItemDelegate::Close().
         controller_->Close(item_.id);
       }
-      ash::ShellPort::Get()->RecordUserMetricsAction(
+      ash::Shell::Get()->metrics()->RecordUserMetricsAction(
           ash::UMA_CLOSE_THROUGH_CONTEXT_MENU);
       if (ash::Shell::Get()
               ->maximize_mode_controller()
               ->IsMaximizeModeWindowManagerEnabled()) {
-        ash::ShellPort::Get()->RecordUserMetricsAction(
+        ash::Shell::Get()->metrics()->RecordUserMetricsAction(
             ash::UMA_TABLET_WINDOW_CLOSE_THROUGH_CONTXT_MENU);
       }
       break;
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.mm
index 87282df..304687d21 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.mm
@@ -496,4 +496,18 @@
   return title;
 }
 
+- (id)accessibilityAttributeValue:(NSString*)attribute {
+  // The buttons on the bookmark bar are like popup buttons, in that they make a
+  // new menu appear, so they ascribe to that role. All other items in the
+  // bookmark menus end up being "buttons". This logic detects bookmark bar
+  // buttons as those which have a corresponding folder but are not actually
+  // folder buttons, which are used inside the bookmark menus.
+  // TODO(lgrey): move menus over to using the MenuItem role
+  if ([attribute isEqual:NSAccessibilityRoleAttribute] && [self bookmarkNode] &&
+      [self bookmarkNode]->is_folder() && ![self isFolderButtonCell]) {
+    return NSAccessibilityPopUpButtonRole;
+  }
+  return [super accessibilityAttributeValue:attribute];
+}
+
 @end
diff --git a/chromecast/browser/test/cast_features_browsertest.cc b/chromecast/browser/test/cast_features_browsertest.cc
index 67071f3..dacbc34 100644
--- a/chromecast/browser/test/cast_features_browsertest.cc
+++ b/chromecast/browser/test/cast_features_browsertest.cc
@@ -290,8 +290,14 @@
   ASSERT_TRUE(GetDCSExperimentIds().empty());
 }
 
+#if defined(OS_LINUX)
+#define MAYBE_TestExperimentIdsPersisted DISABLED_TestExperimentIdsPersisted
+#else
+#define MAYBE_TestExperimentIdsPersisted TestExperimentIdsPersisted
+#endif
 // Test that experiment ids are persisted to disk. Part 3 of 3.
-IN_PROC_BROWSER_TEST_F(CastFeaturesBrowserTest, TestExperimentIdsPersisted) {
+IN_PROC_BROWSER_TEST_F(CastFeaturesBrowserTest,
+                       MAYBE_TestExperimentIdsPersisted) {
   // The experiments set in the last run should be active now.
   std::unordered_set<int32_t> ids({1234, 1, 4000});
   ASSERT_EQ(ids, GetDCSExperimentIds());
diff --git a/content/browser/renderer_host/media/audio_input_renderer_host.cc b/content/browser/renderer_host/media/audio_input_renderer_host.cc
index 439da33..dc96e62 100644
--- a/content/browser/renderer_host/media/audio_input_renderer_host.cc
+++ b/content/browser/renderer_host/media/audio_input_renderer_host.cc
@@ -135,12 +135,12 @@
   BrowserThread::DeleteOnIOThread::Destruct(this);
 }
 
-void AudioInputRendererHost::OnCreated(
-    media::AudioInputController* controller) {
+void AudioInputRendererHost::OnCreated(media::AudioInputController* controller,
+                                       bool initially_muted) {
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::BindOnce(&AudioInputRendererHost::DoCompleteCreation, this,
-                     base::RetainedRef(controller)));
+                     base::RetainedRef(controller), initially_muted));
 }
 
 void AudioInputRendererHost::OnError(media::AudioInputController* controller,
@@ -173,7 +173,8 @@
 }
 
 void AudioInputRendererHost::DoCompleteCreation(
-    media::AudioInputController* controller) {
+    media::AudioInputController* controller,
+    bool initially_muted) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   AudioEntry* entry = LookupByController(controller);
@@ -205,13 +206,15 @@
   }
 
   LogMessage(entry->stream_id,
-             "DoCompleteCreation: IPC channel and stream are now open",
+             base::StringPrintf("DoCompleteCreation: IPC channel and stream "
+                                "are now open (initially %s",
+                                initially_muted ? "muted" : "not muted"),
              true);
 
   Send(new AudioInputMsg_NotifyStreamCreated(
       entry->stream_id, foreign_memory_handle, socket_transit_descriptor,
       writer->shared_memory()->requested_size(),
-      writer->shared_memory_segment_count()));
+      writer->shared_memory_segment_count(), initially_muted));
 
   // Free the foreign socket on here since it isn't needed anymore in this
   // process.
diff --git a/content/browser/renderer_host/media/audio_input_renderer_host.h b/content/browser/renderer_host/media/audio_input_renderer_host.h
index 47682e3..281a83a5 100644
--- a/content/browser/renderer_host/media/audio_input_renderer_host.h
+++ b/content/browser/renderer_host/media/audio_input_renderer_host.h
@@ -110,7 +110,8 @@
   bool OnMessageReceived(const IPC::Message& message) override;
 
   // AudioInputController::EventHandler implementation.
-  void OnCreated(media::AudioInputController* controller) override;
+  void OnCreated(media::AudioInputController* controller,
+                 bool initially_muted) override;
   void OnError(media::AudioInputController* controller,
                media::AudioInputController::ErrorCode error_code) override;
   void OnLog(media::AudioInputController* controller,
@@ -165,7 +166,8 @@
   // Complete the process of creating an audio input stream. This will set up
   // the shared memory or shared socket in low latency mode and send the
   // NotifyStreamCreated message to the peer.
-  void DoCompleteCreation(media::AudioInputController* controller);
+  void DoCompleteCreation(media::AudioInputController* controller,
+                          bool initially_muted);
 
   // Send a state change message to the renderer.
   void DoSendRecordingMessage(media::AudioInputController* controller);
diff --git a/content/browser/renderer_host/media/audio_input_renderer_host_unittest.cc b/content/browser/renderer_host/media/audio_input_renderer_host_unittest.cc
index 24881e1..c441897 100644
--- a/content/browser/renderer_host/media/audio_input_renderer_host_unittest.cc
+++ b/content/browser/renderer_host/media/audio_input_renderer_host_unittest.cc
@@ -77,12 +77,13 @@
 
 class MockRenderer {
  public:
-  MOCK_METHOD5(NotifyStreamCreated,
+  MOCK_METHOD6(NotifyStreamCreated,
                void(int /*stream_id*/,
                     base::SharedMemoryHandle /*handle*/,
                     base::SyncSocket::TransitDescriptor /*socket_desriptor*/,
                     uint32_t /*length*/,
-                    uint32_t /*total_segments*/));
+                    uint32_t /*total_segments*/,
+                    bool initially_muted));
   MOCK_METHOD1(NotifyStreamError, void(int /*stream_id*/));
   MOCK_METHOD0(WasShutDown, void());
 };
@@ -138,7 +139,8 @@
       base::SharedMemoryHandle handle,
       base::SyncSocket::TransitDescriptor socket_descriptor,
       uint32_t length,
-      uint32_t total_segments) {
+      uint32_t total_segments,
+      bool initially_muted) {
     // It's difficult to check that the sync socket and shared memory is
     // valid in the gmock macros, so we check them here.
     EXPECT_NE(base::SyncSocket::UnwrapHandle(socket_descriptor),
@@ -146,7 +148,7 @@
     base::SharedMemory memory(handle, /*read_only*/ true);
     EXPECT_TRUE(memory.Map(length));
     renderer_->NotifyStreamCreated(stream_id, handle, socket_descriptor, length,
-                                   total_segments);
+                                   total_segments, initially_muted);
     EXPECT_TRUE(memory.Unmap());
     memory.Close();
   }
@@ -177,8 +179,8 @@
     GetTaskRunnerForTesting()->PostTask(
         FROM_HERE,
         base::BindOnce(&AudioInputController::EventHandler::OnCreated,
-                       base::Unretained(event_handler),
-                       base::Unretained(this)));
+                       base::Unretained(event_handler), base::Unretained(this),
+                       false));
     ON_CALL(*this, EnableDebugRecording(_))
         .WillByDefault(SaveArg<0>(&file_name));
   }
@@ -334,7 +336,7 @@
       Open("Default device", media::AudioDeviceDescription::kDefaultDeviceId);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
@@ -360,7 +362,7 @@
   int session_id = Open("Nondefault device", GetRawNondefaultId());
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
@@ -376,7 +378,7 @@
       Open("Default device", media::AudioDeviceDescription::kDefaultDeviceId);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
@@ -399,7 +401,7 @@
       Open("Default device", media::AudioDeviceDescription::kDefaultDeviceId);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
@@ -424,7 +426,7 @@
       Open("Default device", media::AudioDeviceDescription::kDefaultDeviceId);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
@@ -445,7 +447,7 @@
       Open("Default device", media::AudioDeviceDescription::kDefaultDeviceId);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
@@ -466,7 +468,7 @@
       Open("Default device", media::AudioDeviceDescription::kDefaultDeviceId);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(renderer_, NotifyStreamError(kStreamId));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
@@ -487,9 +489,9 @@
       Open("Default device", media::AudioDeviceDescription::kDefaultDeviceId);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
-  EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId + 1, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
+  EXPECT_CALL(renderer_, NotifyStreamCreated(kStreamId + 1, _, _, _,
+                                             kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated()).Times(2);
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
@@ -524,7 +526,7 @@
       Open("Default device", media::AudioDeviceDescription::kDefaultDeviceId);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
@@ -556,7 +558,7 @@
   int session_id = Open("Tab capture", controls.audio.device_id);
 
   EXPECT_CALL(renderer_,
-              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount));
+              NotifyStreamCreated(kStreamId, _, _, _, kSharedMemoryCount, _));
   EXPECT_CALL(controller_factory_, ControllerCreated());
 
   airh_->OnMessageReceived(AudioInputHostMsg_CreateStream(
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index fda6b6a..9b484b6 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -5849,8 +5849,8 @@
 
 }  // namespace anonymous
 
-// Flaky under TSan. https://crbug.com/592320
-#if defined(THREAD_SANITIZER)
+// Flaky under Linux and Tsan. https://crbug.com/592320
+#if defined(THREAD_SANITIZER) || defined(OS_LINUX)
 #define MAYBE_SubframeGestureEventRouting DISABLED_SubframeGestureEventRouting
 #else
 #define MAYBE_SubframeGestureEventRouting SubframeGestureEventRouting
diff --git a/content/browser/speech/speech_recognizer_impl.h b/content/browser/speech/speech_recognizer_impl.h
index 4c30dad..711b501 100644
--- a/content/browser/speech/speech_recognizer_impl.h
+++ b/content/browser/speech/speech_recognizer_impl.h
@@ -143,7 +143,8 @@
   void OnAudioClosed(media::AudioInputController*);
 
   // AudioInputController::EventHandler methods.
-  void OnCreated(media::AudioInputController* controller) override {}
+  void OnCreated(media::AudioInputController* controller,
+                 bool initially_muted) override {}
   void OnError(media::AudioInputController* controller,
                media::AudioInputController::ErrorCode error_code) override;
   void OnLog(media::AudioInputController* controller,
diff --git a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
index b1d00eb..ef7b8bd 100644
--- a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
+++ b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
@@ -106,8 +106,9 @@
 class WebRtcGetUserMediaBrowserTest : public WebRtcContentBrowserTestBase {
  public:
   WebRtcGetUserMediaBrowserTest() : trace_log_(NULL) {
-    scoped_feature_list_.InitAndDisableFeature(
-        features::kMediaStreamOldVideoConstraints);
+    scoped_feature_list_.InitWithFeatures(
+        {}, {features::kMediaStreamOldVideoConstraints,
+             features::kMediaStreamOldAudioConstraints});
     // Automatically grant device permission.
     AppendUseFakeUIForMediaStreamFlag();
   }
@@ -429,14 +430,12 @@
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
 
   // Test with invalid mandatory audio sourceID.
-  // TODO(guidou): Update error string when spec-compliant constraint resolution
-  // for audio is implemented. See http://crbug.com/657733.
   NavigateToURL(shell(), url);
-  EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
-      GenerateGetUserMediaWithMandatorySourceID(
-          kGetUserMediaAndExpectFailure,
-          "something invalid",
-          video_ids[0])));
+  EXPECT_EQ("ConstraintNotSatisfiedError",
+            ExecuteJavascriptAndReturnResult(
+                GenerateGetUserMediaWithMandatorySourceID(
+                    kGetUserMediaAndExpectFailure, "something invalid",
+                    video_ids[0])));
 
   // Test with invalid mandatory video sourceID.
   EXPECT_EQ("ConstraintNotSatisfiedError",
@@ -446,13 +445,10 @@
                     "something invalid")));
 
   // Test with empty mandatory audio sourceID.
-  // TODO(guidou): Update error string when spec-compliant constraint resolution
-  // for audio is implemented. See http://crbug.com/657733.
-  EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
-      GenerateGetUserMediaWithMandatorySourceID(
-          kGetUserMediaAndExpectFailure,
-          "",
-          video_ids[0])));
+  EXPECT_EQ("ConstraintNotSatisfiedError",
+            ExecuteJavascriptAndReturnResult(
+                GenerateGetUserMediaWithMandatorySourceID(
+                    kGetUserMediaAndExpectFailure, "", video_ids[0])));
 }
 
 IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest,
@@ -807,8 +803,10 @@
     : public WebRtcContentBrowserTestBase {
  public:
   WebRtcGetUserMediaOldConstraintsBrowserTest() : trace_log_(NULL) {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kMediaStreamOldVideoConstraints);
+    scoped_feature_list_.InitWithFeatures(
+        {features::kMediaStreamOldVideoConstraints,
+         features::kMediaStreamOldAudioConstraints},
+        {});
     // Automatically grant device permission.
     AppendUseFakeUIForMediaStreamFlag();
   }
@@ -1121,8 +1119,6 @@
   GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
 
   // Test with invalid mandatory audio sourceID.
-  // TODO(guidou): Update error string when spec-compliant constraint resolution
-  // for audio is implemented. See http://crbug.com/657733.
   NavigateToURL(shell(), url);
   EXPECT_EQ("DevicesNotFoundError",
             ExecuteJavascriptAndReturnResult(
@@ -1138,8 +1134,6 @@
                     "something invalid")));
 
   // Test with empty mandatory audio sourceID.
-  // TODO(guidou): Update error string when spec-compliant constraint resolution
-  // for audio is implemented. See http://crbug.com/657733.
   EXPECT_EQ("DevicesNotFoundError",
             ExecuteJavascriptAndReturnResult(
                 GenerateGetUserMediaWithMandatorySourceID(
diff --git a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
index f016341..3d815a27 100644
--- a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
+++ b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
@@ -122,7 +122,14 @@
                   kMediaRecorderHtmlFile);
 }
 
-IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest, IllegalPauseThrowsDOMError) {
+// TODO (crbug.com/736268): Flaky on Linux TSan bots.
+#if defined(OS_LINUX)
+#define MAYBE_IllegalPauseThrowsDOMError DISABLED_IllegalPauseThrowsDOMError
+#else
+#define MAYBE_IllegalPauseThrowsDOMError IllegalPauseThrowsDOMError
+#endif
+IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
+                       MAYBE_IllegalPauseThrowsDOMError) {
   MakeTypicalCall("testIllegalPauseThrowsDOMError();", kMediaRecorderHtmlFile);
 }
 
@@ -153,8 +160,15 @@
                   kMediaRecorderHtmlFile);
 }
 
+// Flaky on Linux Tsan (crbug.com/736268)
+#if defined(OS_LINUX)
+#define MAYBE_IllegalRequestDataThrowsDOMError \
+  DISABLED_IllegalRequestDataThrowsDOMError
+#else
+#define MAYBE_IllegalRequestDataThrowsDOMError IllegalRequestDataThrowsDOMError
+#endif
 IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
-                       IllegalRequestDataThrowsDOMError) {
+                       MAYBE_IllegalRequestDataThrowsDOMError) {
   MakeTypicalCall("testIllegalRequestDataThrowsDOMError();",
                   kMediaRecorderHtmlFile);
 }
@@ -180,8 +194,16 @@
                   kMediaRecorderHtmlFile);
 }
 
+// Flaky on Linux Tsan (crbug.com/736268)
+#if defined(OS_LINUX)
+#define MAYBE_AddingTrackToMediaStreamFiresErrorEvent \
+  DISABLED_AddingTrackToMediaStreamFiresErrorEvent
+#else
+#define MAYBE_AddingTrackToMediaStreamFiresErrorEvent \
+  AddingTrackToMediaStreamFiresErrorEvent
+#endif
 IN_PROC_BROWSER_TEST_F(WebRtcMediaRecorderTest,
-                       AddingTrackToMediaStreamFiresErrorEvent) {
+                       MAYBE_AddingTrackToMediaStreamFiresErrorEvent) {
   MakeTypicalCall("testAddingTrackToMediaStreamFiresErrorEvent();",
                   kMediaRecorderHtmlFile);
 }
diff --git a/content/child/web_url_loader_impl.cc b/content/child/web_url_loader_impl.cc
index 1e4ab807..1b7c0e6 100644
--- a/content/child/web_url_loader_impl.cc
+++ b/content/child/web_url_loader_impl.cc
@@ -1074,7 +1074,7 @@
                                            WebURLResponse* response,
                                            bool report_security_info) {
   response->SetURL(url);
-  response->SetResponseTime(info.response_time.ToInternalValue());
+  response->SetResponseTime(info.response_time);
   response->SetMIMEType(WebString::FromUTF8(info.mime_type));
   response->SetTextEncodingName(WebString::FromUTF8(info.charset));
   response->SetExpectedContentLength(info.content_length);
diff --git a/content/common/media/audio_messages.h b/content/common/media/audio_messages.h
index 1fed287..f918660 100644
--- a/content/common/media/audio_messages.h
+++ b/content/common/media/audio_messages.h
@@ -54,15 +54,16 @@
     uint32_t /* length */)
 
 // Tell the renderer process that an audio input stream has been created.
-// The renderer process would be given a SyncSocket that it should read
-// from from then on. It is also given number of segments in shared memory.
-IPC_MESSAGE_CONTROL5(
-    AudioInputMsg_NotifyStreamCreated,
-    int /* stream id */,
-    base::SharedMemoryHandle /* handle */,
-    base::SyncSocket::TransitDescriptor /* socket descriptor */,
-    uint32_t /* length */,
-    uint32_t /* segment count */)
+// The renderer process would be given a SyncSocket that it should read from
+// from then on. It is also given number of segments in shared memory and
+// whether the stream initially is muted.
+IPC_MESSAGE_CONTROL(AudioInputMsg_NotifyStreamCreated,
+                    int /* stream id */,
+                    base::SharedMemoryHandle /* handle */,
+                    base::SyncSocket::TransitDescriptor /* socket descriptor */,
+                    uint32_t /* length */,
+                    uint32_t /* segment count */,
+                    bool /* initially muted */)
 
 // Notification message sent from AudioRendererHost to renderer for state
 // update on error.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index db54f942..ba68c9030 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -136,6 +136,10 @@
     base::FEATURE_DISABLED_BY_DEFAULT
 };
 
+// Enables the old algorithm for processing audio constraints in getUserMedia().
+const base::Feature kMediaStreamOldAudioConstraints{
+    "MediaStreamOldAudioConstraints", base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Enables the old algorithm for processing video constraints in getUserMedia().
 const base::Feature kMediaStreamOldVideoConstraints{
     "MediaStreamOldVideoConstraints", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 631f2be2..e06b6dc 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -43,6 +43,7 @@
 CONTENT_EXPORT extern const base::Feature kLoadingWithMojo;
 CONTENT_EXPORT extern const base::Feature kLocationHardReload;
 CONTENT_EXPORT extern const base::Feature kMediaDocumentDownloadButton;
+CONTENT_EXPORT extern const base::Feature kMediaStreamOldAudioConstraints;
 CONTENT_EXPORT extern const base::Feature kMediaStreamOldVideoConstraints;
 CONTENT_EXPORT extern const base::Feature kMemoryCoordinator;
 CONTENT_EXPORT extern const base::Feature kNotificationContentImage;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index cc4afef5..3ad0810 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -682,6 +682,8 @@
       "media/webrtc/webrtc_audio_sink.h",
       "media/webrtc/webrtc_media_stream_adapter.cc",
       "media/webrtc/webrtc_media_stream_adapter.h",
+      "media/webrtc/webrtc_media_stream_adapter_map.cc",
+      "media/webrtc/webrtc_media_stream_adapter_map.h",
       "media/webrtc/webrtc_media_stream_track_adapter.cc",
       "media/webrtc/webrtc_media_stream_track_adapter.h",
       "media/webrtc/webrtc_media_stream_track_adapter_map.cc",
diff --git a/content/renderer/cache_storage/cache_storage_dispatcher.cc b/content/renderer/cache_storage/cache_storage_dispatcher.cc
index 51b09d23..f1ba2dc8 100644
--- a/content/renderer/cache_storage/cache_storage_dispatcher.cc
+++ b/content/renderer/cache_storage/cache_storage_dispatcher.cc
@@ -630,7 +630,7 @@
   web_response->SetStatus(response.status_code);
   web_response->SetStatusText(WebString::FromASCII(response.status_text));
   web_response->SetResponseType(response.response_type);
-  web_response->SetResponseTime(response.response_time.ToInternalValue());
+  web_response->SetResponseTime(response.response_time);
   web_response->SetCacheStorageCacheName(
       response.is_in_cache_storage
           ? blink::WebString::FromUTF8(response.cache_storage_cache_name)
diff --git a/content/renderer/media/audio_input_message_filter.cc b/content/renderer/media/audio_input_message_filter.cc
index 145215b..9c69e3e 100644
--- a/content/renderer/media/audio_input_message_filter.cc
+++ b/content/renderer/media/audio_input_message_filter.cc
@@ -128,7 +128,8 @@
     base::SharedMemoryHandle handle,
     base::SyncSocket::TransitDescriptor socket_descriptor,
     uint32_t length,
-    uint32_t total_segments) {
+    uint32_t total_segments,
+    bool initially_muted) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
   LogMessage(stream_id, "OnStreamCreated");
 
@@ -143,7 +144,8 @@
     return;
   }
   // Forward message to the stream delegate.
-  delegate->OnStreamCreated(handle, socket_handle, length, total_segments);
+  delegate->OnStreamCreated(handle, socket_handle, length, total_segments,
+                            initially_muted);
 }
 
 void AudioInputMessageFilter::OnStreamError(int stream_id) {
diff --git a/content/renderer/media/audio_input_message_filter.h b/content/renderer/media/audio_input_message_filter.h
index 60952c9..a015af7b 100644
--- a/content/renderer/media/audio_input_message_filter.h
+++ b/content/renderer/media/audio_input_message_filter.h
@@ -73,7 +73,8 @@
                        base::FileDescriptor socket_descriptor,
 #endif
                        uint32_t length,
-                       uint32_t total_segments);
+                       uint32_t total_segments,
+                       bool initially_muted);
 
   // Received when internal state of browser process' audio input stream has
   // encountered an error.
diff --git a/content/renderer/media/media_stream_audio_processor.cc b/content/renderer/media/media_stream_audio_processor.cc
index d98b594..360b40d1 100644
--- a/content/renderer/media/media_stream_audio_processor.cc
+++ b/content/renderer/media/media_stream_audio_processor.cc
@@ -6,7 +6,11 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <algorithm>
+#include <limits>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/command_line.h"
 #include "base/feature_list.h"
@@ -20,13 +24,11 @@
 #include "build/build_config.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/renderer/media/media_stream_audio_processor_options.h"
 #include "content/renderer/media/webrtc_audio_device_impl.h"
 #include "media/base/audio_converter.h"
 #include "media/base/audio_fifo.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
-#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
 #include "third_party/webrtc/api/mediaconstraintsinterface.h"
 #include "third_party/webrtc/modules/audio_processing/typing_detection.h"
 
@@ -128,6 +130,19 @@
       switches::kAecRefinedAdaptiveFilter);
 }
 
+webrtc::Point WebrtcPointFromMediaPoint(const media::Point& point) {
+  return webrtc::Point(point.x(), point.y(), point.z());
+}
+
+std::vector<webrtc::Point> WebrtcPointsFromMediaPoints(
+    const std::vector<media::Point>& points) {
+  std::vector<webrtc::Point> webrtc_points;
+  webrtc_points.reserve(webrtc_points.size());
+  for (const auto& point : points)
+    webrtc_points.push_back(WebrtcPointFromMediaPoint(point));
+  return webrtc_points;
+}
+
 }  // namespace
 
 // Wraps AudioBus to provide access to the array of channel pointers, since this
@@ -292,8 +307,7 @@
 };
 
 MediaStreamAudioProcessor::MediaStreamAudioProcessor(
-    const blink::WebMediaConstraints& constraints,
-    const MediaStreamDevice::AudioDeviceParameters& input_params,
+    const AudioProcessingProperties& properties,
     WebRtcPlayoutDataSource* playout_data_source)
     : render_delay_ms_(0),
       has_echo_cancellation_(false),
@@ -305,7 +319,7 @@
   DCHECK(main_thread_runner_);
   capture_thread_checker_.DetachFromThread();
   render_thread_checker_.DetachFromThread();
-  InitializeAudioProcessingModule(constraints, input_params);
+  InitializeAudioProcessingModule(properties);
 
   aec_dump_message_filter_ = AecDumpMessageFilter::Get();
   // In unit tests not creating a message filter, |aec_dump_message_filter_|
@@ -491,34 +505,30 @@
 
 // static
 bool MediaStreamAudioProcessor::WouldModifyAudio(
-    const blink::WebMediaConstraints& constraints,
-    int effects_flags) {
+    const AudioProcessingProperties& properties) {
   // Note: This method should by kept in-sync with any changes to the logic in
   // MediaStreamAudioProcessor::InitializeAudioProcessingModule().
 
-  const MediaAudioConstraints audio_constraints(constraints, effects_flags);
-
-  if (audio_constraints.GetGoogAudioMirroring())
+  if (properties.goog_audio_mirroring)
     return true;
 
 #if !defined(OS_IOS)
-  if (audio_constraints.GetEchoCancellationProperty() ||
-      audio_constraints.GetGoogAutoGainControl()) {
+  if (properties.enable_sw_echo_cancellation ||
+      properties.goog_auto_gain_control) {
     return true;
   }
 #endif
 
 #if !defined(OS_IOS) && !defined(OS_ANDROID)
-  if (audio_constraints.GetGoogExperimentalEchoCancellation() ||
-      audio_constraints.GetGoogTypingNoiseDetection()) {
+  if (properties.goog_experimental_echo_cancellation ||
+      properties.goog_typing_noise_detection) {
     return true;
   }
 #endif
 
-  if (audio_constraints.GetGoogNoiseSuppression() ||
-      audio_constraints.GetGoogExperimentalNoiseSuppression() ||
-      audio_constraints.GetGoogBeamforming() ||
-      audio_constraints.GetGoogHighpassFilter()) {
+  if (properties.goog_noise_suppression ||
+      properties.goog_experimental_noise_suppression ||
+      properties.goog_beamforming || properties.goog_highpass_filter) {
     return true;
   }
 
@@ -578,78 +588,63 @@
 }
 
 void MediaStreamAudioProcessor::InitializeAudioProcessingModule(
-    const blink::WebMediaConstraints& constraints,
-    const MediaStreamDevice::AudioDeviceParameters& input_params) {
+    const AudioProcessingProperties& properties) {
   DCHECK(main_thread_runner_->BelongsToCurrentThread());
   DCHECK(!audio_processing_);
 
-  MediaAudioConstraints audio_constraints(constraints, input_params.effects);
-
   // Note: The audio mirroring constraint (i.e., swap left and right channels)
   // is handled within this MediaStreamAudioProcessor and does not, by itself,
   // require webrtc::AudioProcessing.
-  audio_mirroring_ = audio_constraints.GetGoogAudioMirroring();
-
-  const bool echo_cancellation =
-      audio_constraints.GetEchoCancellationProperty();
-  has_echo_cancellation_ = echo_cancellation;
-  const bool goog_agc = audio_constraints.GetGoogAutoGainControl();
+  audio_mirroring_ = properties.goog_audio_mirroring;
+  has_echo_cancellation_ = properties.enable_sw_echo_cancellation;
 
 #if defined(OS_ANDROID)
   const bool goog_experimental_aec = false;
   const bool goog_typing_detection = false;
 #else
   const bool goog_experimental_aec =
-      audio_constraints.GetGoogExperimentalEchoCancellation();
-  const bool goog_typing_detection =
-      audio_constraints.GetGoogTypingNoiseDetection();
+      properties.goog_experimental_echo_cancellation;
+  const bool goog_typing_detection = properties.goog_typing_noise_detection;
 #endif
 
-  const bool goog_ns = audio_constraints.GetGoogNoiseSuppression();
-  const bool goog_experimental_ns =
-      audio_constraints.GetGoogExperimentalNoiseSuppression();
-  const bool goog_beamforming = audio_constraints.GetGoogBeamforming();
-  const bool goog_high_pass_filter = audio_constraints.GetGoogHighpassFilter();
-
   // Return immediately if none of the goog constraints requiring
   // webrtc::AudioProcessing are enabled.
-  if (!echo_cancellation && !goog_experimental_aec && !goog_ns &&
-      !goog_high_pass_filter && !goog_typing_detection &&
-      !goog_agc && !goog_experimental_ns && !goog_beamforming) {
+  if (!properties.enable_sw_echo_cancellation && !goog_experimental_aec &&
+      !properties.goog_noise_suppression && !properties.goog_highpass_filter &&
+      !goog_typing_detection && !properties.goog_auto_gain_control &&
+      !properties.goog_experimental_noise_suppression &&
+      !properties.goog_beamforming) {
     // Sanity-check: WouldModifyAudio() should return true iff
     // |audio_mirroring_| is true.
-    DCHECK_EQ(audio_mirroring_, WouldModifyAudio(constraints,
-                                                 input_params.effects));
+    DCHECK_EQ(audio_mirroring_, WouldModifyAudio(properties));
     RecordProcessingState(AUDIO_PROCESSING_DISABLED);
     return;
   }
 
   // Sanity-check: WouldModifyAudio() should return true because the above logic
   // has determined webrtc::AudioProcessing will be used.
-  DCHECK(WouldModifyAudio(constraints, input_params.effects));
+  DCHECK(WouldModifyAudio(properties));
 
   // Experimental options provided at creation.
   webrtc::Config config;
   config.Set<webrtc::ExtendedFilter>(
       new webrtc::ExtendedFilter(goog_experimental_aec));
-  config.Set<webrtc::ExperimentalNs>(
-      new webrtc::ExperimentalNs(goog_experimental_ns));
+  config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(
+      properties.goog_experimental_noise_suppression));
   config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(true));
   if (UseAecRefinedAdaptiveFilter()) {
     config.Set<webrtc::RefinedAdaptiveFilter>(
         new webrtc::RefinedAdaptiveFilter(true));
   }
-  if (goog_beamforming) {
-    const auto& geometry =
-        GetArrayGeometryPreferringConstraints(audio_constraints, input_params);
-
+  if (properties.goog_beamforming) {
     // Only enable beamforming if we have at least two mics.
-    config.Set<webrtc::Beamforming>(
-        new webrtc::Beamforming(geometry.size() > 1, geometry));
+    config.Set<webrtc::Beamforming>(new webrtc::Beamforming(
+        properties.goog_array_geometry.size() > 1,
+        WebrtcPointsFromMediaPoints(properties.goog_array_geometry)));
   }
 
   // If the experimental AGC is enabled, check for overridden config params.
-  if (audio_constraints.GetGoogExperimentalAutoGainControl()) {
+  if (properties.goog_experimental_auto_gain_control) {
     auto startup_min_volume = GetStartupMinVolumeForAgc();
     constexpr int kClippingLevelMin = 70;
     // TODO(hlundin) Make this value default in WebRTC and clean up here.
@@ -667,7 +662,7 @@
     playout_data_source_->AddPlayoutSink(this);
   }
 
-  if (echo_cancellation) {
+  if (properties.enable_sw_echo_cancellation) {
     EnableEchoCancellation(audio_processing_.get());
 
     apm_config.echo_canceller3.enabled = override_aec3_.value_or(
@@ -686,7 +681,7 @@
     apm_config.echo_canceller3.enabled = false;
   }
 
-  if (goog_ns) {
+  if (properties.goog_noise_suppression) {
     // The beamforming postfilter is effective at suppressing stationary noise,
     // so reduce the single-channel NS aggressiveness when enabled.
     const NoiseSuppression::Level ns_level =
@@ -696,7 +691,7 @@
     EnableNoiseSuppression(audio_processing_.get(), ns_level);
   }
 
-  apm_config.high_pass_filter.enabled = goog_high_pass_filter;
+  apm_config.high_pass_filter.enabled = properties.goog_highpass_filter;
 
   if (goog_typing_detection) {
     // TODO(xians): Remove this |typing_detector_| after the typing suppression
@@ -705,7 +700,7 @@
     EnableTypingDetection(audio_processing_.get(), typing_detector_.get());
   }
 
-  if (goog_agc)
+  if (properties.goog_auto_gain_control)
     EnableAutomaticGainControl(audio_processing_.get());
 
   audio_processing_->ApplyConfig(apm_config);
diff --git a/content/renderer/media/media_stream_audio_processor.h b/content/renderer/media/media_stream_audio_processor.h
index fa4c8ff..d001734 100644
--- a/content/renderer/media/media_stream_audio_processor.h
+++ b/content/renderer/media/media_stream_audio_processor.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_RENDERER_MEDIA_MEDIA_STREAM_AUDIO_PROCESSOR_H_
 #define CONTENT_RENDERER_MEDIA_MEDIA_STREAM_AUDIO_PROCESSOR_H_
 
+#include <memory>
+
 #include "base/atomicops.h"
 #include "base/files/file.h"
 #include "base/gtest_prod_util.h"
@@ -19,6 +21,7 @@
 #include "content/public/common/media_stream_request.h"
 #include "content/renderer/media/aec_dump_message_filter.h"
 #include "content/renderer/media/audio_repetition_detector.h"
+#include "content/renderer/media/media_stream_audio_processor_options.h"
 #include "content/renderer/media/webrtc_audio_device_impl.h"
 #include "media/base/audio_converter.h"
 #include "third_party/webrtc/api/mediastreaminterface.h"
@@ -34,10 +37,6 @@
 #endif
 #endif
 
-namespace blink {
-class WebMediaConstraints;
-}
-
 namespace media {
 class AudioBus;
 class AudioParameters;
@@ -70,10 +69,8 @@
   //
   // Threading note: The constructor assumes it is being run on the main render
   // thread.
-  MediaStreamAudioProcessor(
-      const blink::WebMediaConstraints& constraints,
-      const MediaStreamDevice::AudioDeviceParameters& input_params,
-      WebRtcPlayoutDataSource* playout_data_source);
+  MediaStreamAudioProcessor(const AudioProcessingProperties& properties,
+                            WebRtcPlayoutDataSource* playout_data_source);
 
   // Called when the format of the capture data has changed.
   // Called on the main render thread. The caller is responsible for stopping
@@ -126,15 +123,10 @@
   void OnIpcClosing() override;
 
   // Returns true if MediaStreamAudioProcessor would modify the audio signal,
-  // based on the |constraints| and |effects_flags| parsed from a user media
-  // request. If the audio signal would not be modified, there is no need to
-  // instantiate a MediaStreamAudioProcessor and feed audio through it. Doing so
-  // would waste a non-trivial amount of memory and CPU resources.
-  //
-  // See media::AudioParameters::PlatformEffectsMask for interpretation of
-  // |effects_flags|.
-  static bool WouldModifyAudio(const blink::WebMediaConstraints& constraints,
-                               int effects_flags);
+  // based on |properties|. If the audio signal would not be modified, there is
+  // no need to instantiate a MediaStreamAudioProcessor and feed audio through
+  // it. Doing so would waste a non-trivial amount of memory and CPU resources.
+  static bool WouldModifyAudio(const AudioProcessingProperties& properties);
 
  protected:
   ~MediaStreamAudioProcessor() override;
@@ -158,8 +150,7 @@
 
   // Helper to initialize the WebRtc AudioProcessing.
   void InitializeAudioProcessingModule(
-      const blink::WebMediaConstraints& constraints,
-      const MediaStreamDevice::AudioDeviceParameters& input_params);
+      const AudioProcessingProperties& properties);
 
   // Helper to initialize the capture converter.
   void InitializeCaptureFifo(const media::AudioParameters& input_format);
diff --git a/content/renderer/media/media_stream_audio_processor_options.cc b/content/renderer/media/media_stream_audio_processor_options.cc
index 401350e..2da0996 100644
--- a/content/renderer/media/media_stream_audio_processor_options.cc
+++ b/content/renderer/media/media_stream_audio_processor_options.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -18,6 +19,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "content/common/media/media_stream_options.h"
+#include "content/public/common/content_features.h"
 #include "content/renderer/media/media_stream_constraints_util.h"
 #include "content/renderer/media/media_stream_source.h"
 #include "media/base/audio_parameters.h"
@@ -107,19 +109,6 @@
     return DELAY_BASED_ECHO_QUALITY_BAD;
 }
 
-webrtc::Point WebrtcPointFromMediaPoint(const media::Point& point) {
-  return webrtc::Point(point.x(), point.y(), point.z());
-}
-
-std::vector<webrtc::Point> WebrtcPointsFromMediaPoints(
-    const std::vector<media::Point>& points) {
-  std::vector<webrtc::Point> webrtc_points;
-  webrtc_points.reserve(webrtc_points.size());
-  for (const auto& point : points)
-    webrtc_points.push_back(WebrtcPointFromMediaPoint(point));
-  return webrtc_points;
-}
-
 // Scan the basic and advanced constraints until a value is found.
 // If nothing is found, the default is returned.
 // Argument 2 is a pointer to class data member.
@@ -141,6 +130,7 @@
     : constraints_(constraints),
       effects_(effects),
       default_audio_processing_constraint_value_(true) {
+  DCHECK(IsOldAudioConstraints());
   // The default audio processing constraints are turned off when
   // - gUM has a specific kMediaStreamSource, which is used by tab capture
   //   and screen capture.
@@ -286,6 +276,50 @@
     AudioProcessingProperties&& other) = default;
 AudioProcessingProperties::~AudioProcessingProperties() = default;
 
+void AudioProcessingProperties::DisableDefaultPropertiesForTesting() {
+  enable_sw_echo_cancellation = false;
+  goog_auto_gain_control = false;
+  goog_experimental_echo_cancellation = false;
+  goog_typing_noise_detection = false;
+  goog_noise_suppression = false;
+  goog_experimental_noise_suppression = false;
+  goog_beamforming = false;
+  goog_highpass_filter = false;
+  goog_experimental_auto_gain_control = false;
+}
+
+// static
+AudioProcessingProperties AudioProcessingProperties::FromConstraints(
+    const blink::WebMediaConstraints& constraints,
+    const MediaStreamDevice::AudioDeviceParameters& input_params) {
+  DCHECK(IsOldAudioConstraints());
+  MediaAudioConstraints audio_constraints(constraints, input_params.effects);
+  AudioProcessingProperties properties;
+  properties.enable_sw_echo_cancellation =
+      audio_constraints.GetEchoCancellationProperty();
+  // |properties.disable_hw_echo_cancellation| is not used when
+  // IsOldAudioConstraints() is true.
+  properties.goog_audio_mirroring = audio_constraints.GetGoogAudioMirroring();
+  properties.goog_auto_gain_control =
+      audio_constraints.GetGoogAutoGainControl();
+  properties.goog_experimental_echo_cancellation =
+      audio_constraints.GetGoogExperimentalEchoCancellation();
+  properties.goog_typing_noise_detection =
+      audio_constraints.GetGoogTypingNoiseDetection();
+  properties.goog_noise_suppression =
+      audio_constraints.GetGoogNoiseSuppression();
+  properties.goog_experimental_noise_suppression =
+      audio_constraints.GetGoogExperimentalNoiseSuppression();
+  properties.goog_beamforming = audio_constraints.GetGoogBeamforming();
+  properties.goog_highpass_filter = audio_constraints.GetGoogHighpassFilter();
+  properties.goog_experimental_auto_gain_control =
+      audio_constraints.GetGoogExperimentalAutoGainControl();
+  properties.goog_array_geometry =
+      GetArrayGeometryPreferringConstraints(audio_constraints, input_params);
+
+  return properties;
+}
+
 EchoInformation::EchoInformation()
     : delay_stats_time_ms_(0),
       echo_frames_received_(false),
@@ -482,7 +516,7 @@
       apm_stats.residual_echo_likelihood_recent_max;
 }
 
-std::vector<webrtc::Point> GetArrayGeometryPreferringConstraints(
+std::vector<media::Point> GetArrayGeometryPreferringConstraints(
     const MediaAudioConstraints& audio_constraints,
     const MediaStreamDevice::AudioDeviceParameters& input_params) {
   const std::string constraints_geometry =
@@ -490,10 +524,14 @@
 
   // Give preference to the audio constraint over the device-supplied mic
   // positions. This is mainly for testing purposes.
-  return WebrtcPointsFromMediaPoints(
-      constraints_geometry.empty()
-          ? input_params.mic_positions
-          : media::ParsePointsFromString(constraints_geometry));
+  return constraints_geometry.empty()
+             ? input_params.mic_positions
+             : media::ParsePointsFromString(constraints_geometry);
+}
+
+bool IsOldAudioConstraints() {
+  return base::FeatureList::IsEnabled(
+      features::kMediaStreamOldAudioConstraints);
 }
 
 }  // namespace content
diff --git a/content/renderer/media/media_stream_audio_processor_options.h b/content/renderer/media/media_stream_audio_processor_options.h
index c891c8c..153a26c5 100644
--- a/content/renderer/media/media_stream_audio_processor_options.h
+++ b/content/renderer/media/media_stream_audio_processor_options.h
@@ -110,6 +110,14 @@
   AudioProcessingProperties& operator=(AudioProcessingProperties&& other);
   ~AudioProcessingProperties();
 
+  // Disables properties that are enabled by default.
+  void DisableDefaultPropertiesForTesting();
+
+  // TODO(guidou): Remove this function. http://crbug.com/706408
+  static AudioProcessingProperties FromConstraints(
+      const blink::WebMediaConstraints& constraints,
+      const MediaStreamDevice::AudioDeviceParameters& input_params);
+
   bool enable_sw_echo_cancellation = true;
   bool disable_hw_echo_cancellation = false;
   bool goog_audio_mirroring = false;
@@ -197,10 +205,14 @@
 
 // Returns the array geometry from the media constraints if existing and
 // otherwise that provided by the input device.
-CONTENT_EXPORT std::vector<webrtc::Point> GetArrayGeometryPreferringConstraints(
+// TODO(guidou): Remove this function. http://crbug.com/706408
+CONTENT_EXPORT std::vector<media::Point> GetArrayGeometryPreferringConstraints(
     const MediaAudioConstraints& audio_constraints,
     const MediaStreamDevice::AudioDeviceParameters& input_params);
 
+// TODO(guidou): Remove this function. http://crbug.com/706408
+CONTENT_EXPORT bool IsOldAudioConstraints();
+
 }  // namespace content
 
 #endif  // CONTENT_RENDERER_MEDIA_MEDIA_STREAM_AUDIO_PROCESSOR_OPTIONS_H_
diff --git a/content/renderer/media/media_stream_audio_processor_unittest.cc b/content/renderer/media/media_stream_audio_processor_unittest.cc
index 3404ed5..c5a9f4b 100644
--- a/content/renderer/media/media_stream_audio_processor_unittest.cc
+++ b/content/renderer/media/media_stream_audio_processor_unittest.cc
@@ -14,10 +14,12 @@
 #include "base/memory/aligned_memory.h"
 #include "base/message_loop/message_loop.h"
 #include "base/path_service.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/common/media/media_stream_options.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/media_stream_request.h"
 #include "content/renderer/media/media_stream_audio_processor.h"
 #include "content/renderer/media/media_stream_audio_processor_options.h"
@@ -81,6 +83,12 @@
   MediaStreamAudioProcessorTest()
       : params_(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                 media::CHANNEL_LAYOUT_STEREO, 48000, 16, 512) {
+    // This file includes tests for MediaStreamAudioProcessor, but also for
+    // the old constraints algorithm. The MediaStreamAudioProcessor tests are
+    // insensitive to the constraints algorithm, but the constraints tests
+    // require that the old constraints algorithm be enabled.
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kMediaStreamOldAudioConstraints);
   }
 
  protected:
@@ -212,6 +220,9 @@
   base::MessageLoop main_thread_message_loop_;
   media::AudioParameters params_;
   MediaStreamDevice::AudioDeviceParameters input_device_params_;
+
+  // TODO(guidou): Remove this field. http://crbug.com/706408
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Test crashing with ASAN on Android. crbug.com/468762
@@ -221,13 +232,12 @@
 #define MAYBE_WithAudioProcessing WithAudioProcessing
 #endif
 TEST_F(MediaStreamAudioProcessorTest, MAYBE_WithAudioProcessing) {
-  MockConstraintFactory constraint_factory;
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new WebRtcAudioDeviceImpl());
+  AudioProcessingProperties properties;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          constraint_factory.CreateWebMediaConstraints(), input_device_params_,
-          webrtc_audio_device.get()));
+          properties, webrtc_audio_device.get()));
   EXPECT_TRUE(audio_processor->has_audio_processing());
   audio_processor->OnCaptureFormatChanged(params_);
   VerifyDefaultComponents(audio_processor.get());
@@ -242,52 +252,15 @@
   audio_processor->Stop();
 }
 
-TEST_F(MediaStreamAudioProcessorTest, VerifyTabCaptureWithoutAudioProcessing) {
-  scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
-      new WebRtcAudioDeviceImpl());
-  // Create MediaStreamAudioProcessor instance for kMediaStreamSourceTab source.
-  MockConstraintFactory tab_constraint_factory;
-  const std::string tab_string = kMediaStreamSourceTab;
-  tab_constraint_factory.basic().media_stream_source.SetExact(
-      blink::WebString::FromUTF8(tab_string));
-  scoped_refptr<MediaStreamAudioProcessor> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          tab_constraint_factory.CreateWebMediaConstraints(),
-          input_device_params_, webrtc_audio_device.get()));
-  EXPECT_FALSE(audio_processor->has_audio_processing());
-  audio_processor->OnCaptureFormatChanged(params_);
-
-  ProcessDataAndVerifyFormat(audio_processor.get(),
-                             params_.sample_rate(),
-                             params_.channels(),
-                             params_.sample_rate() / 100);
-
-  // Create MediaStreamAudioProcessor instance for kMediaStreamSourceSystem
-  // source.
-  MockConstraintFactory system_constraint_factory;
-  const std::string system_string = kMediaStreamSourceSystem;
-  system_constraint_factory.basic().media_stream_source.SetExact(
-      blink::WebString::FromUTF8(system_string));
-  audio_processor = new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-      system_constraint_factory.CreateWebMediaConstraints(),
-      input_device_params_, webrtc_audio_device.get());
-  EXPECT_FALSE(audio_processor->has_audio_processing());
-
-  // Stop |audio_processor| so that it removes itself from
-  // |webrtc_audio_device| and clears its pointer to it.
-  audio_processor->Stop();
-}
-
 TEST_F(MediaStreamAudioProcessorTest, TurnOffDefaultConstraints) {
+  AudioProcessingProperties properties;
   // Turn off the default constraints and pass it to MediaStreamAudioProcessor.
-  MockConstraintFactory constraint_factory;
-  constraint_factory.DisableDefaultAudioConstraints();
+  properties.DisableDefaultPropertiesForTesting();
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new WebRtcAudioDeviceImpl());
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          constraint_factory.CreateWebMediaConstraints(), input_device_params_,
-          webrtc_audio_device.get()));
+          properties, webrtc_audio_device.get()));
   EXPECT_FALSE(audio_processor->has_audio_processing());
   audio_processor->OnCaptureFormatChanged(params_);
 
@@ -301,6 +274,7 @@
   audio_processor->Stop();
 }
 
+// TODO(guidou): Remove this test. http://crbug.com/706408
 TEST_F(MediaStreamAudioProcessorTest, VerifyConstraints) {
   {
     // Verify that echo cancellation is off when platform aec effect is on.
@@ -340,6 +314,7 @@
   }
 }
 
+// TODO(guidou): Remove this test. http://crbug.com/706408
 TEST_F(MediaStreamAudioProcessorTest, ValidateBadConstraints) {
   MockConstraintFactory constraint_factory;
   // Add a constraint that is not valid for audio.
@@ -349,6 +324,7 @@
   EXPECT_FALSE(audio_constraints.IsValid());
 }
 
+// TODO(guidou): Remove this test. http://crbug.com/706408
 TEST_F(MediaStreamAudioProcessorTest, ValidateGoodConstraints) {
   MockConstraintFactory constraint_factory;
   // Check that the renderToAssociatedSink constraint is considered valid.
@@ -358,6 +334,7 @@
   EXPECT_TRUE(audio_constraints.IsValid());
 }
 
+// TODO(guidou): Remove this test. http://crbug.com/706408
 TEST_F(MediaStreamAudioProcessorTest, NoEchoTurnsOffProcessing) {
   {
     MockConstraintFactory constraint_factory;
@@ -387,19 +364,20 @@
   }
 }
 
+// TODO(guidou): Remove this function. http://crbug.com/706408
 MediaAudioConstraints MakeMediaAudioConstraints(
     const MockConstraintFactory& constraint_factory) {
   return MediaAudioConstraints(constraint_factory.CreateWebMediaConstraints(),
                                AudioParameters::NO_EFFECTS);
 }
 
+// TODO(guidou): Remove this test. http://crbug.com/706408
 TEST_F(MediaStreamAudioProcessorTest, SelectsConstraintsArrayGeometryIfExists) {
-  std::vector<webrtc::Point> constraints_geometry(1,
-                                                  webrtc::Point(-0.02f, 0, 0));
-  constraints_geometry.push_back(webrtc::Point(0.02f, 0, 0));
+  std::vector<media::Point> constraints_geometry(1, media::Point(-0.02f, 0, 0));
+  constraints_geometry.push_back(media::Point(0.02f, 0, 0));
 
-  std::vector<webrtc::Point> input_device_geometry(1, webrtc::Point(0, 0, 0));
-  input_device_geometry.push_back(webrtc::Point(0, 0.05f, 0));
+  std::vector<media::Point> input_device_geometry(1, media::Point(0, 0, 0));
+  input_device_geometry.push_back(media::Point(0, 0.05f, 0));
 
   {
     // Both geometries empty.
@@ -408,7 +386,7 @@
 
     const auto& actual_geometry = GetArrayGeometryPreferringConstraints(
         MakeMediaAudioConstraints(constraint_factory), input_params);
-    EXPECT_EQ(std::vector<webrtc::Point>(), actual_geometry);
+    EXPECT_EQ(std::vector<media::Point>(), actual_geometry);
   }
   {
     // Constraints geometry empty.
@@ -455,13 +433,12 @@
 #define MAYBE_TestAllSampleRates TestAllSampleRates
 #endif
 TEST_F(MediaStreamAudioProcessorTest, MAYBE_TestAllSampleRates) {
-  MockConstraintFactory constraint_factory;
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new WebRtcAudioDeviceImpl());
+  AudioProcessingProperties properties;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          constraint_factory.CreateWebMediaConstraints(), input_device_params_,
-          webrtc_audio_device.get()));
+          properties, webrtc_audio_device.get()));
   EXPECT_TRUE(audio_processor->has_audio_processing());
 
   static const int kSupportedSampleRates[] =
@@ -495,13 +472,12 @@
       new AecDumpMessageFilter(base::ThreadTaskRunnerHandle::Get(),
                                base::ThreadTaskRunnerHandle::Get()));
 
-  MockConstraintFactory constraint_factory;
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new WebRtcAudioDeviceImpl());
+  AudioProcessingProperties properties;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          constraint_factory.CreateWebMediaConstraints(), input_device_params_,
-          webrtc_audio_device.get()));
+          properties, webrtc_audio_device.get()));
 
   EXPECT_TRUE(audio_processor->aec_dump_message_filter_.get());
 
@@ -511,17 +487,15 @@
 }
 
 TEST_F(MediaStreamAudioProcessorTest, TestStereoAudio) {
-  // Set up the correct constraints to turn off the audio processing and turn
-  // on the stereo channels mirroring.
-  MockConstraintFactory constraint_factory;
-  constraint_factory.basic().echo_cancellation.SetExact(false);
-  constraint_factory.basic().goog_audio_mirroring.SetExact(true);
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new WebRtcAudioDeviceImpl());
+  AudioProcessingProperties properties;
+  // Turn off the audio processing and turn on the stereo channels mirroring.
+  properties.DisableDefaultPropertiesForTesting();
+  properties.goog_audio_mirroring = true;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          constraint_factory.CreateWebMediaConstraints(), input_device_params_,
-          webrtc_audio_device.get()));
+          properties, webrtc_audio_device.get()));
   EXPECT_FALSE(audio_processor->has_audio_processing());
   const media::AudioParameters source_params(
       media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -577,14 +551,12 @@
 #define MAYBE_TestWithKeyboardMicChannel TestWithKeyboardMicChannel
 #endif
 TEST_F(MediaStreamAudioProcessorTest, MAYBE_TestWithKeyboardMicChannel) {
-  MockConstraintFactory constraint_factory;
-  constraint_factory.basic().goog_experimental_noise_suppression.SetExact(true);
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new WebRtcAudioDeviceImpl());
+  AudioProcessingProperties properties;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          constraint_factory.CreateWebMediaConstraints(), input_device_params_,
-          webrtc_audio_device.get()));
+          properties, webrtc_audio_device.get()));
   EXPECT_TRUE(audio_processor->has_audio_processing());
 
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -604,13 +576,12 @@
 
 // Test that the OnAec3Enable method has the desired effect on the APM config.
 TEST_F(MediaStreamAudioProcessorTest, TestAec3Switch) {
-  MockConstraintFactory constraint_factory;
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new WebRtcAudioDeviceImpl());
+  AudioProcessingProperties properties;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          constraint_factory.CreateWebMediaConstraints(), input_device_params_,
-          webrtc_audio_device.get()));
+          properties, webrtc_audio_device.get()));
 
   audio_processor->OnAec3Enable(true);
   EXPECT_TRUE(GetAec3ConfigState(audio_processor.get()));
@@ -626,15 +597,14 @@
 // Same test as above, but when AEC is disabled in the constrants. The expected
 // outcome is that AEC3 should be disabled in all cases.
 TEST_F(MediaStreamAudioProcessorTest, TestAec3Switch_AecOff) {
-  MockConstraintFactory constraint_factory;
-  // Disable the AEC.
-  constraint_factory.DisableAecAudioConstraints();
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new WebRtcAudioDeviceImpl());
+  AudioProcessingProperties properties;
+  // Disable the AEC.
+  properties.enable_sw_echo_cancellation = false;
   scoped_refptr<MediaStreamAudioProcessor> audio_processor(
       new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-          constraint_factory.CreateWebMediaConstraints(), input_device_params_,
-          webrtc_audio_device.get()));
+          properties, webrtc_audio_device.get()));
 
   EXPECT_FALSE(GetAec3ConfigState(audio_processor.get()));
 
diff --git a/content/renderer/media/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/rtc_peer_connection_handler_unittest.cc
index ecc1946..66d6be9 100644
--- a/content/renderer/media/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/rtc_peer_connection_handler_unittest.cc
@@ -301,7 +301,7 @@
                              media::AudioParameters::kAudioCDSampleRate,
                              media::CHANNEL_LAYOUT_STEREO,
                              media::AudioParameters::kAudioCDSampleRate / 100),
-            MockConstraintFactory().CreateWebMediaConstraints(),
+            AudioProcessingProperties(),
             base::Bind(&RTCPeerConnectionHandlerTest::OnAudioSourceStarted),
             mock_dependency_factory_.get());
     audio_source->SetAllowInvalidRenderFrameIdForTesting(true);
diff --git a/content/renderer/media/user_media_client_impl.cc b/content/renderer/media/user_media_client_impl.cc
index aa65b67..96ebd7bd 100644
--- a/content/renderer/media/user_media_client_impl.cc
+++ b/content/renderer/media/user_media_client_impl.cc
@@ -21,6 +21,7 @@
 #include "content/renderer/media/local_media_stream_audio_source.h"
 #include "content/renderer/media/media_stream.h"
 #include "content/renderer/media/media_stream_constraints_util.h"
+#include "content/renderer/media/media_stream_constraints_util_audio.h"
 #include "content/renderer/media/media_stream_constraints_util_video_content.h"
 #include "content/renderer/media/media_stream_constraints_util_video_device.h"
 #include "content/renderer/media/media_stream_dispatcher.h"
@@ -49,6 +50,7 @@
     *destination = constraint.Exact()[0].Utf8();
 }
 
+// TODO(guidou): Remove this function. http://crbug.com/706408
 bool FindDeviceId(const blink::WebVector<blink::WebString> candidates,
                   const MediaDeviceInfoArray& device_infos,
                   std::string* device_id) {
@@ -79,8 +81,7 @@
 // such device ID is copied to |*device_id| and the function returns true.
 // If no such device ID is found, |*device_id| is left unmodified and the
 // function returns true.
-// TODO(guidou): Replace with a spec-compliant selection algorithm. See
-// http://crbug.com/657733.
+// TODO(guidou): Remove this function. http://crbug.com/706408
 bool PickDeviceId(const blink::WebMediaConstraints& constraints,
                   const MediaDeviceInfoArray& device_infos,
                   std::string* device_id) {
@@ -123,8 +124,7 @@
   return source.empty();
 }
 
-// TODO(guidou): Remove once audio constraints are processed with spec-compliant
-// algorithm. See http://crbug.com/657733.
+// TODO(guidou): Remove this function. http://crbug.com/706408
 void CopyConstraintsToTrackControls(
     const blink::WebMediaConstraints& constraints,
     TrackControls* track_controls,
@@ -157,6 +157,7 @@
                   &track_controls->stream_source);
 }
 
+// TODO(guidou): Remove this function. http://crbug.com/706408
 void CopyHotwordAndLocalEchoToStreamControls(
     const blink::WebMediaConstraints& audio_constraints,
     StreamControls* controls) {
@@ -219,6 +220,18 @@
   }
 }
 
+bool IsValidAudioContentSource(const std::string& source) {
+  return source == kMediaStreamSourceTab ||
+         source == kMediaStreamSourceDesktop ||
+         source == kMediaStreamSourceSystem;
+}
+
+bool IsValidVideoContentSource(const std::string& source) {
+  return source == kMediaStreamSourceTab ||
+         source == kMediaStreamSourceDesktop ||
+         source == kMediaStreamSourceScreen;
+}
+
 static int g_next_request_id = 0;
 
 }  // namespace
@@ -265,11 +278,33 @@
   State state() const { return state_; }
   void set_state(State state) { state_ = state; }
 
-  bool enable_automatic_output_device_selection() const {
-    return enable_automatic_output_device_selection_;
+  // TODO(guidou): Remove this function. http://crbug.com/706408
+  bool legacy_enable_automatic_output_device_selection() const {
+    DCHECK(IsOldAudioConstraints());
+    return legacy_enable_automatic_output_device_selection_;
   }
-  void set_enable_automatic_output_device_selection(bool value) {
-    enable_automatic_output_device_selection_ = value;
+  // TODO(guidou): Remove this function. http://crbug.com/706408
+  void set_legacy_enable_automatic_output_device_selection(bool value) {
+    DCHECK(IsOldAudioConstraints());
+    legacy_enable_automatic_output_device_selection_ = value;
+  }
+  const AudioCaptureSettings& audio_capture_settings() const {
+    DCHECK(!IsOldAudioConstraints());
+    return audio_capture_settings_;
+  }
+  bool is_audio_content_capture() const {
+    DCHECK(!IsOldAudioConstraints());
+    return audio_capture_settings_.HasValue() && is_audio_content_capture_;
+  }
+  bool is_audio_device_capture() const {
+    DCHECK(!IsOldAudioConstraints());
+    return audio_capture_settings_.HasValue() && !is_audio_content_capture_;
+  }
+  void SetAudioCaptureSettings(const AudioCaptureSettings& settings,
+                               bool is_content_capture) {
+    DCHECK(settings.HasValue());
+    is_audio_content_capture_ = is_content_capture;
+    audio_capture_settings_ = settings;
   }
   const VideoCaptureSettings& video_capture_settings() const {
     return video_capture_settings_;
@@ -305,7 +340,10 @@
 
   const int request_id_;
   State state_;
-  bool enable_automatic_output_device_selection_;
+  // TODO(guidou): Remove this field. http://crbug.com/706408
+  bool legacy_enable_automatic_output_device_selection_;
+  AudioCaptureSettings audio_capture_settings_;
+  bool is_audio_content_capture_;
   VideoCaptureSettings video_capture_settings_;
   bool is_video_content_capture_;
   blink::WebMediaStream web_stream_;
@@ -390,46 +428,63 @@
   current_request_info_ = std::move(pending_request_infos_.front());
   pending_request_infos_.pop_front();
 
-  // TODO(guidou): Request audio and video capabilities in parallel.
+  // TODO(guidou): Set up audio and video in parallel.
   if (current_request_info_->request().Audio()) {
-    bool request_audio_input_devices = false;
-    // TODO(guidou): Implement spec-compliant device selection for audio. See
-    // http://crbug.com/623104.
-    CopyConstraintsToTrackControls(
-        current_request_info_->request().AudioConstraints(),
-        &current_request_info_->stream_controls()->audio,
-        &request_audio_input_devices);
-    CopyHotwordAndLocalEchoToStreamControls(
-        current_request_info_->request().AudioConstraints(),
-        current_request_info_->stream_controls());
-    // Check if this input device should be used to select a matching output
-    // device for audio rendering.
-    bool enable_automatic_output_device_selection = false;
-    GetConstraintValueAsBoolean(
-        current_request_info_->request().AudioConstraints(),
-        &blink::WebMediaTrackConstraintSet::render_to_associated_sink,
-        &enable_automatic_output_device_selection);
-    current_request_info_->set_enable_automatic_output_device_selection(
-        enable_automatic_output_device_selection);
-
-    if (request_audio_input_devices) {
-      GetMediaDevicesDispatcher()->EnumerateDevices(
-          true /* audio_input */, false /* video_input */,
-          false /* audio_output */,
-          base::Bind(&UserMediaClientImpl::SelectAudioInputDevice,
-                     weak_factory_.GetWeakPtr(),
-                     current_request_info_->request()));
-      return;
-    }
+    if (IsOldAudioConstraints())
+      LegacySetupAudioInput();
+    else
+      SetupAudioInput();
+    return;
   }
-
-  SetupVideoInput(current_request_info_->request());
+  SetupVideoInput();
 }
 
-void UserMediaClientImpl::SelectAudioInputDevice(
+void UserMediaClientImpl::LegacySetupAudioInput() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsOldAudioConstraints());
+  DCHECK(current_request_info_);
+  DCHECK(current_request_info_->request().Audio());
+
+  bool request_audio_input_devices = false;
+  CopyConstraintsToTrackControls(
+      current_request_info_->request().AudioConstraints(),
+      &current_request_info_->stream_controls()->audio,
+      &request_audio_input_devices);
+  CopyHotwordAndLocalEchoToStreamControls(
+      current_request_info_->request().AudioConstraints(),
+      current_request_info_->stream_controls());
+  // Check if this input device should be used to select a matching output
+  // device for audio rendering.
+  bool enable_automatic_output_device_selection = false;
+  GetConstraintValueAsBoolean(
+      current_request_info_->request().AudioConstraints(),
+      &blink::WebMediaTrackConstraintSet::render_to_associated_sink,
+      &enable_automatic_output_device_selection);
+  current_request_info_->set_legacy_enable_automatic_output_device_selection(
+      enable_automatic_output_device_selection);
+
+  if (request_audio_input_devices) {
+    GetMediaDevicesDispatcher()->EnumerateDevices(
+        true /* audio_input */, false /* video_input */,
+        false /* audio_output */,
+        base::Bind(&UserMediaClientImpl::LegacySelectAudioInputDevice,
+                   weak_factory_.GetWeakPtr(),
+                   current_request_info_->request()));
+    return;
+  }
+
+  // No further audio setup required. Continue with video.
+  SetupVideoInput();
+}
+
+void UserMediaClientImpl::LegacySelectAudioInputDevice(
     const blink::WebUserMediaRequest& user_media_request,
     const EnumerationResult& device_enumeration) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsOldAudioConstraints());
+  // The frame might reload or |user_media_request| might be cancelled while
+  // devices are enumerated. Do nothing if a different request is being
+  // processed at this point.
   if (!IsCurrentRequestInfo(user_media_request))
     return;
 
@@ -440,38 +495,118 @@
   if (!PickDeviceId(user_media_request.AudioConstraints(),
                     device_enumeration[MEDIA_DEVICE_TYPE_AUDIO_INPUT],
                     &audio_controls.device_id)) {
-    GetUserMediaRequestFailed(user_media_request, MEDIA_DEVICE_NO_HARDWARE, "");
+    GetUserMediaRequestFailed(MEDIA_DEVICE_NO_HARDWARE, "");
     return;
   }
 
-  SetupVideoInput(user_media_request);
+  // No further audio setup required. Continue with video.
+  SetupVideoInput();
 }
 
-void UserMediaClientImpl::SetupVideoInput(
-    const blink::WebUserMediaRequest& user_media_request) {
+void UserMediaClientImpl::SetupAudioInput() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!IsOldAudioConstraints());
+  DCHECK(current_request_info_);
+  DCHECK(current_request_info_->request().Audio());
+
+  auto& audio_controls = current_request_info_->stream_controls()->audio;
+  InitializeTrackControls(current_request_info_->request().AudioConstraints(),
+                          &audio_controls);
+  if (IsDeviceSource(audio_controls.stream_source)) {
+    GetMediaDevicesDispatcher()->GetAudioInputCapabilities(base::Bind(
+        &UserMediaClientImpl::SelectAudioSettings, weak_factory_.GetWeakPtr(),
+        current_request_info_->request()));
+  } else {
+    if (!IsValidAudioContentSource(audio_controls.stream_source)) {
+      blink::WebString failed_constraint_name =
+          blink::WebString::FromASCII(current_request_info_->request()
+                                          .AudioConstraints()
+                                          .Basic()
+                                          .media_stream_source.GetName());
+      MediaStreamRequestResult result = MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
+      GetUserMediaRequestFailed(result, failed_constraint_name);
+      return;
+    }
+    SelectAudioSettings(current_request_info_->request(),
+                        AudioDeviceCaptureCapabilities());
+  }
+}
+
+void UserMediaClientImpl::SelectAudioSettings(
+    const blink::WebUserMediaRequest& user_media_request,
+    std::vector<::mojom::AudioInputDeviceCapabilitiesPtr>
+        audio_input_capabilities) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!IsOldAudioConstraints());
+  // The frame might reload or |user_media_request| might be cancelled while
+  // capabilities are queried. Do nothing if a different request is being
+  // processed at this point.
   if (!IsCurrentRequestInfo(user_media_request))
     return;
 
-  if (!user_media_request.Video()) {
+  DCHECK(current_request_info_->stream_controls()->audio.requested);
+  auto settings =
+      SelectSettingsAudioCapture(std::move(audio_input_capabilities),
+                                 user_media_request.AudioConstraints());
+  if (!settings.HasValue()) {
+    blink::WebString failed_constraint_name =
+        blink::WebString::FromASCII(settings.failed_constraint_name());
+    MediaStreamRequestResult result =
+        failed_constraint_name.IsEmpty()
+            ? MEDIA_DEVICE_NO_HARDWARE
+            : MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
+    GetUserMediaRequestFailed(result, failed_constraint_name);
+    return;
+  }
+  current_request_info_->stream_controls()->audio.device_id =
+      settings.device_id();
+  current_request_info_->stream_controls()->disable_local_echo =
+      settings.disable_local_echo();
+  current_request_info_->stream_controls()->hotword_enabled =
+      settings.hotword_enabled();
+  current_request_info_->SetAudioCaptureSettings(
+      settings,
+      !IsDeviceSource(
+          current_request_info_->stream_controls()->audio.stream_source));
+
+  // No further audio setup required. Continue with video.
+  SetupVideoInput();
+}
+
+void UserMediaClientImpl::SetupVideoInput() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(current_request_info_);
+
+  if (!current_request_info_->request().Video()) {
     GenerateStreamForCurrentRequestInfo();
     return;
   }
   auto& video_controls = current_request_info_->stream_controls()->video;
-  InitializeTrackControls(user_media_request.VideoConstraints(),
+  InitializeTrackControls(current_request_info_->request().VideoConstraints(),
                           &video_controls);
   if (IsDeviceSource(video_controls.stream_source)) {
-    GetMediaDevicesDispatcher()->GetVideoInputCapabilities(
-        base::Bind(&UserMediaClientImpl::SelectVideoDeviceSettings,
-                   weak_factory_.GetWeakPtr(), user_media_request));
+    GetMediaDevicesDispatcher()->GetVideoInputCapabilities(base::Bind(
+        &UserMediaClientImpl::SelectVideoDeviceSettings,
+        weak_factory_.GetWeakPtr(), current_request_info_->request()));
   } else {
+    if (!IsValidVideoContentSource(video_controls.stream_source)) {
+      blink::WebString failed_constraint_name =
+          blink::WebString::FromASCII(current_request_info_->request()
+                                          .VideoConstraints()
+                                          .Basic()
+                                          .media_stream_source.GetName());
+      MediaStreamRequestResult result = MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
+      GetUserMediaRequestFailed(result, failed_constraint_name);
+      return;
+    }
     base::PostTaskAndReplyWithResult(
         worker_task_runner_.get(), FROM_HERE,
         base::Bind(&SelectSettingsVideoContentCapture,
-                   user_media_request.VideoConstraints(),
+                   current_request_info_->request().VideoConstraints(),
                    video_controls.stream_source),
         base::Bind(&UserMediaClientImpl::FinalizeSelectVideoContentSettings,
-                   weak_factory_.GetWeakPtr(), user_media_request));
+                   weak_factory_.GetWeakPtr(),
+                   current_request_info_->request()));
   }
 }
 
@@ -480,6 +615,9 @@
     std::vector<::mojom::VideoInputDeviceCapabilitiesPtr>
         video_input_capabilities) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // The frame might reload or |user_media_request| might be cancelled while
+  // capabilities are queried. Do nothing if a different request is being
+  // processed at this point.
   if (!IsCurrentRequestInfo(user_media_request))
     return;
 
@@ -518,8 +656,7 @@
         failed_constraint_name.IsEmpty()
             ? MEDIA_DEVICE_NO_HARDWARE
             : MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED;
-    GetUserMediaRequestFailed(user_media_request, result,
-                              failed_constraint_name);
+    GetUserMediaRequestFailed(result, failed_constraint_name);
     return;
   }
   current_request_info_->stream_controls()->video.device_id =
@@ -540,10 +677,7 @@
     blink::WebString failed_constraint_name =
         blink::WebString::FromASCII(settings.failed_constraint_name());
     DCHECK(!failed_constraint_name.IsEmpty());
-    blink::WebString device_id_constraint_name = blink::WebString::FromASCII(
-        user_media_request.VideoConstraints().Basic().device_id.GetName());
-    GetUserMediaRequestFailed(user_media_request,
-                              MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED,
+    GetUserMediaRequestFailed(MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED,
                               failed_constraint_name);
     return;
   }
@@ -763,7 +897,7 @@
     return;
   }
 
-  GetUserMediaRequestFailed(current_request_info_->request(), result, "");
+  GetUserMediaRequestFailed(result, "");
   DeleteRequestInfo(current_request_info_->request());
 }
 
@@ -847,12 +981,18 @@
     const blink::WebMediaConstraints& constraints,
     const MediaStreamSource::ConstraintsCallback& source_ready) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(current_request_info_);
   // If the audio device is a loopback device (for screen capture), or if the
   // constraints/effects parameters indicate no audio processing is needed,
   // create an efficient, direct-path MediaStreamAudioSource instance.
+  AudioProcessingProperties audio_processing_properties =
+      IsOldAudioConstraints() ? AudioProcessingProperties::FromConstraints(
+                                    constraints, device.device.input)
+                              : current_request_info_->audio_capture_settings()
+                                    .audio_processing_properties();
   if (IsScreenCaptureMediaType(device.device.type) ||
       !MediaStreamAudioProcessor::WouldModifyAudio(
-          constraints, device.device.input.effects)) {
+          audio_processing_properties)) {
     return new LocalMediaStreamAudioSource(RenderFrameObserver::routing_id(),
                                            device, source_ready);
   }
@@ -860,8 +1000,8 @@
   // The audio device is not associated with screen capture and also requires
   // processing.
   ProcessedLocalAudioSource* source = new ProcessedLocalAudioSource(
-      RenderFrameObserver::routing_id(), device, constraints, source_ready,
-      dependency_factory_);
+      RenderFrameObserver::routing_id(), device, audio_processing_properties,
+      source_ready, dependency_factory_);
   return source;
 }
 
@@ -906,7 +1046,14 @@
   DCHECK_EQ(devices.size(), webkit_tracks->size());
 
   StreamDeviceInfoArray overridden_audio_array = devices;
-  if (!current_request_info_->enable_automatic_output_device_selection()) {
+  bool render_to_associated_sink =
+      IsOldAudioConstraints()
+          ? current_request_info_
+                ->legacy_enable_automatic_output_device_selection()
+          : current_request_info_->audio_capture_settings().HasValue() &&
+                current_request_info_->audio_capture_settings()
+                    .render_to_associated_sink();
+  if (!render_to_associated_sink) {
     // If the GetUserMedia request did not explicitly set the constraint
     // kMediaStreamRenderToAssociatedSink, the output device parameters must
     // be removed.
@@ -937,7 +1084,7 @@
                                  request_info->request());
     media_stream_dispatcher_->OnStreamStarted(label);
   } else {
-    GetUserMediaRequestFailed(request_info->request(), result, result_name);
+    GetUserMediaRequestFailed(result, result_name);
 
     blink::WebVector<blink::WebMediaStreamTrack> tracks;
     request_info->web_stream()->AudioTracks(tracks);
@@ -1002,9 +1149,9 @@
 }
 
 void UserMediaClientImpl::GetUserMediaRequestFailed(
-    blink::WebUserMediaRequest request,
     MediaStreamRequestResult result,
     const blink::WebString& result_name) {
+  DCHECK(current_request_info_);
   // Completing the getUserMedia request can lead to that the RenderFrame and
   // the UserMediaClientImpl is destroyed if the JavaScript code request the
   // frame to be destroyed within the scope of the callback. Therefore,
@@ -1012,7 +1159,8 @@
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::Bind(&UserMediaClientImpl::DelayedGetUserMediaRequestFailed,
-                 weak_factory_.GetWeakPtr(), request, result, result_name));
+                 weak_factory_.GetWeakPtr(), current_request_info_->request(),
+                 result, result_name));
 }
 
 void UserMediaClientImpl::DelayedGetUserMediaRequestFailed(
@@ -1261,13 +1409,25 @@
   return media_devices_dispatcher_;
 }
 
+const AudioCaptureSettings&
+UserMediaClientImpl::AudioCaptureSettingsForTesting() const {
+  DCHECK(current_request_info_);
+  return current_request_info_->audio_capture_settings();
+}
+
+const VideoCaptureSettings&
+UserMediaClientImpl::VideoCaptureSettingsForTesting() const {
+  DCHECK(current_request_info_);
+  return current_request_info_->video_capture_settings();
+}
+
 base::Optional<bool>
 UserMediaClientImpl::AutomaticOutputDeviceSelectionEnabledForCurrentRequest() {
   if (!current_request_info_)
     return base::Optional<bool>();
 
   return base::Optional<bool>(
-      current_request_info_->enable_automatic_output_device_selection());
+      current_request_info_->legacy_enable_automatic_output_device_selection());
 }
 
 void UserMediaClientImpl::OnDestruct() {
@@ -1281,7 +1441,8 @@
     const url::Origin& security_origin)
     : request_id_(request_id),
       state_(State::NOT_SENT_FOR_GENERATION),
-      enable_automatic_output_device_selection_(false),
+      legacy_enable_automatic_output_device_selection_(false),
+      is_audio_content_capture_(false),
       is_video_content_capture_(false),
       request_(request),
       is_processing_user_gesture_(is_processing_user_gesture),
@@ -1293,6 +1454,11 @@
     const blink::WebMediaStreamTrack& track,
     bool is_pending) {
   DCHECK(track.Source().GetType() == blink::WebMediaStreamSource::kTypeAudio);
+  DCHECK(request_.Audio());
+#if DCHECK_IS_ON()
+  if (!IsOldAudioConstraints())
+    DCHECK(audio_capture_settings_.HasValue());
+#endif
   MediaStreamAudioSource* native_source =
       MediaStreamAudioSource::From(track.Source());
   // Add the source as pending since OnTrackStarted will expect it to be there.
diff --git a/content/renderer/media/user_media_client_impl.h b/content/renderer/media/user_media_client_impl.h
index 0e4f587a..ba4998d 100644
--- a/content/renderer/media/user_media_client_impl.h
+++ b/content/renderer/media/user_media_client_impl.h
@@ -37,10 +37,11 @@
 }
 
 namespace content {
-class PeerConnectionDependencyFactory;
+class AudioCaptureSettings;
 class MediaStreamAudioSource;
 class MediaStreamDispatcher;
 class MediaStreamVideoSource;
+class PeerConnectionDependencyFactory;
 class VideoCaptureSettings;
 
 // UserMediaClientImpl is a delegate for the Media Stream GetUserMedia API.
@@ -101,8 +102,7 @@
   // |request| have completed.
   virtual void GetUserMediaRequestSucceeded(const blink::WebMediaStream& stream,
                                             blink::WebUserMediaRequest request);
-  virtual void GetUserMediaRequestFailed(blink::WebUserMediaRequest request,
-                                         MediaStreamRequestResult result,
+  virtual void GetUserMediaRequestFailed(MediaStreamRequestResult result,
                                          const blink::WebString& result_name);
 
   virtual void EnumerateDevicesSucceded(
@@ -121,10 +121,13 @@
 
   // Returns no value if there is no request being processed. Use only for
   // testing.
-  // TODO(guidou): Remove this method once spec-compliant constraints algorithm
-  // for audio is implemented. http://crbug.com/543997
+  // TODO(guidou): Remove this function. http://crbug.com/706408
   base::Optional<bool> AutomaticOutputDeviceSelectionEnabledForCurrentRequest();
 
+  // Intended to be used only for testing.
+  const AudioCaptureSettings& AudioCaptureSettingsForTesting() const;
+  const VideoCaptureSettings& VideoCaptureSettingsForTesting() const;
+
  private:
   class UserMediaRequestInfo;
   typedef std::vector<blink::WebMediaStreamSource> LocalStreamSources;
@@ -225,21 +228,25 @@
 
   const ::mojom::MediaDevicesDispatcherHostPtr& GetMediaDevicesDispatcher();
 
-  void SelectAudioInputDevice(
+  // TODO(guidou): Remove these functions. http://crbug.com/706408
+  void LegacySetupAudioInput();
+  void LegacySelectAudioInputDevice(
       const blink::WebUserMediaRequest& user_media_request,
       const EnumerationResult& device_enumeration);
 
-  void SetupVideoInput(const blink::WebUserMediaRequest& user_media_request);
+  void SetupAudioInput();
+  void SelectAudioSettings(const blink::WebUserMediaRequest& user_media_request,
+                           std::vector<::mojom::AudioInputDeviceCapabilitiesPtr>
+                               audio_input_capabilities);
 
+  void SetupVideoInput();
   void SelectVideoDeviceSettings(
       const blink::WebUserMediaRequest& user_media_request,
       std::vector<::mojom::VideoInputDeviceCapabilitiesPtr>
           video_input_capabilities);
-
   void FinalizeSelectVideoDeviceSettings(
       const blink::WebUserMediaRequest& user_media_request,
       const VideoCaptureSettings& settings);
-
   void FinalizeSelectVideoContentSettings(
       const blink::WebUserMediaRequest& user_media_request,
       const VideoCaptureSettings& settings);
diff --git a/content/renderer/media/user_media_client_impl_unittest.cc b/content/renderer/media/user_media_client_impl_unittest.cc
index 8847631..b5505f07 100644
--- a/content/renderer/media/user_media_client_impl_unittest.cc
+++ b/content/renderer/media/user_media_client_impl_unittest.cc
@@ -13,15 +13,21 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "content/child/child_process.h"
 #include "content/common/media/media_devices.h"
+#include "content/public/common/content_features.h"
 #include "content/renderer/media/media_stream.h"
+#include "content/renderer/media/media_stream_audio_processor_options.h"
 #include "content/renderer/media/media_stream_audio_source.h"
+#include "content/renderer/media/media_stream_constraints_util.h"
+#include "content/renderer/media/media_stream_constraints_util_video_content.h"
 #include "content/renderer/media/media_stream_track.h"
 #include "content/renderer/media/mock_constraint_factory.h"
 #include "content/renderer/media/mock_media_stream_dispatcher.h"
 #include "content/renderer/media/mock_media_stream_video_source.h"
 #include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
+#include "media/audio/audio_device_description.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h"
@@ -47,7 +53,6 @@
     const char* basic_ideal_value = nullptr,
     const char* advanced_exact_value = nullptr) {
   MockConstraintFactory factory;
-  blink::WebMediaTrackConstraintSet basic;
   if (basic_exact_value) {
     factory.basic().device_id.SetExact(
         blink::WebString::FromUTF8(basic_exact_value));
@@ -161,13 +166,43 @@
 
   void GetAudioInputCapabilities(
       GetAudioInputCapabilitiesCallback client_callback) override {
-    NOTREACHED();
+    if (IsOldAudioConstraints())
+      NOTREACHED();
+
+    std::vector<::mojom::AudioInputDeviceCapabilitiesPtr> result;
+    ::mojom::AudioInputDeviceCapabilitiesPtr device =
+        ::mojom::AudioInputDeviceCapabilities::New();
+    device->device_id = media::AudioDeviceDescription::kDefaultDeviceId;
+    device->parameters = audio_parameters_;
+    result.push_back(std::move(device));
+
+    device = ::mojom::AudioInputDeviceCapabilities::New();
+    device->device_id = kFakeAudioInputDeviceId1;
+    device->parameters = audio_parameters_;
+    result.push_back(std::move(device));
+
+    device = ::mojom::AudioInputDeviceCapabilities::New();
+    device->device_id = kFakeAudioInputDeviceId2;
+    device->parameters = audio_parameters_;
+    result.push_back(std::move(device));
+
+    std::move(client_callback).Run(std::move(result));
+  }
+
+  media::AudioParameters& AudioParameters() { return audio_parameters_; }
+
+  void ResetAudioParameters() {
+    audio_parameters_ = media::AudioParameters::UnavailableDeviceParams();
   }
 
   MOCK_METHOD2(SubscribeDeviceChangeNotifications,
                void(MediaDeviceType type, uint32_t subscription_id));
   MOCK_METHOD2(UnsubscribeDeviceChangeNotifications,
                void(MediaDeviceType type, uint32_t subscription_id));
+
+ private:
+  media::AudioParameters audio_parameters_ =
+      media::AudioParameters::UnavailableDeviceParams();
 };
 
 class UserMediaClientImplUnderTest : public UserMediaClientImpl {
@@ -221,7 +256,6 @@
   }
 
   void GetUserMediaRequestFailed(
-      blink::WebUserMediaRequest request_info,
       content::MediaStreamRequestResult result,
       const blink::WebString& result_name) override {
     last_generated_stream_.Reset();
@@ -306,7 +340,15 @@
   content::MediaStreamRequestResult error_reason() const { return result_; }
   blink::WebString error_name() const { return result_name_; }
 
+  AudioCaptureSettings AudioSettings() const {
+    return AudioCaptureSettingsForTesting();
+  }
+  VideoCaptureSettings VideoSettings() const {
+    return VideoCaptureSettingsForTesting();
+  }
+
   // Access to the request queue for testing.
+  // TODO(guidou): Remove this function. http://crbug.com/704608
   bool UserMediaRequestHasAutomaticDeviceSelection() {
     base::Optional<bool> enabled =
         AutomaticOutputDeviceSelectionEnabledForCurrentRequest();
@@ -325,11 +367,19 @@
   MockMediaStreamVideoCapturerSource* video_source_;
 };
 
-class UserMediaClientImplTest : public ::testing::Test {
+class UserMediaClientImplTest : public ::testing::TestWithParam<bool> {
  public:
   UserMediaClientImplTest()
       : binding_user_media(&media_devices_dispatcher_),
-        binding_event_dispatcher_(&media_devices_dispatcher_) {}
+        binding_event_dispatcher_(&media_devices_dispatcher_) {
+    if (GetParam()) {
+      scoped_feature_list_.InitAndEnableFeature(
+          features::kMediaStreamOldAudioConstraints);
+    } else {
+      scoped_feature_list_.InitAndDisableFeature(
+          features::kMediaStreamOldAudioConstraints);
+    }
+  }
 
   void SetUp() override {
     // Create our test object.
@@ -463,16 +513,18 @@
 
   std::unique_ptr<UserMediaClientImplUnderTest> user_media_client_impl_;
   std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  // TODO(guidou): Remove this field. http://crbug.com/706408
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-TEST_F(UserMediaClientImplTest, GenerateMediaStream) {
+TEST_P(UserMediaClientImplTest, GenerateMediaStream) {
   // Generate a stream with both audio and video.
   blink::WebMediaStream mixed_desc = RequestLocalMediaStream();
 }
 
 // Test that the same source object is used if two MediaStreams are generated
 // using the same source.
-TEST_F(UserMediaClientImplTest, GenerateTwoMediaStreamsWithSameSource) {
+TEST_P(UserMediaClientImplTest, GenerateTwoMediaStreamsWithSameSource) {
   blink::WebMediaStream desc1 = RequestLocalMediaStream();
   blink::WebMediaStream desc2 = RequestLocalMediaStream();
 
@@ -499,7 +551,7 @@
 
 // Test that the same source object is not used if two MediaStreams are
 // generated using different sources.
-TEST_F(UserMediaClientImplTest, GenerateTwoMediaStreamsWithDifferentSources) {
+TEST_P(UserMediaClientImplTest, GenerateTwoMediaStreamsWithDifferentSources) {
   blink::WebMediaStream desc1 = RequestLocalMediaStream();
   // Make sure another device is selected (another |session_id|) in  the next
   // gUM request.
@@ -527,7 +579,7 @@
             MediaStreamAudioSource::From(desc2_audio_tracks[0].Source()));
 }
 
-TEST_F(UserMediaClientImplTest, StopLocalTracks) {
+TEST_P(UserMediaClientImplTest, StopLocalTracks) {
   // Generate a stream with both audio and video.
   blink::WebMediaStream mixed_desc = RequestLocalMediaStream();
 
@@ -548,7 +600,7 @@
 // MediaStream is stopped if there are two MediaStreams with tracks using the
 // same device. The source is stopped
 // if there are no more MediaStream tracks using the device.
-TEST_F(UserMediaClientImplTest, StopLocalTracksWhenTwoStreamUseSameDevices) {
+TEST_P(UserMediaClientImplTest, StopLocalTracksWhenTwoStreamUseSameDevices) {
   // Generate a stream with both audio and video.
   blink::WebMediaStream desc1 = RequestLocalMediaStream();
   blink::WebMediaStream desc2 = RequestLocalMediaStream();
@@ -578,7 +630,7 @@
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
 }
 
-TEST_F(UserMediaClientImplTest, StopSourceWhenMediaStreamGoesOutOfScope) {
+TEST_P(UserMediaClientImplTest, StopSourceWhenMediaStreamGoesOutOfScope) {
   // Generate a stream with both audio and video.
   RequestLocalMediaStream();
   // Makes sure the test itself don't hold a reference to the created
@@ -593,7 +645,7 @@
 
 // Test that the MediaStreams are deleted if a new document is loaded in the
 // frame.
-TEST_F(UserMediaClientImplTest, LoadNewDocumentInFrame) {
+TEST_P(UserMediaClientImplTest, LoadNewDocumentInFrame) {
   // Test a stream with both audio and video.
   blink::WebMediaStream mixed_desc = RequestLocalMediaStream();
   blink::WebMediaStream desc2 = RequestLocalMediaStream();
@@ -604,7 +656,7 @@
 }
 
 // This test what happens if a video source to a MediaSteam fails to start.
-TEST_F(UserMediaClientImplTest, MediaVideoSourceFailToStart) {
+TEST_P(UserMediaClientImplTest, MediaVideoSourceFailToStart) {
   user_media_client_impl_->RequestUserMediaForTest();
   FakeMediaStreamDispatcherRequestUserMediaComplete();
   FailToStartMockedVideoSource();
@@ -619,7 +671,7 @@
 }
 
 // This test what happens if an audio source fail to initialize.
-TEST_F(UserMediaClientImplTest, MediaAudioSourceFailToInitialize) {
+TEST_P(UserMediaClientImplTest, MediaAudioSourceFailToInitialize) {
   user_media_client_impl_->SetCreateSourceThatFails(true);
   user_media_client_impl_->RequestUserMediaForTest();
   FakeMediaStreamDispatcherRequestUserMediaComplete();
@@ -636,7 +688,7 @@
 
 // This test what happens if UserMediaClientImpl is deleted before a source has
 // started.
-TEST_F(UserMediaClientImplTest, MediaStreamImplShutDown) {
+TEST_P(UserMediaClientImplTest, MediaStreamImplShutDown) {
   user_media_client_impl_->RequestUserMediaForTest();
   FakeMediaStreamDispatcherRequestUserMediaComplete();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
@@ -647,7 +699,7 @@
 
 // This test what happens if a new document is loaded in the frame while the
 // MediaStream is being generated by the MediaStreamDispatcher.
-TEST_F(UserMediaClientImplTest, ReloadFrameWhileGeneratingStream) {
+TEST_P(UserMediaClientImplTest, ReloadFrameWhileGeneratingStream) {
   user_media_client_impl_->RequestUserMediaForTest();
   LoadNewDocumentInFrame();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
@@ -659,7 +711,7 @@
 
 // This test what happens if a newdocument is loaded in the frame while the
 // sources are being started.
-TEST_F(UserMediaClientImplTest, ReloadFrameWhileGeneratingSources) {
+TEST_P(UserMediaClientImplTest, ReloadFrameWhileGeneratingSources) {
   user_media_client_impl_->RequestUserMediaForTest();
   FakeMediaStreamDispatcherRequestUserMediaComplete();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
@@ -672,7 +724,7 @@
 
 // This test what happens if stop is called on a track after the frame has
 // been reloaded.
-TEST_F(UserMediaClientImplTest, StopTrackAfterReload) {
+TEST_P(UserMediaClientImplTest, StopTrackAfterReload) {
   blink::WebMediaStream mixed_desc = RequestLocalMediaStream();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   LoadNewDocumentInFrame();
@@ -693,7 +745,7 @@
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
 }
 
-TEST_F(UserMediaClientImplTest, EnumerateMediaDevices) {
+TEST_P(UserMediaClientImplTest, EnumerateMediaDevices) {
   user_media_client_impl_->RequestMediaDevicesForTest();
   base::RunLoop().RunUntilIdle();
 
@@ -749,7 +801,11 @@
       user_media_client_impl_->last_devices()[4].GroupId()));
 }
 
-TEST_F(UserMediaClientImplTest, RenderToAssociatedSinkConstraint) {
+// TODO(guidou): Remove this test. http://crbug.com/706408
+TEST_P(UserMediaClientImplTest, RenderToAssociatedSinkConstraint) {
+  if (!IsOldAudioConstraints())
+    return;
+
   // For a UserMediaRequest without audio, we expect false.
   blink::WebUserMediaRequest request =
       blink::WebUserMediaRequest::CreateForTesting(blink::WebMediaConstraints(),
@@ -786,7 +842,256 @@
       factory.CreateWebMediaConstraints()));
 }
 
-TEST_F(UserMediaClientImplTest, ObserveMediaDeviceChanges) {
+TEST_P(UserMediaClientImplTest, DefaultConstraintsPropagate) {
+  if (IsOldAudioConstraints())
+    return;
+
+  blink::WebUserMediaRequest request =
+      blink::WebUserMediaRequest::CreateForTesting(CreateDefaultConstraints(),
+                                                   CreateDefaultConstraints());
+  user_media_client_impl_->RequestUserMediaForTest(request);
+  AudioCaptureSettings audio_capture_settings =
+      user_media_client_impl_->AudioSettings();
+  VideoCaptureSettings video_capture_settings =
+      user_media_client_impl_->VideoSettings();
+  user_media_client_impl_->CancelUserMediaRequest(request);
+
+  // Check default values selected by the constraints algorithm.
+  EXPECT_TRUE(audio_capture_settings.HasValue());
+  EXPECT_EQ(media::AudioDeviceDescription::kDefaultDeviceId,
+            audio_capture_settings.device_id());
+  EXPECT_FALSE(audio_capture_settings.hotword_enabled());
+  EXPECT_TRUE(audio_capture_settings.disable_local_echo());
+  EXPECT_FALSE(audio_capture_settings.render_to_associated_sink());
+
+  const AudioProcessingProperties& properties =
+      audio_capture_settings.audio_processing_properties();
+  EXPECT_TRUE(properties.enable_sw_echo_cancellation);
+  EXPECT_FALSE(properties.disable_hw_echo_cancellation);
+  EXPECT_FALSE(properties.goog_audio_mirroring);
+  EXPECT_TRUE(properties.goog_auto_gain_control);
+  // The default value for goog_experimental_echo_cancellation is platform
+  // dependent.
+  EXPECT_EQ(AudioProcessingProperties().goog_experimental_echo_cancellation,
+            properties.goog_experimental_echo_cancellation);
+  EXPECT_TRUE(properties.goog_typing_noise_detection);
+  EXPECT_TRUE(properties.goog_noise_suppression);
+  EXPECT_TRUE(properties.goog_experimental_noise_suppression);
+  EXPECT_TRUE(properties.goog_beamforming);
+  EXPECT_TRUE(properties.goog_highpass_filter);
+  EXPECT_TRUE(properties.goog_experimental_auto_gain_control);
+  EXPECT_TRUE(properties.goog_array_geometry.empty());
+
+  EXPECT_TRUE(video_capture_settings.HasValue());
+  EXPECT_EQ(video_capture_settings.Width(),
+            MediaStreamVideoSource::kDefaultWidth);
+  EXPECT_EQ(video_capture_settings.Height(),
+            MediaStreamVideoSource::kDefaultHeight);
+  EXPECT_EQ(video_capture_settings.FrameRate(),
+            MediaStreamVideoSource::kDefaultFrameRate);
+  EXPECT_EQ(video_capture_settings.ResolutionChangePolicy(),
+            media::RESOLUTION_POLICY_FIXED_RESOLUTION);
+  EXPECT_EQ(video_capture_settings.PowerLineFrequency(),
+            media::PowerLineFrequency::FREQUENCY_DEFAULT);
+  EXPECT_FALSE(video_capture_settings.noise_reduction());
+  EXPECT_EQ(video_capture_settings.min_frame_rate(), 1.0);
+
+  const VideoTrackAdapterSettings& track_settings =
+      video_capture_settings.track_adapter_settings();
+  EXPECT_EQ(track_settings.max_width, MediaStreamVideoSource::kDefaultWidth);
+  EXPECT_EQ(track_settings.max_height, MediaStreamVideoSource::kDefaultHeight);
+  EXPECT_EQ(track_settings.min_aspect_ratio,
+            1.0 / MediaStreamVideoSource::kDefaultHeight);
+  EXPECT_EQ(track_settings.max_aspect_ratio,
+            MediaStreamVideoSource::kDefaultWidth);
+  // 0.0 is the default max_frame_rate and it indicates no frame-rate adjustment
+  EXPECT_EQ(track_settings.max_frame_rate, 0.0);
+}
+
+TEST_P(UserMediaClientImplTest, DefaultTabCapturePropagate) {
+  if (IsOldAudioConstraints())
+    return;
+
+  MockConstraintFactory factory;
+  factory.basic().media_stream_source.SetExact(
+      blink::WebString::FromASCII(kMediaStreamSourceTab));
+  blink::WebMediaConstraints audio_constraints =
+      factory.CreateWebMediaConstraints();
+  blink::WebMediaConstraints video_constraints =
+      factory.CreateWebMediaConstraints();
+  blink::WebUserMediaRequest request =
+      blink::WebUserMediaRequest::CreateForTesting(audio_constraints,
+                                                   video_constraints);
+  user_media_client_impl_->RequestUserMediaForTest(request);
+  AudioCaptureSettings audio_capture_settings =
+      user_media_client_impl_->AudioSettings();
+  VideoCaptureSettings video_capture_settings =
+      user_media_client_impl_->VideoSettings();
+  user_media_client_impl_->CancelUserMediaRequest(request);
+
+  // Check default values selected by the constraints algorithm.
+  EXPECT_TRUE(audio_capture_settings.HasValue());
+  EXPECT_EQ(std::string(), audio_capture_settings.device_id());
+  EXPECT_FALSE(audio_capture_settings.hotword_enabled());
+  EXPECT_TRUE(audio_capture_settings.disable_local_echo());
+  EXPECT_FALSE(audio_capture_settings.render_to_associated_sink());
+
+  const AudioProcessingProperties& properties =
+      audio_capture_settings.audio_processing_properties();
+  EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+  EXPECT_FALSE(properties.disable_hw_echo_cancellation);
+  EXPECT_FALSE(properties.goog_audio_mirroring);
+  EXPECT_FALSE(properties.goog_auto_gain_control);
+  EXPECT_FALSE(properties.goog_experimental_echo_cancellation);
+  EXPECT_FALSE(properties.goog_typing_noise_detection);
+  EXPECT_FALSE(properties.goog_noise_suppression);
+  EXPECT_FALSE(properties.goog_experimental_noise_suppression);
+  EXPECT_FALSE(properties.goog_beamforming);
+  EXPECT_FALSE(properties.goog_highpass_filter);
+  EXPECT_FALSE(properties.goog_experimental_auto_gain_control);
+  EXPECT_TRUE(properties.goog_array_geometry.empty());
+
+  EXPECT_TRUE(video_capture_settings.HasValue());
+  EXPECT_EQ(video_capture_settings.Width(), kDefaultScreenCastWidth);
+  EXPECT_EQ(video_capture_settings.Height(), kDefaultScreenCastHeight);
+  EXPECT_EQ(video_capture_settings.FrameRate(), kDefaultScreenCastFrameRate);
+  EXPECT_EQ(video_capture_settings.ResolutionChangePolicy(),
+            media::RESOLUTION_POLICY_FIXED_RESOLUTION);
+  EXPECT_EQ(video_capture_settings.PowerLineFrequency(),
+            media::PowerLineFrequency::FREQUENCY_DEFAULT);
+  EXPECT_FALSE(video_capture_settings.noise_reduction());
+  EXPECT_FALSE(video_capture_settings.min_frame_rate().has_value());
+  EXPECT_FALSE(video_capture_settings.max_frame_rate().has_value());
+
+  const VideoTrackAdapterSettings& track_settings =
+      video_capture_settings.track_adapter_settings();
+  EXPECT_EQ(track_settings.max_width, kDefaultScreenCastWidth);
+  EXPECT_EQ(track_settings.max_height, kDefaultScreenCastHeight);
+  EXPECT_EQ(track_settings.min_aspect_ratio, 1.0 / kMaxScreenCastDimension);
+  EXPECT_EQ(track_settings.max_aspect_ratio, kMaxScreenCastDimension);
+  // 0.0 is the default max_frame_rate and it indicates no frame-rate adjustment
+  EXPECT_EQ(track_settings.max_frame_rate, 0.0);
+}
+
+TEST_P(UserMediaClientImplTest, DefaultDesktopCapturePropagate) {
+  if (IsOldAudioConstraints())
+    return;
+
+  MockConstraintFactory factory;
+  factory.basic().media_stream_source.SetExact(
+      blink::WebString::FromASCII(kMediaStreamSourceDesktop));
+  blink::WebMediaConstraints audio_constraints =
+      factory.CreateWebMediaConstraints();
+  blink::WebMediaConstraints video_constraints =
+      factory.CreateWebMediaConstraints();
+  blink::WebUserMediaRequest request =
+      blink::WebUserMediaRequest::CreateForTesting(audio_constraints,
+                                                   video_constraints);
+  user_media_client_impl_->RequestUserMediaForTest(request);
+  AudioCaptureSettings audio_capture_settings =
+      user_media_client_impl_->AudioSettings();
+  VideoCaptureSettings video_capture_settings =
+      user_media_client_impl_->VideoSettings();
+  user_media_client_impl_->CancelUserMediaRequest(request);
+
+  // Check default values selected by the constraints algorithm.
+  EXPECT_TRUE(audio_capture_settings.HasValue());
+  EXPECT_EQ(std::string(), audio_capture_settings.device_id());
+  EXPECT_FALSE(audio_capture_settings.hotword_enabled());
+  EXPECT_FALSE(audio_capture_settings.disable_local_echo());
+  EXPECT_FALSE(audio_capture_settings.render_to_associated_sink());
+
+  const AudioProcessingProperties& properties =
+      audio_capture_settings.audio_processing_properties();
+  EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+  EXPECT_FALSE(properties.disable_hw_echo_cancellation);
+  EXPECT_FALSE(properties.goog_audio_mirroring);
+  EXPECT_FALSE(properties.goog_auto_gain_control);
+  EXPECT_FALSE(properties.goog_experimental_echo_cancellation);
+  EXPECT_FALSE(properties.goog_typing_noise_detection);
+  EXPECT_FALSE(properties.goog_noise_suppression);
+  EXPECT_FALSE(properties.goog_experimental_noise_suppression);
+  EXPECT_FALSE(properties.goog_beamforming);
+  EXPECT_FALSE(properties.goog_highpass_filter);
+  EXPECT_FALSE(properties.goog_experimental_auto_gain_control);
+  EXPECT_TRUE(properties.goog_array_geometry.empty());
+
+  EXPECT_TRUE(video_capture_settings.HasValue());
+  EXPECT_EQ(video_capture_settings.Width(), kDefaultScreenCastWidth);
+  EXPECT_EQ(video_capture_settings.Height(), kDefaultScreenCastHeight);
+  EXPECT_EQ(video_capture_settings.FrameRate(), kDefaultScreenCastFrameRate);
+  EXPECT_EQ(video_capture_settings.ResolutionChangePolicy(),
+            media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT);
+  EXPECT_EQ(video_capture_settings.PowerLineFrequency(),
+            media::PowerLineFrequency::FREQUENCY_DEFAULT);
+  EXPECT_FALSE(video_capture_settings.noise_reduction());
+  EXPECT_FALSE(video_capture_settings.min_frame_rate().has_value());
+  EXPECT_FALSE(video_capture_settings.max_frame_rate().has_value());
+
+  const VideoTrackAdapterSettings& track_settings =
+      video_capture_settings.track_adapter_settings();
+  EXPECT_EQ(track_settings.max_width, kDefaultScreenCastWidth);
+  EXPECT_EQ(track_settings.max_height, kDefaultScreenCastHeight);
+  EXPECT_EQ(track_settings.min_aspect_ratio, 1.0 / kMaxScreenCastDimension);
+  EXPECT_EQ(track_settings.max_aspect_ratio, kMaxScreenCastDimension);
+  // 0.0 is the default max_frame_rate and it indicates no frame-rate adjustment
+  EXPECT_EQ(track_settings.max_frame_rate, 0.0);
+}
+
+TEST_P(UserMediaClientImplTest, NonDefaultAudioConstraintsPropagate) {
+  if (IsOldAudioConstraints())
+    return;
+
+  MockConstraintFactory factory;
+  factory.basic().device_id.SetExact(
+      blink::WebString::FromASCII(kFakeAudioInputDeviceId1));
+  factory.basic().hotword_enabled.SetExact(true);
+  factory.basic().disable_local_echo.SetExact(true);
+  factory.basic().render_to_associated_sink.SetExact(true);
+  factory.basic().echo_cancellation.SetExact(false);
+  factory.basic().goog_audio_mirroring.SetExact(true);
+  factory.basic().goog_typing_noise_detection.SetExact(true);
+  factory.basic().goog_array_geometry.SetExact(
+      blink::WebString::FromASCII("1 1 1"));
+  blink::WebMediaConstraints audio_constraints =
+      factory.CreateWebMediaConstraints();
+  // Request contains only audio
+  blink::WebUserMediaRequest request =
+      blink::WebUserMediaRequest::CreateForTesting(
+          audio_constraints, blink::WebMediaConstraints());
+  user_media_client_impl_->RequestUserMediaForTest(request);
+  AudioCaptureSettings audio_capture_settings =
+      user_media_client_impl_->AudioSettings();
+  VideoCaptureSettings video_capture_settings =
+      user_media_client_impl_->VideoSettings();
+  user_media_client_impl_->CancelUserMediaRequest(request);
+
+  EXPECT_FALSE(video_capture_settings.HasValue());
+
+  EXPECT_TRUE(audio_capture_settings.HasValue());
+  EXPECT_EQ(kFakeAudioInputDeviceId1, audio_capture_settings.device_id());
+  EXPECT_TRUE(audio_capture_settings.hotword_enabled());
+  EXPECT_TRUE(audio_capture_settings.disable_local_echo());
+  EXPECT_TRUE(audio_capture_settings.render_to_associated_sink());
+
+  const AudioProcessingProperties& properties =
+      audio_capture_settings.audio_processing_properties();
+  EXPECT_FALSE(properties.enable_sw_echo_cancellation);
+  EXPECT_TRUE(properties.disable_hw_echo_cancellation);
+  EXPECT_TRUE(properties.goog_audio_mirroring);
+  EXPECT_FALSE(properties.goog_auto_gain_control);
+  EXPECT_FALSE(properties.goog_experimental_echo_cancellation);
+  EXPECT_TRUE(properties.goog_typing_noise_detection);
+  EXPECT_FALSE(properties.goog_noise_suppression);
+  EXPECT_FALSE(properties.goog_experimental_noise_suppression);
+  EXPECT_FALSE(properties.goog_beamforming);
+  EXPECT_FALSE(properties.goog_highpass_filter);
+  EXPECT_FALSE(properties.goog_experimental_auto_gain_control);
+  const std::vector<media::Point> kGeometry = {{1.0, 1.0, 1.0}};
+  EXPECT_EQ(kGeometry, properties.goog_array_geometry);
+}
+
+TEST_P(UserMediaClientImplTest, ObserveMediaDeviceChanges) {
   EXPECT_CALL(media_devices_dispatcher_, SubscribeDeviceChangeNotifications(
                                              MEDIA_DEVICE_TYPE_AUDIO_INPUT, _));
   EXPECT_CALL(media_devices_dispatcher_, SubscribeDeviceChangeNotifications(
@@ -822,7 +1127,7 @@
 }
 
 // This test what happens if the audio stream has same id with video stream.
-TEST_F(UserMediaClientImplTest, AudioVideoWithSameId) {
+TEST_P(UserMediaClientImplTest, AudioVideoWithSameId) {
   ms_dispatcher_->TestSameId();
 
   // Generate a stream with both audio and video.
@@ -847,7 +1152,7 @@
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
 }
 
-TEST_F(UserMediaClientImplTest, CreateWithMandatoryInvalidAudioDeviceId) {
+TEST_P(UserMediaClientImplTest, CreateWithMandatoryInvalidAudioDeviceId) {
   blink::WebMediaConstraints audio_constraints =
       CreateDeviceConstraints(kInvalidDeviceId);
   blink::WebUserMediaRequest request =
@@ -858,7 +1163,7 @@
             user_media_client_impl_->request_state());
 }
 
-TEST_F(UserMediaClientImplTest, CreateWithMandatoryInvalidVideoDeviceId) {
+TEST_P(UserMediaClientImplTest, CreateWithMandatoryInvalidVideoDeviceId) {
   blink::WebMediaConstraints video_constraints =
       CreateDeviceConstraints(kInvalidDeviceId);
   blink::WebUserMediaRequest request =
@@ -869,7 +1174,7 @@
             user_media_client_impl_->request_state());
 }
 
-TEST_F(UserMediaClientImplTest, CreateWithMandatoryValidDeviceIds) {
+TEST_P(UserMediaClientImplTest, CreateWithMandatoryValidDeviceIds) {
   blink::WebMediaConstraints audio_constraints =
       CreateDeviceConstraints(kFakeAudioInputDeviceId1);
   blink::WebMediaConstraints video_constraints =
@@ -879,7 +1184,7 @@
                                   kFakeVideoInputDeviceId1);
 }
 
-TEST_F(UserMediaClientImplTest, CreateWithBasicIdealValidDeviceId) {
+TEST_P(UserMediaClientImplTest, CreateWithBasicIdealValidDeviceId) {
   blink::WebMediaConstraints audio_constraints =
       CreateDeviceConstraints(nullptr, kFakeAudioInputDeviceId1);
   blink::WebMediaConstraints video_constraints =
@@ -889,7 +1194,7 @@
                                   kFakeVideoInputDeviceId1);
 }
 
-TEST_F(UserMediaClientImplTest, CreateWithAdvancedExactValidDeviceId) {
+TEST_P(UserMediaClientImplTest, CreateWithAdvancedExactValidDeviceId) {
   blink::WebMediaConstraints audio_constraints =
       CreateDeviceConstraints(nullptr, nullptr, kFakeAudioInputDeviceId1);
   blink::WebMediaConstraints video_constraints = CreateDeviceConstraints(
@@ -899,19 +1204,23 @@
                                   kFakeVideoInputDeviceId1);
 }
 
-TEST_F(UserMediaClientImplTest, CreateWithAllOptionalInvalidDeviceId) {
+TEST_P(UserMediaClientImplTest, CreateWithAllOptionalInvalidDeviceId) {
   blink::WebMediaConstraints audio_constraints =
       CreateDeviceConstraints(nullptr, kInvalidDeviceId, kInvalidDeviceId);
   blink::WebMediaConstraints video_constraints =
       CreateDeviceConstraints(nullptr, kInvalidDeviceId, kInvalidDeviceId);
   // MockMediaStreamDispatcher uses empty string as default audio device ID.
   // MockMediaDevicesDispatcher uses the first device in the enumeration as
-  // default video device ID.
+  // default audio or video device ID.
+  std::string expected_audio_device_id =
+      IsOldAudioConstraints() ? std::string()
+                              : media::AudioDeviceDescription::kDefaultDeviceId;
   TestValidRequestWithConstraints(audio_constraints, video_constraints,
-                                  std::string(), kFakeVideoInputDeviceId1);
+                                  expected_audio_device_id,
+                                  kFakeVideoInputDeviceId1);
 }
 
-TEST_F(UserMediaClientImplTest, CreateWithFacingModeUser) {
+TEST_P(UserMediaClientImplTest, CreateWithFacingModeUser) {
   blink::WebMediaConstraints audio_constraints =
       CreateDeviceConstraints(kFakeAudioInputDeviceId1);
   blink::WebMediaConstraints video_constraints =
@@ -922,7 +1231,7 @@
                                   kFakeVideoInputDeviceId1);
 }
 
-TEST_F(UserMediaClientImplTest, CreateWithFacingModeEnvironment) {
+TEST_P(UserMediaClientImplTest, CreateWithFacingModeEnvironment) {
   blink::WebMediaConstraints audio_constraints =
       CreateDeviceConstraints(kFakeAudioInputDeviceId1);
   blink::WebMediaConstraints video_constraints =
@@ -933,4 +1242,8 @@
                                   kFakeVideoInputDeviceId2);
 }
 
+INSTANTIATE_TEST_CASE_P(,
+                        UserMediaClientImplTest,
+                        testing::Values(true, false));
+
 }  // namespace content
diff --git a/content/renderer/media/video_track_adapter.h b/content/renderer/media/video_track_adapter.h
index 01237e5..c37fe6e 100644
--- a/content/renderer/media/video_track_adapter.h
+++ b/content/renderer/media/video_track_adapter.h
@@ -34,6 +34,9 @@
   int max_height;
   double min_aspect_ratio;
   double max_aspect_ratio;
+  // A |max_frame_rate| of zero is used to signal that no frame-rate adjustment
+  // is necessary.
+  // TODO(guidou): Change this to base::Optional. http://crbug.com/734528
   double max_frame_rate;
   // If supplied, this can be used to detect frames from a rotated device.
   base::Optional<gfx::Size> expected_native_size;
diff --git a/content/renderer/media/webrtc/processed_local_audio_source.cc b/content/renderer/media/webrtc/processed_local_audio_source.cc
index a4fb2da..6fea798 100644
--- a/content/renderer/media/webrtc/processed_local_audio_source.cc
+++ b/content/renderer/media/webrtc/processed_local_audio_source.cc
@@ -4,6 +4,9 @@
 
 #include "content/renderer/media/webrtc/processed_local_audio_source.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
@@ -29,13 +32,13 @@
 ProcessedLocalAudioSource::ProcessedLocalAudioSource(
     int consumer_render_frame_id,
     const StreamDeviceInfo& device_info,
-    const blink::WebMediaConstraints& constraints,
+    const AudioProcessingProperties& audio_processing_properties,
     const ConstraintsCallback& started_callback,
     PeerConnectionDependencyFactory* factory)
     : MediaStreamAudioSource(true /* is_local_source */),
       consumer_render_frame_id_(consumer_render_frame_id),
       pc_factory_(factory),
-      constraints_(constraints),
+      audio_processing_properties_(audio_processing_properties),
       started_callback_(started_callback),
       volume_(0),
       allow_invalid_render_frame_id_for_testing_(false) {
@@ -91,28 +94,15 @@
       device_info().device.matched_output.frames_per_buffer,
       device_info().device.input.effects));
 
-  // Sanity-check that the constraints, plus the additional input effects are
-  // valid when combined.
-  const MediaAudioConstraints audio_constraints(
-      constraints_, device_info().device.input.effects);
-  if (!audio_constraints.IsValid()) {
-    WebRtcLogMessage("ProcessedLocalAudioSource::EnsureSourceIsStarted() fails "
-                     " because MediaAudioConstraints are not valid.");
-    return false;
-  }
-
-  if (device_info().device.input.effects &
-      media::AudioParameters::ECHO_CANCELLER) {
-    // TODO(hta): Figure out if we should be looking at echoCancellation.
-    // Previous code had googEchoCancellation only.
-    const blink::BooleanConstraint& echoCancellation =
-        constraints_.Basic().goog_echo_cancellation;
-    if (echoCancellation.HasExact() && !echoCancellation.Exact()) {
-      StreamDeviceInfo modified_device_info(device_info());
-      modified_device_info.device.input.effects &=
-          ~media::AudioParameters::ECHO_CANCELLER;
-      SetDeviceInfo(modified_device_info);
-    }
+  // Disable HW echo cancellation if constraints explicitly specified no
+  // echo cancellation.
+  if (audio_processing_properties_.disable_hw_echo_cancellation &&
+      (device_info().device.input.effects &
+       media::AudioParameters::ECHO_CANCELLER)) {
+    StreamDeviceInfo modified_device_info(device_info());
+    modified_device_info.device.input.effects &=
+        ~media::AudioParameters::ECHO_CANCELLER;
+    SetDeviceInfo(modified_device_info);
   }
 
   // Create the MediaStreamAudioProcessor, bound to the WebRTC audio device
@@ -125,7 +115,7 @@
     return false;
   }
   audio_processor_ = new rtc::RefCountedObject<MediaStreamAudioProcessor>(
-      constraints_, device_info().device.input, rtc_audio_device);
+      audio_processing_properties_, rtc_audio_device);
 
   // If KEYBOARD_MIC effect is set, change the layout to the corresponding
   // layout that includes the keyboard mic.
@@ -133,7 +123,7 @@
       device_info().device.input.channel_layout);
   if ((device_info().device.input.effects &
        media::AudioParameters::KEYBOARD_MIC) &&
-      audio_constraints.GetGoogExperimentalNoiseSuppression()) {
+      audio_processing_properties_.goog_experimental_noise_suppression) {
     if (channel_layout == media::CHANNEL_LAYOUT_STEREO) {
       channel_layout = media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC;
       DVLOG(1) << "Changed stereo layout to stereo + keyboard mic layout due "
diff --git a/content/renderer/media/webrtc/processed_local_audio_source.h b/content/renderer/media/webrtc/processed_local_audio_source.h
index c712472..ad54656 100644
--- a/content/renderer/media/webrtc/processed_local_audio_source.h
+++ b/content/renderer/media/webrtc/processed_local_audio_source.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_RENDERER_MEDIA_WEBRTC_PROCESSED_LOCAL_AUDIO_SOURCE_H_
 #define CONTENT_RENDERER_MEDIA_WEBRTC_PROCESSED_LOCAL_AUDIO_SOURCE_H_
 
+#include <string>
+
 #include "base/atomicops.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -36,11 +38,12 @@
   // |consumer_render_frame_id| references the RenderFrame that will consume the
   // audio data. Audio parameters and (optionally) a pre-existing audio session
   // ID are derived from |device_info|. |factory| must outlive this instance.
-  ProcessedLocalAudioSource(int consumer_render_frame_id,
-                            const StreamDeviceInfo& device_info,
-                            const blink::WebMediaConstraints& constraints,
-                            const ConstraintsCallback& started_callback,
-                            PeerConnectionDependencyFactory* factory);
+  ProcessedLocalAudioSource(
+      int consumer_render_frame_id,
+      const StreamDeviceInfo& device_info,
+      const AudioProcessingProperties& audio_processing_properties,
+      const ConstraintsCallback& started_callback,
+      PeerConnectionDependencyFactory* factory);
 
   ~ProcessedLocalAudioSource() final;
 
@@ -55,10 +58,8 @@
     allow_invalid_render_frame_id_for_testing_ = allowed;
   }
 
-  // Gets/Sets source constraints. Using this is optional, but must be done
-  // before the first call to ConnectToTrack().
-  const blink::WebMediaConstraints& source_constraints() const {
-    return constraints_;
+  const AudioProcessingProperties& audio_processing_properties() const {
+    return audio_processing_properties_;
   }
 
   // The following accessors are not valid until after the source is started
@@ -112,8 +113,7 @@
   // or data flow changes are being called on the main thread.
   base::ThreadChecker thread_checker_;
 
-  // Cached audio constraints for the capturer.
-  const blink::WebMediaConstraints constraints_;
+  AudioProcessingProperties audio_processing_properties_;
 
   // Callback that's called when the audio source has been initialized.
   ConstraintsCallback started_callback_;
diff --git a/content/renderer/media/webrtc/processed_local_audio_source_unittest.cc b/content/renderer/media/webrtc/processed_local_audio_source_unittest.cc
index 95bf66d..e0bd3aa8 100644
--- a/content/renderer/media/webrtc/processed_local_audio_source_unittest.cc
+++ b/content/renderer/media/webrtc/processed_local_audio_source_unittest.cc
@@ -9,9 +9,9 @@
 #include "base/message_loop/message_loop.h"
 #include "build/build_config.h"
 #include "content/public/renderer/media_stream_audio_sink.h"
+#include "content/renderer/media/media_stream_audio_processor_options.h"
 #include "content/renderer/media/media_stream_audio_track.h"
 #include "content/renderer/media/mock_audio_device_factory.h"
-#include "content/renderer/media/mock_constraint_factory.h"
 #include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "content/renderer/media/webrtc/processed_local_audio_source.h"
 #include "media/base/audio_bus.h"
@@ -96,13 +96,13 @@
   }
 
   void CreateProcessedLocalAudioSource(
-      const blink::WebMediaConstraints& constraints) {
+      const AudioProcessingProperties& properties) {
     ProcessedLocalAudioSource* const source = new ProcessedLocalAudioSource(
         -1 /* consumer_render_frame_id is N/A for non-browser tests */,
         StreamDeviceInfo(MEDIA_DEVICE_AUDIO_CAPTURE, "Mock audio device",
                          "mock_audio_device_id", kSampleRate, kChannelLayout,
                          kRequestedBufferSize),
-        constraints,
+        properties,
         base::Bind(&ProcessedLocalAudioSourceTest::OnAudioSourceStarted,
                    base::Unretained(this)),
         &mock_dependency_factory_);
@@ -161,11 +161,9 @@
 
   // Turn off the default constraints so the sink will get audio in chunks of
   // the native buffer size.
-  MockConstraintFactory constraint_factory;
-  constraint_factory.DisableDefaultAudioConstraints();
-
-  CreateProcessedLocalAudioSource(
-      constraint_factory.CreateWebMediaConstraints());
+  AudioProcessingProperties properties;
+  properties.DisableDefaultPropertiesForTesting();
+  CreateProcessedLocalAudioSource(properties);
 
   // Connect the track, and expect the MockCapturerSource to be initialized and
   // started by ProcessedLocalAudioSource.
@@ -206,35 +204,5 @@
   MediaStreamAudioTrack::From(blink_audio_track())->Stop();
 }
 
-// Tests that the source is not started when invalid audio constraints are
-// present.
-TEST_F(ProcessedLocalAudioSourceTest, FailToStartWithWrongConstraints) {
-  MockConstraintFactory constraint_factory;
-  const std::string dummy_constraint = "dummy";
-  // Set a non-audio constraint.
-  constraint_factory.basic().width.SetExact(240);
-
-  CreateProcessedLocalAudioSource(
-      constraint_factory.CreateWebMediaConstraints());
-
-  // Expect the MockCapturerSource is never initialized/started and the
-  // ConnectToTrack() operation fails due to the invalid constraint.
-  EXPECT_CALL(*mock_audio_device_factory()->mock_capturer_source(),
-              Initialize(_, capture_source_callback(), -1))
-      .Times(0);
-  EXPECT_CALL(*mock_audio_device_factory()->mock_capturer_source(),
-              SetAutomaticGainControl(true)).Times(0);
-  EXPECT_CALL(*mock_audio_device_factory()->mock_capturer_source(), Start())
-      .Times(0);
-  EXPECT_FALSE(audio_source()->ConnectToTrack(blink_audio_track()));
-
-  // Even though ConnectToTrack() failed, there should still have been a new
-  // MediaStreamAudioTrack instance created, owned by the
-  // blink::WebMediaStreamTrack.
-  EXPECT_TRUE(MediaStreamAudioTrack::From(blink_audio_track()));
-}
-
-// TODO(miu): There's a lot of logic in ProcessedLocalAudioSource around
-// constraints processing and validation that should have unit testing.
 
 }  // namespace content
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_adapter.cc b/content/renderer/media/webrtc/webrtc_media_stream_adapter.cc
index 6419adf..e20521ed 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_adapter.cc
+++ b/content/renderer/media/webrtc/webrtc_media_stream_adapter.cc
@@ -36,6 +36,7 @@
     TrackAdded(video_track);
 
   MediaStream* const native_stream = MediaStream::GetMediaStream(web_stream_);
+  DCHECK(native_stream);
   native_stream->AddObserver(this);
 }
 
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_adapter_map.cc b/content/renderer/media/webrtc/webrtc_media_stream_adapter_map.cc
new file mode 100644
index 0000000..e309151
--- /dev/null
+++ b/content/renderer/media/webrtc/webrtc_media_stream_adapter_map.cc
@@ -0,0 +1,88 @@
+// 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 "content/renderer/media/webrtc/webrtc_media_stream_adapter_map.h"
+
+namespace content {
+
+WebRtcMediaStreamAdapterMap::AdapterEntry::AdapterEntry(
+    std::unique_ptr<WebRtcMediaStreamAdapter> adapter)
+    : adapter(std::move(adapter)), ref_count(0) {
+  DCHECK(this->adapter);
+}
+
+WebRtcMediaStreamAdapterMap::AdapterEntry::AdapterEntry(AdapterEntry&& other)
+    : adapter(other.adapter.release()), ref_count(other.ref_count) {}
+
+WebRtcMediaStreamAdapterMap::AdapterEntry::~AdapterEntry() {
+  // |ref_count| is allowed to be non-zero only if this entry has been moved
+  // which is the case if the |adapter| has already been released.
+  DCHECK(!ref_count || !adapter);
+}
+
+WebRtcMediaStreamAdapterMap::AdapterRef::AdapterRef(
+    scoped_refptr<WebRtcMediaStreamAdapterMap> map,
+    const MapEntryIterator& it)
+    : map_(std::move(map)), it_(it) {
+  DCHECK(map_);
+  DCHECK(map_->main_thread_->BelongsToCurrentThread());
+  DCHECK(it_ != map_->local_stream_adapters_.end());
+  DCHECK(entry()->adapter);
+  ++entry()->ref_count;
+}
+
+WebRtcMediaStreamAdapterMap::AdapterRef::~AdapterRef() {
+  DCHECK(map_->main_thread_->BelongsToCurrentThread());
+  if (--entry()->ref_count == 0) {
+    map_->local_stream_adapters_.erase(it_);
+  }
+}
+
+WebRtcMediaStreamAdapterMap::WebRtcMediaStreamAdapterMap(
+    PeerConnectionDependencyFactory* const factory,
+    scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map)
+    : factory_(factory),
+      main_thread_(base::ThreadTaskRunnerHandle::Get()),
+      track_adapter_map_(std::move(track_adapter_map)) {
+  DCHECK(factory_);
+  DCHECK(main_thread_);
+  DCHECK(track_adapter_map_);
+}
+
+WebRtcMediaStreamAdapterMap::~WebRtcMediaStreamAdapterMap() {
+  DCHECK(main_thread_->BelongsToCurrentThread());
+  DCHECK(local_stream_adapters_.empty());
+}
+
+std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef>
+WebRtcMediaStreamAdapterMap::GetLocalStreamAdapter(const std::string& id) {
+  DCHECK(main_thread_->BelongsToCurrentThread());
+  auto it = local_stream_adapters_.find(id);
+  if (it == local_stream_adapters_.end())
+    return nullptr;
+  return base::WrapUnique(new AdapterRef(this, it));
+}
+
+std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef>
+WebRtcMediaStreamAdapterMap::GetOrCreateLocalStreamAdapter(
+    const blink::WebMediaStream& web_stream) {
+  DCHECK(main_thread_->BelongsToCurrentThread());
+  std::string id = web_stream.Id().Utf8();
+  auto it = local_stream_adapters_.find(id);
+  if (it == local_stream_adapters_.end()) {
+    it = local_stream_adapters_
+             .insert(std::make_pair(
+                 id, AdapterEntry(base::MakeUnique<WebRtcMediaStreamAdapter>(
+                         factory_, track_adapter_map_, web_stream))))
+             .first;
+  }
+  return base::WrapUnique(new AdapterRef(this, it));
+}
+
+size_t WebRtcMediaStreamAdapterMap::GetLocalStreamCount() const {
+  DCHECK(main_thread_->BelongsToCurrentThread());
+  return local_stream_adapters_.size();
+}
+
+}  // namespace content
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_adapter_map.h b/content/renderer/media/webrtc/webrtc_media_stream_adapter_map.h
new file mode 100644
index 0000000..112c314
--- /dev/null
+++ b/content/renderer/media/webrtc/webrtc_media_stream_adapter_map.h
@@ -0,0 +1,109 @@
+// 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 CONTENT_RENDERER_MEDIA_WEBRTC_WEBRTC_MEDIA_STREAM_ADAPTER_MAP_H_
+#define CONTENT_RENDERER_MEDIA_WEBRTC_WEBRTC_MEDIA_STREAM_ADAPTER_MAP_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "content/common/content_export.h"
+#include "content/renderer/media/webrtc/webrtc_media_stream_adapter.h"
+#include "third_party/WebKit/public/platform/WebMediaStream.h"
+#include "third_party/webrtc/api/mediastreaminterface.h"
+
+namespace content {
+
+// A map and owner of |WebRtcMediaStreamAdapter|s. Adapters are the glue between
+// blink and webrtc layer versions of streams. As long as a stream is in use by
+// a peer connection there has to exist an adapter for it. The map takes care of
+// creating and disposing stream adapters. Adapters are accessed via
+// |AdapterRef|s, when all references to an adapter are destroyed it is
+// destroyed and removed from the map.
+class CONTENT_EXPORT WebRtcMediaStreamAdapterMap
+    : public base::RefCountedThreadSafe<WebRtcMediaStreamAdapterMap> {
+ private:
+  // The map's entries are reference counted in order to remove the adapter when
+  // all |AdapterRef|s referencing an entry are destroyed.
+  // Private section needed here due to |AdapterRef|'s usage of |AdapterEntry|.
+  struct AdapterEntry {
+    AdapterEntry(std::unique_ptr<WebRtcMediaStreamAdapter> adapter);
+    AdapterEntry(AdapterEntry&& other);
+    ~AdapterEntry();
+
+    AdapterEntry(const AdapterEntry&) = delete;
+    AdapterEntry& operator=(const AdapterEntry&) = delete;
+
+    std::unique_ptr<WebRtcMediaStreamAdapter> adapter;
+    size_t ref_count;
+  };
+
+ public:
+  // Accessor to an adapter to take care of reference counting. When the last
+  // |AdapterRef| is destroyed, the corresponding adapter is destroyed and
+  // removed from the map.
+  class CONTENT_EXPORT AdapterRef {
+   public:
+    // Must be invoked on the main thread. If this was the last reference to the
+    // adapter it will be disposed and removed from the map.
+    ~AdapterRef();
+
+    const WebRtcMediaStreamAdapter& adapter() const {
+      return *it_->second.adapter;
+    }
+
+   private:
+    friend class WebRtcMediaStreamAdapterMap;
+    using MapEntryIterator = std::map<std::string, AdapterEntry>::iterator;
+
+    // Increments the |AdapterEntry::ref_count|.
+    AdapterRef(scoped_refptr<WebRtcMediaStreamAdapterMap> map,
+               const MapEntryIterator& it);
+
+    AdapterEntry* entry() { return &it_->second; }
+
+    scoped_refptr<WebRtcMediaStreamAdapterMap> map_;
+    MapEntryIterator it_;
+  };
+
+  // Must be invoked on the main thread.
+  WebRtcMediaStreamAdapterMap(
+      PeerConnectionDependencyFactory* const factory,
+      scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map);
+
+  // Invoke on the main thread. Gets a new reference to the local stream adapter
+  // by ID, or null if no such adapter was found. When all references are
+  // destroyed the adapter is destroyed and removed from the map. References
+  // must be destroyed on the main thread.
+  std::unique_ptr<AdapterRef> GetLocalStreamAdapter(const std::string& id);
+  // Invoke on the main thread. Gets a new reference to the local stream adapter
+  // for the web stream. If no adapter exists for the stream one is created.
+  // When all references are destroyed the adapter is destroyed and removed from
+  // the map. References must be destroyed on the main thread.
+  std::unique_ptr<AdapterRef> GetOrCreateLocalStreamAdapter(
+      const blink::WebMediaStream& web_stream);
+  // Invoke on the main thread.
+  size_t GetLocalStreamCount() const;
+
+ protected:
+  friend class base::RefCountedThreadSafe<WebRtcMediaStreamAdapterMap>;
+
+  // Invoke on the main thread.
+  virtual ~WebRtcMediaStreamAdapterMap();
+
+ private:
+  // Pointer to a |PeerConnectionDependencyFactory| owned by the |RenderThread|.
+  // It's valid for the lifetime of |RenderThread|.
+  PeerConnectionDependencyFactory* const factory_;
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
+  // Takes care of creating and owning track adapters, used by stream adapters.
+  scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map_;
+
+  std::map<std::string, AdapterEntry> local_stream_adapters_;
+  // TODO(hbos): Take care of remote stream adapters as well. This will require
+  // usage of the signaling thread. crbug.com/705901
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_WEBRTC_MEDIA_STREAM_ADAPTER_MAP_H_
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc b/content/renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc
new file mode 100644
index 0000000..db43fcdd
--- /dev/null
+++ b/content/renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc
@@ -0,0 +1,102 @@
+// 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 "content/renderer/media/webrtc/webrtc_media_stream_adapter_map.h"
+
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "content/child/child_process.h"
+#include "content/renderer/media/media_stream.h"
+#include "content/renderer/media/media_stream_video_source.h"
+#include "content/renderer/media/media_stream_video_track.h"
+#include "content/renderer/media/mock_audio_device_factory.h"
+#include "content/renderer/media/mock_media_stream_video_source.h"
+#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
+#include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebMediaStream.h"
+#include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
+#include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/web/WebHeap.h"
+
+using ::testing::_;
+
+namespace content {
+
+class WebRtcMediaStreamAdapterMapTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    main_thread_ = base::ThreadTaskRunnerHandle::Get();
+    map_ = new WebRtcMediaStreamAdapterMap(
+        dependency_factory_.get(),
+        new WebRtcMediaStreamTrackAdapterMap(dependency_factory_.get()));
+  }
+
+  void TearDown() override { blink::WebHeap::CollectAllGarbageForTesting(); }
+
+  blink::WebMediaStream CreateLocalStream(const std::string& id) {
+    blink::WebVector<blink::WebMediaStreamTrack> web_video_tracks(
+        static_cast<size_t>(1));
+    blink::WebMediaStreamSource video_source;
+    video_source.Initialize("video_source",
+                            blink::WebMediaStreamSource::kTypeVideo,
+                            "video_source", false /* remote */);
+    MediaStreamVideoSource* native_source =
+        new MockMediaStreamVideoSource(false);
+    video_source.SetExtraData(native_source);
+    web_video_tracks[0] = MediaStreamVideoTrack::CreateVideoTrack(
+        native_source, MediaStreamVideoSource::ConstraintsCallback(), true);
+
+    blink::WebMediaStream web_stream;
+    web_stream.Initialize(blink::WebString::FromUTF8(id),
+                          blink::WebVector<blink::WebMediaStreamTrack>(),
+                          web_video_tracks);
+    web_stream.SetExtraData(new MediaStream());
+    return web_stream;
+  }
+
+ protected:
+  // Message loop and child processes is needed for task queues and threading to
+  // work, as is necessary to create tracks and adapters.
+  base::MessageLoop message_loop_;
+  ChildProcess child_process_;
+
+  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
+  scoped_refptr<WebRtcMediaStreamAdapterMap> map_;
+};
+
+TEST_F(WebRtcMediaStreamAdapterMapTest, AddAndRemoveLocalStreamAdapter) {
+  blink::WebMediaStream local_stream = CreateLocalStream("local_stream");
+  std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef> adapter_ref =
+      map_->GetOrCreateLocalStreamAdapter(local_stream);
+  EXPECT_TRUE(adapter_ref);
+  EXPECT_TRUE(adapter_ref->adapter().IsEqual(local_stream));
+  EXPECT_EQ(1u, map_->GetLocalStreamCount());
+
+  std::unique_ptr<WebRtcMediaStreamAdapterMap::AdapterRef> adapter_ref2 =
+      map_->GetLocalStreamAdapter("local_stream");
+  EXPECT_TRUE(adapter_ref2);
+  EXPECT_EQ(&adapter_ref2->adapter(), &adapter_ref->adapter());
+  EXPECT_EQ(1u, map_->GetLocalStreamCount());
+
+  adapter_ref.reset();
+  EXPECT_EQ(1u, map_->GetLocalStreamCount());
+  adapter_ref2.reset();
+  EXPECT_EQ(0u, map_->GetLocalStreamCount());
+}
+
+TEST_F(WebRtcMediaStreamAdapterMapTest, GetLocalStreamAdapterInvalidID) {
+  EXPECT_FALSE(map_->GetLocalStreamAdapter("invalid"));
+}
+
+}  // namespace content
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc b/content/renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc
index 17f809f2..1d4204eb 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc
@@ -61,7 +61,7 @@
                            media::AudioParameters::kAudioCDSampleRate,
                            media::CHANNEL_LAYOUT_STEREO,
                            media::AudioParameters::kAudioCDSampleRate / 50),
-          MockConstraintFactory().CreateWebMediaConstraints(),
+          AudioProcessingProperties(),
           base::Bind(&WebRtcMediaStreamAdapterTest::OnAudioSourceStarted),
           dependency_factory_.get());
       source->SetAllowInvalidRenderFrameIdForTesting(true);
diff --git a/content/renderer/pepper/pepper_platform_audio_input.cc b/content/renderer/pepper/pepper_platform_audio_input.cc
index 80580711..b40b0e0 100644
--- a/content/renderer/pepper/pepper_platform_audio_input.cc
+++ b/content/renderer/pepper/pepper_platform_audio_input.cc
@@ -79,7 +79,8 @@
     base::SharedMemoryHandle handle,
     base::SyncSocket::Handle socket_handle,
     int length,
-    int total_segments) {
+    int total_segments,
+    bool initially_muted) {
 #if defined(OS_WIN)
   DCHECK(handle.IsValid());
   DCHECK(socket_handle);
@@ -95,8 +96,9 @@
     // If shutdown has occurred, |client_| will be NULL and the handles will be
     // cleaned up on the main thread.
     main_task_runner_->PostTask(
-        FROM_HERE, base::Bind(&PepperPlatformAudioInput::OnStreamCreated, this,
-                              handle, socket_handle, length, total_segments));
+        FROM_HERE,
+        base::Bind(&PepperPlatformAudioInput::OnStreamCreated, this, handle,
+                   socket_handle, length, total_segments, initially_muted));
   } else {
     // Must dereference the client only on the main thread. Shutdown may have
     // occurred while the request was in-flight, so we need to NULL check.
diff --git a/content/renderer/pepper/pepper_platform_audio_input.h b/content/renderer/pepper/pepper_platform_audio_input.h
index 54f21acd..9060d2f 100644
--- a/content/renderer/pepper/pepper_platform_audio_input.h
+++ b/content/renderer/pepper/pepper_platform_audio_input.h
@@ -58,7 +58,8 @@
   void OnStreamCreated(base::SharedMemoryHandle handle,
                        base::SyncSocket::Handle socket_handle,
                        int length,
-                       int total_segments) override;
+                       int total_segments,
+                       bool initially_muted) override;
   void OnError() override;
   void OnMuted(bool is_muted) override;
   void OnIPCClosed() override;
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 06e60c54..168c460d 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -22,6 +22,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/child/blob_storage/webblobregistry_impl.h"
@@ -411,7 +412,7 @@
 }
 
 void RendererBlinkPlatformImpl::CacheMetadata(const blink::WebURL& url,
-                                              int64_t response_time,
+                                              base::Time response_time,
                                               const char* data,
                                               size_t size) {
   // Let the browser know we generated cacheable metadata for this resource. The
@@ -419,13 +420,13 @@
   // the processing of this resource.
   std::vector<char> copy(data, data + size);
   RenderThread::Get()->Send(
-      new RenderProcessHostMsg_DidGenerateCacheableMetadata(
-          url, base::Time::FromInternalValue(response_time), copy));
+      new RenderProcessHostMsg_DidGenerateCacheableMetadata(url, response_time,
+                                                            copy));
 }
 
 void RendererBlinkPlatformImpl::CacheMetadataInCacheStorage(
     const blink::WebURL& url,
-    int64_t response_time,
+    base::Time response_time,
     const char* data,
     size_t size,
     const blink::WebSecurityOrigin& cacheStorageOrigin,
@@ -436,8 +437,8 @@
   std::vector<char> copy(data, data + size);
   RenderThread::Get()->Send(
       new RenderProcessHostMsg_DidGenerateCacheableMetadataInCacheStorage(
-          url, base::Time::FromInternalValue(response_time), copy,
-          cacheStorageOrigin, cacheStorageCacheName.Utf8()));
+          url, response_time, copy, cacheStorageOrigin,
+          cacheStorageCacheName.Utf8()));
 }
 
 WebString RendererBlinkPlatformImpl::DefaultLocale() {
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 7884354..05ff5408 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -13,6 +13,7 @@
 #include "base/compiler_specific.h"
 #include "base/id_map.h"
 #include "base/macros.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "cc/blink/web_compositor_support_impl.h"
 #include "content/child/blink_platform_impl.h"
@@ -93,12 +94,12 @@
       std::unique_ptr<blink::WebMessagePortChannel>* channel2) override;
   blink::WebPrescientNetworking* PrescientNetworking() override;
   void CacheMetadata(const blink::WebURL&,
-                     int64_t,
+                     base::Time,
                      const char*,
                      size_t) override;
   void CacheMetadataInCacheStorage(
       const blink::WebURL&,
-      int64_t,
+      base::Time,
       const char*,
       size_t,
       const blink::WebSecurityOrigin& cacheStorageOrigin,
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 8dfe95e..20489a66 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -245,7 +245,7 @@
                           response.blob_size);
   }
   web_response->SetError(response.error);
-  web_response->SetResponseTime(response.response_time.ToInternalValue());
+  web_response->SetResponseTime(response.response_time);
   if (response.is_in_cache_storage) {
     web_response->SetCacheStorageCacheName(
         blink::WebString::FromUTF8(response.cache_storage_cache_name));
diff --git a/content/renderer/service_worker/service_worker_type_util.cc b/content/renderer/service_worker/service_worker_type_util.cc
index 9baae86..c6024b0 100644
--- a/content/renderer/service_worker/service_worker_type_util.cc
+++ b/content/renderer/service_worker/service_worker_type_util.cc
@@ -87,7 +87,7 @@
       web_response.StatusText().Utf8(), web_response.ResponseType(),
       GetHeaderMap(web_response), web_response.BlobUUID().Utf8(),
       web_response.BlobSize(), web_response.GetError(),
-      base::Time::FromInternalValue(web_response.ResponseTime()),
+      web_response.ResponseTime(),
       !web_response.CacheStorageCacheName().IsNull(),
       web_response.CacheStorageCacheName().Utf8(),
       GetHeaderList(web_response.CorsExposedHeaderNames()));
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 88a50c97..fe4acf6 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1671,6 +1671,7 @@
       "../renderer/media/webrtc/processed_local_audio_source_unittest.cc",
       "../renderer/media/webrtc/rtc_stats_unittest.cc",
       "../renderer/media/webrtc/stun_field_trial_unittest.cc",
+      "../renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc",
       "../renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc",
       "../renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc",
       "../renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc",
diff --git a/extensions/browser/guest_view/web_view/DEPS b/extensions/browser/guest_view/web_view/DEPS
new file mode 100644
index 0000000..c2974b7b
--- /dev/null
+++ b/extensions/browser/guest_view/web_view/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  ".*(test)\.(cc|h)$": [
+    "+media", # Tests need to be able to tweak media devices.
+  ]
+}
diff --git a/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc b/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
index c013665..1fab6b4 100644
--- a/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_media_access_apitest.cc
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/strings/stringprintf.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/guest_view/web_view/web_view_apitest.h"
 #include "extensions/test/extension_test_message_listener.h"
+#include "media/base/media_switches.h"
 
 namespace {
 
@@ -80,6 +82,14 @@
         base::StringPrintf("runTest('%s');", test_name.c_str())));
     ASSERT_TRUE(test_run_listener.WaitUntilSatisfied());
   }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Enable fake devices to make sure there is at least one device in the
+    // system. Otherwise, this test would fail on machines without physical
+    // media devices since getUserMedia fails early in those cases.
+    WebViewAPITest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
+  }
 };
 
 IN_PROC_BROWSER_TEST_F(WebViewMediaAccessAPITest, TestAllow) {
diff --git a/ios/chrome/browser/content_suggestions/BUILD.gn b/ios/chrome/browser/content_suggestions/BUILD.gn
index 40964dd..d4c92662 100644
--- a/ios/chrome/browser/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/content_suggestions/BUILD.gn
@@ -46,12 +46,14 @@
     "//ios/chrome/browser/ui/content_suggestions/cells:cells_ui",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
     "//ios/chrome/browser/ui/favicon",
+    "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/ntp:ntp_header",
     "//ios/chrome/browser/ui/ntp:ntp_internal",
     "//ios/chrome/browser/ui/reading_list",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/images",
+    "//ios/public/provider/chrome/browser/voice",
     "//ios/web",
     "//ui/base",
     "//ui/strings",
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.h b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.h
index 572e226..8d5f1d3 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.h
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.h
@@ -6,17 +6,20 @@
 #define IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COORDINATOR_H_
 
 #import "ios/chrome/browser/chrome_coordinator.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h"
 
 namespace ios {
 class ChromeBrowserState;
 }
 
+@class ContentSuggestionsHeaderController;
 @protocol UrlLoader;
 class WebStateList;
 
 // Coordinator to manage the Suggestions UI via a
 // ContentSuggestionsViewController.
-@interface ContentSuggestionsCoordinator : ChromeCoordinator
+@interface ContentSuggestionsCoordinator
+    : ChromeCoordinator<NewTabPagePanelProtocol>
 
 // BrowserState used to create the ContentSuggestionFactory.
 @property(nonatomic, assign) ios::ChromeBrowserState* browserState;
@@ -27,6 +30,11 @@
 // Whether the Suggestions UI is displayed. If this is true, start is a no-op.
 @property(nonatomic, readonly) BOOL visible;
 
+@property(nonatomic, strong, readonly)
+    ContentSuggestionsHeaderController* headerController;
+
+- (UIViewController*)viewController;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COORDINATOR_H_
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
index 172cf0f..8d87baa4 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
@@ -29,10 +29,12 @@
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h"
 #import "ios/chrome/browser/ui/ntp/google_landing_mediator.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/ui/ntp/notification_promo_whats_new.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/url_loader.h"
@@ -54,10 +56,15 @@
     ContentSuggestionsViewController* suggestionsViewController;
 @property(nonatomic, strong)
     ContentSuggestionsMediator* contentSuggestionsMediator;
-@property(nonatomic, strong)
-    ContentSuggestionsHeaderController* headerController;
 @property(nonatomic, strong) GoogleLandingMediator* googleLandingMediator;
 
+// Redefined as readwrite.
+@property(nonatomic, strong, readwrite)
+    ContentSuggestionsHeaderController* headerController;
+
+// |YES| if the fakebox header should be animated on scroll.
+@property(nonatomic, assign) BOOL animateHeader;
+
 // Opens the |URL| in a new tab |incognito| or not.
 - (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito;
 // Dismisses the |article|, removing it from the content service, and dismisses
@@ -80,6 +87,8 @@
 @synthesize googleLandingMediator = _googleLandingMediator;
 @synthesize webStateList = _webStateList;
 @synthesize dispatcher = _dispatcher;
+@synthesize delegate = _delegate;
+@synthesize animateHeader = _animateHeader;
 
 - (void)start {
   if (self.visible || !self.browserState) {
@@ -89,6 +98,7 @@
   }
 
   _visible = YES;
+  self.animateHeader = YES;
 
   ntp_snippets::ContentSuggestionsService* contentSuggestionsService =
       IOSChromeContentSuggestionsServiceFactory::GetForBrowserState(
@@ -147,6 +157,10 @@
   _visible = NO;
 }
 
+- (UIViewController*)viewController {
+  return self.suggestionsViewController;
+}
+
 #pragma mark - ContentSuggestionsCommands
 
 - (void)openReadingList {
@@ -360,6 +374,39 @@
   NOTREACHED();
 }
 
+#pragma mark - NewTabPagePanelProtocol
+
+- (CGFloat)alphaForBottomShadow {
+  // TODO(crbug.com/700375): implement this.
+  return 0;
+}
+
+- (UIView*)view {
+  return self.suggestionsViewController.view;
+}
+
+- (void)reload {
+  // TODO(crbug.com/700375): implement this.
+}
+
+- (void)wasShown {
+  // TODO(crbug.com/700375): implement this.
+}
+
+- (void)wasHidden {
+  // TODO(crbug.com/700375): implement this.
+}
+
+- (void)dismissModals {
+  // TODO(crbug.com/700375): implement this.
+}
+
+- (void)dismissKeyboard {
+}
+
+- (void)setScrollsToTop:(BOOL)enable {
+}
+
 #pragma mark - Private
 
 - (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito {
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_header_controller.h b/ios/chrome/browser/content_suggestions/content_suggestions_header_controller.h
index 3d3e743a..86e4e33 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_header_controller.h
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_header_controller.h
@@ -9,6 +9,8 @@
 
 #import "ios/chrome/browser/content_suggestions/content_suggestions_header_provider.h"
 #import "ios/chrome/browser/ui/ntp/google_landing_consumer.h"
+#import "ios/chrome/browser/ui/toolbar/toolbar_owner.h"
+#import "ios/public/provider/chrome/browser/voice/logo_animation_controller.h"
 
 @protocol ContentSuggestionsHeaderControllerDelegate;
 @protocol ContentSuggestionsHeaderControllerCommandHandler;
@@ -20,7 +22,10 @@
 // the interactions between the header and the collection, and the rest of the
 // application.
 @interface ContentSuggestionsHeaderController
-    : NSObject<ContentSuggestionsHeaderProvider, GoogleLandingConsumer>
+    : NSObject<ContentSuggestionsHeaderProvider,
+               GoogleLandingConsumer,
+               ToolbarOwner,
+               LogoAnimationControllerOwnerOwner>
 
 @property(nonatomic, weak) id<UrlLoader, OmniboxFocuser> dispatcher;
 @property(nonatomic, weak) id<ContentSuggestionsHeaderControllerDelegate>
@@ -29,6 +34,15 @@
     commandHandler;
 @property(nonatomic, assign) ReadingListModel* readingListModel;
 
+// |YES| when notifications indicate the omnibox is focused.
+@property(nonatomic, assign) BOOL omniboxFocused;
+
+// Whether the Google logo or doodle is being shown.
+@property(nonatomic, assign) BOOL logoIsShowing;
+
+// Update the iPhone fakebox's frame based on the current scroll view |offset|.
+- (void)updateSearchFieldForOffset:(CGFloat)offset;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_HEADER_CONTROLLER_H_
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_header_controller.mm b/ios/chrome/browser/content_suggestions/content_suggestions_header_controller.mm
index 34c5dcb..77f5336 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_header_controller.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_header_controller.mm
@@ -31,9 +31,6 @@
 
 @interface ContentSuggestionsHeaderController ()
 
-// Whether the Google logo or doodle is being shown.
-@property(nonatomic, assign) BOOL logoIsShowing;
-
 // |YES| if this consumer is has voice search enabled.
 @property(nonatomic, assign) BOOL voiceSearchIsEnabled;
 
@@ -62,9 +59,6 @@
 // ignored.
 @property(nonatomic, assign) BOOL isShowing;
 
-// |YES| when notifications indicate the omnibox is focused.
-@property(nonatomic, assign) BOOL omniboxFocused;
-
 // The number of tabs to show in the google landing fake toolbar.
 @property(nonatomic, assign) int tabCount;
 
@@ -111,6 +105,20 @@
 @synthesize logoIsShowing = _logoIsShowing;
 @synthesize logoFetched = _logoFetched;
 
+#pragma mark - Public
+
+- (void)updateSearchFieldForOffset:(CGFloat)offset {
+  NSArray* constraints =
+      @[ self.hintLabelLeadingConstraint, self.voiceTapTrailingConstraint ];
+
+  [self.headerView updateSearchFieldWidth:self.fakeOmniboxWidthConstraint
+                                   height:self.fakeOmniboxHeightConstraint
+                                topMargin:self.fakeOmniboxTopMarginConstraint
+                       subviewConstraints:constraints
+                            logoIsShowing:self.logoIsShowing
+                                forOffset:offset];
+}
+
 #pragma mark - ContentSuggestionsHeaderProvider
 
 - (UIView*)headerForWidth:(CGFloat)width {
@@ -258,6 +266,22 @@
   ]];
 }
 
+#pragma mark - ToolbarOwner
+
+- (ToolbarController*)relinquishedToolbarController {
+  return [self.headerView relinquishedToolbarController];
+}
+
+- (void)reparentToolbarController {
+  [self.headerView reparentToolbarController];
+}
+
+#pragma mark - LogoAnimationControllerOwnerOwner
+
+- (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner {
+  return [self.logoVendor logoAnimationControllerOwner];
+}
+
 #pragma mark - GoogleLandingConsumer
 
 - (void)setLogoIsShowing:(BOOL)logoIsShowing {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
index 9adf2d5..c89d739 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
@@ -364,7 +364,7 @@
       NSInteger orderedSectionIdentifier =
           SectionIdentifierForInfo(orderedSectionInfo);
       if (orderedSectionIdentifier == sectionIdentifier) {
-        continue;
+        break;
       }
       if ([model hasSectionForSectionIdentifier:orderedSectionIdentifier]) {
         sectionIndex++;
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index f045030..6629b0b 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -154,6 +154,7 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/content_suggestions",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/tabs",
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm
index a84eeed..09804fa 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm
@@ -15,6 +15,9 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/sync_sessions/synced_session.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/content_suggestions/content_suggestions_coordinator.h"
+#import "ios/chrome/browser/content_suggestions/content_suggestions_header_controller.h"
+#include "ios/chrome/browser/experimental_flags.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service.h"
@@ -111,31 +114,31 @@
 }  // anonymous namespace
 
 @interface NewTabPageController () {
-  ios::ChromeBrowserState* browserState_;  // weak.
-  __weak id<UrlLoader> loader_;
-  __weak id<NewTabPageControllerObserver> newTabPageObserver_;
-  BookmarkHomeTabletNTPController* bookmarkController_;
-  GoogleLandingViewController* googleLandingController_;
-  id<NewTabPagePanelProtocol> incognitoController_;
+  ios::ChromeBrowserState* _browserState;  // weak.
+  __weak id<UrlLoader> _loader;
+  __weak id<NewTabPageControllerObserver> _newTabPageObserver;
+  BookmarkHomeTabletNTPController* _bookmarkController;
+  GoogleLandingViewController* _googleLandingController;
+  id<NewTabPagePanelProtocol> _incognitoController;
   // The currently visible controller, one of the above.
-  __weak id<NewTabPagePanelProtocol> currentController_;
+  __weak id<NewTabPagePanelProtocol> _currentController;
 
-  GoogleLandingMediator* googleLandingMediator_;
+  GoogleLandingMediator* _googleLandingMediator;
 
-  RecentTabsPanelController* openTabsController_;
+  RecentTabsPanelController* _openTabsController;
   // Has the scrollView been initialized.
-  BOOL scrollInitialized_;
+  BOOL _scrollInitialized;
 
   // Dominant color cache. Key: (NSString*)url, val: (UIColor*)dominantColor.
-  __weak NSMutableDictionary* dominantColorCache_;  // Owned by bvc.
+  __weak NSMutableDictionary* _dominantColorCache;  // Owned by bvc.
 
   // Delegate to focus and blur the omnibox.
-  __weak id<OmniboxFocuser> focuser_;
+  __weak id<OmniboxFocuser> _focuser;
 
   // Delegate to fetch the ToolbarModel and current web state from.
-  __weak id<WebToolbarDelegate> webToolbarDelegate_;
+  __weak id<WebToolbarDelegate> _webToolbarDelegate;
 
-  TabModel* tabModel_;
+  TabModel* _tabModel;
 }
 
 // Load and bring panel into view.
@@ -179,14 +182,28 @@
 // to be used by the reuabled NTP panels.
 @property(nonatomic, weak) id dispatcher;
 
+// Panel displaying the "Home" view, with the logo and the fake omnibox.
+@property(nonatomic, strong) id<NewTabPagePanelProtocol> homePanel;
+
+// Coordinator for the ContentSuggestions.
+@property(nonatomic, strong)
+    ContentSuggestionsCoordinator* contentSuggestionsCoordinator;
+
+// Controller for the header of the Home panel.
+@property(nonatomic, strong) id<LogoAnimationControllerOwnerOwner, ToolbarOwner>
+    headerController;
+
 @end
 
 @implementation NewTabPageController
 
-@synthesize ntpView = ntpView_;
-@synthesize swipeRecognizerProvider = swipeRecognizerProvider_;
-@synthesize parentViewController = parentViewController_;
-@synthesize dispatcher = dispatcher_;
+@synthesize ntpView = _ntpView;
+@synthesize swipeRecognizerProvider = _swipeRecognizerProvider;
+@synthesize parentViewController = _parentViewController;
+@synthesize dispatcher = _dispatcher;
+@synthesize homePanel = _homePanel;
+@synthesize contentSuggestionsCoordinator = _contentSuggestionsCoordinator;
+@synthesize headerController = _headerController;
 
 - (id)initWithUrl:(const GURL&)url
                   loader:(id<UrlLoader>)loader
@@ -201,17 +218,17 @@
   self = [super initWithNibName:nil url:url];
   if (self) {
     DCHECK(browserState);
-    browserState_ = browserState;
-    loader_ = loader;
-    newTabPageObserver_ = ntpObserver;
-    parentViewController_ = parentViewController;
-    dispatcher_ = dispatcher;
-    focuser_ = focuser;
-    webToolbarDelegate_ = webToolbarDelegate;
-    tabModel_ = tabModel;
-    dominantColorCache_ = colorCache;
+    _browserState = browserState;
+    _loader = loader;
+    _newTabPageObserver = ntpObserver;
+    _parentViewController = parentViewController;
+    _dispatcher = dispatcher;
+    _focuser = focuser;
+    _webToolbarDelegate = webToolbarDelegate;
+    _tabModel = tabModel;
+    _dominantColorCache = colorCache;
     self.title = l10n_util::GetNSString(IDS_NEW_TAB_TITLE);
-    scrollInitialized_ = NO;
+    _scrollInitialized = NO;
 
     UIScrollView* scrollView =
         [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 412)];
@@ -219,14 +236,14 @@
                                      UIViewAutoresizingFlexibleHeight)];
     NewTabPageBar* tabBar =
         [[NewTabPageBar alloc] initWithFrame:CGRectMake(0, 412, 320, 48)];
-    ntpView_ = [[NewTabPageView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)
+    _ntpView = [[NewTabPageView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)
                                        andScrollView:scrollView
                                            andTabBar:tabBar];
     // TODO(crbug.com/607113): Merge view and ntpView.
-    self.view = ntpView_;
+    self.view = _ntpView;
     [tabBar setDelegate:self];
 
-    bool isIncognito = browserState_->IsOffTheRecord();
+    bool isIncognito = _browserState->IsOffTheRecord();
 
     NSString* incognito = l10n_util::GetNSString(IDS_IOS_NEW_TAB_INCOGNITO);
     NSString* mostVisited =
@@ -277,7 +294,7 @@
       if (!IsIPadIdiom()) {
         itemToDisplay = mostVisitedItem;
       } else {
-        PrefService* prefs = browserState_->GetPrefs();
+        PrefService* prefs = _browserState->GetPrefs();
         int shownPage = prefs->GetInteger(prefs::kNtpShownPage);
         shownPage = shownPage & ~INDEX_MASK;
 
@@ -303,19 +320,23 @@
   // delegate.
   self.ntpView.scrollView.delegate = nil;
 
-  [googleLandingMediator_ shutdown];
+  [_googleLandingMediator shutdown];
 
   // This is not an ideal place to put view controller contaimnent, rather a
   // //web -wasDismissed method on CRWNativeContent would be more accurate. If
   // CRWNativeContent leaks, this will not be called.
   // TODO(crbug.com/708319): Also call -removeFromParentViewController for
   // open tabs and incognito here.
-  [googleLandingController_ removeFromParentViewController];
-  [bookmarkController_ removeFromParentViewController];
+  [_googleLandingController removeFromParentViewController];
+  [_bookmarkController removeFromParentViewController];
+  [[self.contentSuggestionsCoordinator viewController]
+      removeFromParentViewController];
 
-  [googleLandingController_ setDelegate:nil];
-  [bookmarkController_ setDelegate:nil];
-  [openTabsController_ setDelegate:nil];
+  [self.contentSuggestionsCoordinator stop];
+
+  [self.homePanel setDelegate:nil];
+  [_bookmarkController setDelegate:nil];
+  [_openTabsController setDelegate:nil];
   [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
@@ -327,26 +348,28 @@
   // containment methods.
   // TODO(crbug.com/708319): Also call -willMoveToParentViewController:nil for
   // open tabs and incognito here.
-  [googleLandingController_ willMoveToParentViewController:nil];
-  [bookmarkController_ willMoveToParentViewController:nil];
+  [_googleLandingController willMoveToParentViewController:nil];
+  [_bookmarkController willMoveToParentViewController:nil];
+  [[self.contentSuggestionsCoordinator viewController]
+      willMoveToParentViewController:nil];
 }
 
 - (void)reload {
-  [currentController_ reload];
+  [_currentController reload];
   [super reload];
 }
 
 - (void)wasShown {
-  [currentController_ wasShown];
+  [_currentController wasShown];
   // Ensure that the NTP has the latest data when it is shown.
   [self reload];
   [self.ntpView.tabBar updateColorsForScrollView:self.ntpView.scrollView];
   [self.ntpView.tabBar
-      setShadowAlpha:[currentController_ alphaForBottomShadow]];
+      setShadowAlpha:[_currentController alphaForBottomShadow]];
 }
 
 - (void)wasHidden {
-  [currentController_ wasHidden];
+  [_currentController wasHidden];
 }
 
 - (BOOL)wantsKeyboardShield {
@@ -359,7 +382,7 @@
     return YES;
   // Always show the location bar hint text if the search engine is not Google.
   TemplateURLService* service =
-      ios::TemplateURLServiceFactory::GetForBrowserState(browserState_);
+      ios::TemplateURLServiceFactory::GetForBrowserState(_browserState);
   if (service) {
     const TemplateURL* defaultURL = service->GetDefaultSearchProvider();
     if (defaultURL &&
@@ -370,31 +393,31 @@
   }
 
   // Always return true when incognito.
-  if (browserState_->IsOffTheRecord())
+  if (_browserState->IsOffTheRecord())
     return YES;
 
   return [self selectedPanelID] != NewTabPage::kMostVisitedPanel;
 }
 
 - (void)dismissKeyboard {
-  [currentController_ dismissKeyboard];
+  [_currentController dismissKeyboard];
 }
 
 - (void)dismissModals {
-  [currentController_ dismissModals];
+  [_currentController dismissModals];
 }
 
 - (void)willUpdateSnapshot {
-  if ([currentController_ respondsToSelector:@selector(willUpdateSnapshot)]) {
-    [currentController_ willUpdateSnapshot];
+  if ([_currentController respondsToSelector:@selector(willUpdateSnapshot)]) {
+    [_currentController willUpdateSnapshot];
   }
 }
 
 #pragma mark -
 
 - (void)setSwipeRecognizerProvider:(id<CRWSwipeRecognizerProvider>)provider {
-  swipeRecognizerProvider_ = provider;
-  NSSet* recognizers = [swipeRecognizerProvider_ swipeRecognizers];
+  _swipeRecognizerProvider = provider;
+  NSSet* recognizers = [_swipeRecognizerProvider swipeRecognizers];
   for (UISwipeGestureRecognizer* swipeRecognizer in recognizers) {
     [self.ntpView.scrollView.panGestureRecognizer
         requireGestureRecognizerToFail:swipeRecognizer];
@@ -424,7 +447,7 @@
   [self.ntpView updateScrollViewContentSize];
   [self.ntpView.tabBar updateColorsForScrollView:scrollView];
 
-  scrollInitialized_ = YES;
+  _scrollInitialized = YES;
 }
 
 - (void)disableScroll {
@@ -437,7 +460,7 @@
 
 // Update selectedIndex and scroll position as the scroll view moves.
 - (void)scrollViewDidScroll:(UIScrollView*)scrollView {
-  if (!scrollInitialized_)
+  if (!_scrollInitialized)
     return;
 
   // Position is used to track the exact X position of the scroll view, whereas
@@ -491,7 +514,7 @@
     [self scrollToPanel:selectedItem animate:YES];
   }
 
-  [newTabPageObserver_ selectedPanelDidChange];
+  [_newTabPageObserver selectedPanelDidChange];
 }
 
 - (void)selectPanel:(NewTabPage::PanelIdentifier)panelType {
@@ -531,47 +554,67 @@
   BOOL created = NO;
   // Only load the controllers once.
   if (item.identifier == NewTabPage::kBookmarksPanel) {
-    if (!bookmarkController_) {
+    if (!_bookmarkController) {
       BookmarkControllerFactory* factory =
           [[BookmarkControllerFactory alloc] init];
-      bookmarkController_ =
-          [factory bookmarkPanelControllerForBrowserState:browserState_
-                                                   loader:loader_
-                                               colorCache:dominantColorCache_];
+      _bookmarkController =
+          [factory bookmarkPanelControllerForBrowserState:_browserState
+                                                   loader:_loader
+                                               colorCache:_dominantColorCache];
     }
-    panelController = bookmarkController_;
-    view = [bookmarkController_ view];
-    [bookmarkController_ setDelegate:self];
+    panelController = _bookmarkController;
+    view = [_bookmarkController view];
+    [_bookmarkController setDelegate:self];
   } else if (item.identifier == NewTabPage::kMostVisitedPanel) {
-    if (!googleLandingController_) {
-      googleLandingController_ = [[GoogleLandingViewController alloc] init];
-      [googleLandingController_ setDispatcher:self.dispatcher];
-      googleLandingMediator_ = [[GoogleLandingMediator alloc]
-          initWithConsumer:googleLandingController_
-              browserState:browserState_
-                dispatcher:self.dispatcher
-              webStateList:[tabModel_ webStateList]];
-      [googleLandingController_ setDataSource:googleLandingMediator_];
+    if (experimental_flags::IsSuggestionsUIEnabled()) {
+      if (!self.contentSuggestionsCoordinator) {
+        self.contentSuggestionsCoordinator =
+            [[ContentSuggestionsCoordinator alloc]
+                initWithBaseViewController:nil];
+        self.contentSuggestionsCoordinator.URLLoader = _loader;
+        self.contentSuggestionsCoordinator.browserState = _browserState;
+        self.contentSuggestionsCoordinator.dispatcher = self.dispatcher;
+        self.contentSuggestionsCoordinator.webStateList =
+            [_tabModel webStateList];
+        [self.contentSuggestionsCoordinator start];
+        self.headerController =
+            self.contentSuggestionsCoordinator.headerController;
+      }
+      panelController = [self.contentSuggestionsCoordinator viewController];
+      self.homePanel = self.contentSuggestionsCoordinator;
+    } else {
+      if (!_googleLandingController) {
+        _googleLandingController = [[GoogleLandingViewController alloc] init];
+        [_googleLandingController setDispatcher:self.dispatcher];
+        _googleLandingMediator = [[GoogleLandingMediator alloc]
+            initWithConsumer:_googleLandingController
+                browserState:_browserState
+                  dispatcher:self.dispatcher
+                webStateList:[_tabModel webStateList]];
+        [_googleLandingController setDataSource:_googleLandingMediator];
+        self.headerController = _googleLandingController;
+      }
+      panelController = _googleLandingController;
+      self.homePanel = _googleLandingController;
     }
-    panelController = googleLandingController_;
-    view = [googleLandingController_ view];
-    [googleLandingController_ setDelegate:self];
+    view = panelController.view;
+    [self.homePanel setDelegate:self];
   } else if (item.identifier == NewTabPage::kOpenTabsPanel) {
-    if (!openTabsController_)
-      openTabsController_ =
-          [[RecentTabsPanelController alloc] initWithLoader:loader_
-                                               browserState:browserState_];
+    if (!_openTabsController)
+      _openTabsController =
+          [[RecentTabsPanelController alloc] initWithLoader:_loader
+                                               browserState:_browserState];
     // TODO(crbug.com/708319): Also set panelController for opentabs here.
-    view = [openTabsController_ view];
-    [openTabsController_ setDelegate:self];
+    view = [_openTabsController view];
+    [_openTabsController setDelegate:self];
   } else if (item.identifier == NewTabPage::kIncognitoPanel) {
-    if (!incognitoController_)
-      incognitoController_ =
-          [[IncognitoPanelController alloc] initWithLoader:loader_
-                                              browserState:browserState_
-                                        webToolbarDelegate:webToolbarDelegate_];
+    if (!_incognitoController)
+      _incognitoController =
+          [[IncognitoPanelController alloc] initWithLoader:_loader
+                                              browserState:_browserState
+                                        webToolbarDelegate:_webToolbarDelegate];
     // TODO(crbug.com/708319): Also set panelController for incognito here.
-    view = [incognitoController_ view];
+    view = [_incognitoController view];
   } else {
     NOTREACHED();
     return NO;
@@ -620,7 +663,7 @@
     }
   }
 
-  if (currentController_ == nil) {
+  if (_currentController == nil) {
     [self updateCurrentController:item index:index];
   }
 }
@@ -652,44 +695,43 @@
                           index:(NSUInteger)index {
   if (!IsIPadIdiom() && (item.identifier == NewTabPage::kBookmarksPanel ||
                          item.identifier == NewTabPage::kOpenTabsPanel)) {
-    // Don't update |currentController_| for iPhone since Bookmarks and Recent
+    // Don't update |_currentController| for iPhone since Bookmarks and Recent
     // Tabs are presented in a modal view controller.
     return;
   }
 
-  id<NewTabPagePanelProtocol> oldController = currentController_;
+  id<NewTabPagePanelProtocol> oldController = _currentController;
   self.ntpView.tabBar.selectedIndex = index;
   if (item.identifier == NewTabPage::kBookmarksPanel)
-    currentController_ = bookmarkController_;
+    _currentController = _bookmarkController;
   else if (item.identifier == NewTabPage::kMostVisitedPanel)
-    currentController_ = googleLandingController_;
+    _currentController = self.homePanel;
   else if (item.identifier == NewTabPage::kOpenTabsPanel)
-    currentController_ = openTabsController_;
+    _currentController = _openTabsController;
   else if (item.identifier == NewTabPage::kIncognitoPanel)
-    currentController_ = incognitoController_;
+    _currentController = _incognitoController;
 
-  [bookmarkController_
-      setScrollsToTop:(currentController_ == bookmarkController_)];
-  [googleLandingController_
-      setScrollsToTop:(currentController_ == googleLandingController_)];
-  [openTabsController_
-      setScrollsToTop:(currentController_ == openTabsController_)];
+  [_bookmarkController
+      setScrollsToTop:(_currentController == _bookmarkController)];
+  [self.homePanel setScrollsToTop:(_currentController == self.homePanel)];
+  [_openTabsController
+      setScrollsToTop:(_currentController == _openTabsController)];
   [self.ntpView.tabBar
-      setShadowAlpha:[currentController_ alphaForBottomShadow]];
+      setShadowAlpha:[_currentController alphaForBottomShadow]];
 
-  if (oldController != currentController_) {
-    [currentController_ wasShown];
+  if (oldController != _currentController) {
+    [_currentController wasShown];
     [oldController wasHidden];
   }
 }
 
 - (void)panelChanged:(NewTabPageBarItem*)item {
-  if (browserState_->IsOffTheRecord())
+  if (_browserState->IsOffTheRecord())
     return;
 
   // Save state and update metrics. Intentionally omitting a metric for the
   // Incognito panel.
-  PrefService* prefs = browserState_->GetPrefs();
+  PrefService* prefs = _browserState->GetPrefs();
   if (item.identifier == NewTabPage::kBookmarksPanel) {
     base::RecordAction(UserMetricsAction("MobileNTPSwitchToBookmarks"));
     prefs->SetInteger(prefs::kNtpShownPage, BOOKMARKS_PAGE_ID);
@@ -714,31 +756,31 @@
 #pragma mark - LogoAnimationControllerOwnerOwner
 
 - (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner {
-  return [googleLandingController_ logoAnimationControllerOwner];
+  return [self.headerController logoAnimationControllerOwner];
 }
 
 #pragma mark -
 #pragma mark ToolbarOwner
 
 - (ToolbarController*)relinquishedToolbarController {
-  return [googleLandingController_ relinquishedToolbarController];
+  return [self.headerController relinquishedToolbarController];
 }
 
 - (void)reparentToolbarController {
-  [googleLandingController_ reparentToolbarController];
+  [self.headerController reparentToolbarController];
 }
 
 - (CGFloat)toolbarHeight {
   // If the google landing controller is nil, there is no toolbar visible in the
   // native content view, finally there is no toolbar on iPad.
-  return googleLandingController_ && !IsIPadIdiom() ? kToolbarHeight : 0.0;
+  return self.headerController && !IsIPadIdiom() ? kToolbarHeight : 0.0;
 }
 
 #pragma mark - NewTabPagePanelControllerDelegate
 
 - (void)updateNtpBarShadowForPanelController:
     (id<NewTabPagePanelProtocol>)ntpPanelController {
-  if (currentController_ != ntpPanelController)
+  if (_currentController != ntpPanelController)
     return;
   [self.ntpView.tabBar
       setShadowAlpha:[ntpPanelController alphaForBottomShadow]];
@@ -749,19 +791,19 @@
 @implementation NewTabPageController (TestSupport)
 
 - (id<NewTabPagePanelProtocol>)currentController {
-  return currentController_;
+  return _currentController;
 }
 
 - (BookmarkHomeTabletNTPController*)bookmarkController {
-  return bookmarkController_;
+  return _bookmarkController;
 }
 
 - (GoogleLandingViewController*)googleLandingController {
-  return googleLandingController_;
+  return _googleLandingController;
 }
 
 - (id<NewTabPagePanelProtocol>)incognitoController {
-  return incognitoController_;
+  return _incognitoController;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 7635b63..8381db4 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -179,6 +179,7 @@
     "//ios/public/provider/chrome/browser/signin",
     "//ios/public/provider/chrome/browser/user_feedback",
     "//ios/public/provider/chrome/browser/voice",
+    "//ios/shared/chrome/browser/ui/settings",
     "//ios/third_party/material_components_ios",
     "//ios/third_party/material_roboto_font_loader_ios",
     "//ios/web",
diff --git a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.mm b/ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.mm
index ecfbcfb..4eeab2b8 100644
--- a/ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill_credit_card_edit_collection_view_controller.mm
@@ -155,21 +155,21 @@
       toSectionWithIdentifier:SectionIdentifierFields];
 
   // Card number (PAN).
-  AutofillEditItem* cardNumberitem =
+  AutofillEditItem* cardNumberItem =
       [[AutofillEditItem alloc] initWithType:ItemTypeCardNumber];
-  cardNumberitem.textFieldName =
+  cardNumberItem.textFieldName =
       l10n_util::GetNSString(IDS_IOS_AUTOFILL_CARD_NUMBER);
   // Never show full card number for Wallet cards, even if copied locally.
-  cardNumberitem.textFieldValue =
+  cardNumberItem.textFieldValue =
       autofill::IsCreditCardLocal(_creditCard)
           ? base::SysUTF16ToNSString(_creditCard.number())
-          : base::SysUTF16ToNSString(_creditCard.LastFourDigits());
-  cardNumberitem.textFieldEnabled = isEditing;
-  cardNumberitem.autofillUIType = AutofillUITypeCreditCardNumber;
-  cardNumberitem.keyboardType = UIKeyboardTypeNumberPad;
-  cardNumberitem.identifyingIcon =
-      [self cardTypeIconFromCardNumber:cardNumberitem.textFieldValue];
-  [model addItem:cardNumberitem
+          : base::SysUTF16ToNSString(_creditCard.NetworkAndLastFourDigits());
+  cardNumberItem.textFieldEnabled = isEditing;
+  cardNumberItem.autofillUIType = AutofillUITypeCreditCardNumber;
+  cardNumberItem.keyboardType = UIKeyboardTypeNumberPad;
+  cardNumberItem.identifyingIcon =
+      [self cardTypeIconFromNetwork:_creditCard.network().c_str()];
+  [model addItem:cardNumberItem
       toSectionWithIdentifier:SectionIdentifierFields];
 
   // Expiration month.
@@ -232,7 +232,9 @@
     NSString* updatedText =
         [textField.text stringByReplacingCharactersInRange:range
                                                 withString:newText];
-    item.identifyingIcon = [self cardTypeIconFromCardNumber:updatedText];
+    const char* network = autofill::CreditCard::GetCardNetwork(
+        base::SysNSStringToUTF16(updatedText));
+    item.identifyingIcon = [self cardTypeIconFromNetwork:network];
     // Update the cell.
     [self reconfigureCellsForItems:@[ item ]];
   }
@@ -300,9 +302,7 @@
 
 #pragma mark - Helper Methods
 
-- (UIImage*)cardTypeIconFromCardNumber:(NSString*)cardNumber {
-  const char* network = autofill::CreditCard::GetCardNetwork(
-      base::SysNSStringToUTF16(cardNumber));
+- (UIImage*)cardTypeIconFromNetwork:(const char*)network {
   if (network != autofill::kGenericCard) {
     int resourceID =
         autofill::data_util::GetPaymentRequestData(network).icon_resource_id;
diff --git a/ios/chrome/browser/ui/settings/settings_collection_view_controller.h b/ios/chrome/browser/ui/settings/settings_collection_view_controller.h
index 0088828..1b43cab 100644
--- a/ios/chrome/browser/ui/settings/settings_collection_view_controller.h
+++ b/ios/chrome/browser/ui/settings/settings_collection_view_controller.h
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_root_collection_view_controller.h"
 
+@protocol SettingsMainPageCommands;
 @class SigninInteractionController;
 namespace ios {
 class ChromeBrowserState;
@@ -37,6 +38,9 @@
 @property(weak, nonatomic, readonly)
     SigninInteractionController* signinInteractionController;
 
+// This controller's dispatcher. By default it is the controller itself.
+@property(weak, nonatomic) id<SettingsMainPageCommands> dispatcher;
+
 // Initializes a new SettingsCollectionViewController. |browserState| must not
 // be nil and must not be an off-the-record browser state.
 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
diff --git a/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
index a6988c9..1d248518 100644
--- a/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_collection_view_controller.mm
@@ -73,6 +73,7 @@
 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
 #import "ios/public/provider/chrome/browser/signin/signin_resources_provider.h"
 #include "ios/public/provider/chrome/browser/voice/voice_search_prefs.h"
+#import "ios/shared/chrome/browser/ui/settings/settings_main_page_commands.h"
 #import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -176,13 +177,14 @@
 
 #pragma mark - SettingsCollectionViewController
 
-@interface SettingsCollectionViewController ()<SettingsControllerProtocol,
-                                               SyncObserverModelBridge,
+@interface SettingsCollectionViewController ()<BooleanObserver,
                                                ChromeIdentityServiceObserver,
-                                               BooleanObserver,
                                                PrefObserverDelegate,
+                                               SettingsControllerProtocol,
+                                               SettingsMainPageCommands,
                                                SigninPromoViewConsumer,
-                                               SigninPromoViewDelegate> {
+                                               SigninPromoViewDelegate,
+                                               SyncObserverModelBridge> {
   // The current browser state that hold the settings. Never off the record.
   ios::ChromeBrowserState* _browserState;  // weak
 
@@ -231,6 +233,7 @@
 @end
 
 @implementation SettingsCollectionViewController
+@synthesize dispatcher = _dispatcher;
 
 #pragma mark Initialization
 
@@ -275,6 +278,8 @@
     _prefObserverBridge->ObserveChangesForPreference(
         autofill::prefs::kAutofillEnabled, &_prefChangeRegistrar);
 
+    _dispatcher = self;
+
     [self loadModel];
   }
   return self;
@@ -761,7 +766,7 @@
       // and only the switch is tappable.
       break;
     case ItemTypeCellCatalog:
-      controller = [[MaterialCellCatalogViewController alloc] init];
+      [self.dispatcher showMaterialCellCatalog];
       break;
     default:
       break;
@@ -982,6 +987,14 @@
   [self reloadData];
 }
 
+#pragma mark Material Cell Catalog
+
+- (void)showMaterialCellCatalog {
+  [self.navigationController
+      pushViewController:[[MaterialCellCatalogViewController alloc] init]
+                animated:YES];
+}
+
 #pragma mark NotificationBridgeDelegate
 
 - (void)onSignInStateChanged {
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.h b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
index de84b6b..c7c99cf 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.h
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.h
@@ -124,6 +124,16 @@
 newAutofillController:(ios::ChromeBrowserState*)browserState
              delegate:(id<SettingsNavigationControllerDelegate>)delegate;
 
+// Initializes the UINavigationController with |rootViewController|.
+- (instancetype)
+initWithRootViewController:(UIViewController*)rootViewController
+              browserState:(ios::ChromeBrowserState*)browserState
+                  delegate:(id<SettingsNavigationControllerDelegate>)delegate
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithRootViewController:(UIViewController*)rootViewController
+    NS_UNAVAILABLE;
+
 // Returns a new Done button for a UINavigationItem which will call
 // closeSettings when it is pressed. Should only be called by view controllers
 // owned by SettingsNavigationController.
@@ -146,15 +156,4 @@
 
 @end
 
-@interface SettingsNavigationController (ExposedForTesting)
-
-// Initializes the UINavigationController with |rootViewController|.
-// User of this class should not call the normal |initWithRootViewController|.
-- (instancetype)
-initWithRootViewController:(UIViewController*)rootViewController
-              browserState:(ios::ChromeBrowserState*)browserState
-                  delegate:(id<SettingsNavigationControllerDelegate>)delegate;
-
-@end
-
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_NAVIGATION_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index 797b617..8eb7c10 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -288,7 +288,9 @@
                   delegate:(id<SettingsNavigationControllerDelegate>)delegate {
   DCHECK(browserState);
   DCHECK(!browserState->IsOffTheRecord());
-  self = [super initWithRootViewController:rootViewController];
+  self = rootViewController
+             ? [super initWithRootViewController:rootViewController]
+             : [super init];
   if (self) {
     mainBrowserState_ = browserState;
     delegate_ = delegate;
diff --git a/ios/clean/README.md b/ios/clean/README.md
index 80dffe1c..8c41156 100644
--- a/ios/clean/README.md
+++ b/ios/clean/README.md
@@ -1,6 +1,214 @@
-# Application Steps
+# iOS Chrome Architecture
 
------
-**The files in this directory are only used in the new architecture for iOS
-Chrome**
------
+`/ios/clean/` houses a revised and updated architecture for iOS Chrome's UI. iOS
+is in the process of migrating into this new architecture. During this process,
+code in `/ios/clean` should not depend on code in `ios/chrome/browser/ui`, or
+vice-versa; instead code that is shared between the two will be moved into
+`ios/shared`.
+
+This document is a general overview of the fundamental concepts of the new
+architecture. More specific documentation is located alongside the
+implementations.
+
+[TOC]
+
+## Goals and core principles.
+
+The goals of the new architecture, briefly, are to:
+
+* Allow for radical user interface experimentation and changes.
+* Make development and maintenance of features easier.
+* Cut the Gordian knots of the existing architecture, removing the need for
+  [unmaintainable monster classes](/ios/chrome/browser/ui/BrowserViewController.mm)
+  that accrete functionality.
+* Keep up with the evolution if iOS by providing a structure in which the
+  affordances provided by Cocoa Touch can be used without radical changes.
+
+In order to meet these goals, the new architecture is designed according to
+these three core principles:
+
+* **Strong Decoupling.** Components of the browser, especially UI components,
+  should not depend on other components explicitly. Where components interact,
+  they should do so through protocols or other abstractions that prevent
+  tight coupling.
+* **Strong Encapsulation.** A complement to decoupling is encapsulation.
+  Components of the browser should expose little specifics about their
+  implementation or state. Public APIs should be as small as possible.
+  Architectural commonalities (for example, the use of a common superclass for
+  coordinators) will mean that the essential interfaces for complex components
+  can be both small and common across many implementations. Overall the
+  combination of decoupling and encapsulation means that components of
+  Chrome can be rearranged or removed without impacting the rest of the
+  application.
+* **Separation of layers.** Objects should operate at a specific layer, and
+  their interactions with objects in other layers should be well-defined. We
+  distinguish model, coordinator, and UI layers.
+
+## Structure.
+
+### View controllers.
+
+Since this is an iOS application, view controllers remain a fundamental building
+block of the user interface. However, the scope of a view controller’s
+responsibilities are strongly constrained. View controllers are responsible for
+the display and user interactions of a specific piece of user interface. They
+may contain, or be contained in, other view controllers in order to compose the
+complete UI that a user interacts with.
+
+For example, when viewing a web page in a Chrome tab, the web page contents, the
+toolbar above the page, and the location bar in the toolbar are each separate
+view controllers, and a fourth view controller is responsible for composing them
+together. View controllers do not directly interact with each other or with any
+model-layer services.
+
+### Coordinators.
+
+Since view controllers are heavily encapsulated, another object is responsible
+for creating them and connecting them to other objects. This object is a
+coordinator, and in this architecture all coordinators are subclasses of
+[BrowserCoordinator](/ios/shared/chrome/browser/ui/coordinators/). Coordinators
+exist in a hierarchy, roughly parallel to the view controller hierarchy; each
+coordinator can have multiple child coordinators, and there is a single root
+coordinator that is created when the application launches.
+
+The primary role of coordinators is to create and configure their view
+controller, connect it to a mediator and/or a dispatcher (see below), and to
+handle the creation and lifecycle of any child view controllers. Coordinators
+shouldn’t directly interact with model-layer services, and should contain
+little if any “business logic”.
+
+### Mediators.
+
+Mediators encapsulate the interactions between the model layer and the user
+interface. They are created by coordinators and are handed connections to any
+model-layer services they need, as well as to a “consumer” which they will
+push user interface configuration and updates into.
+
+The consumer is typically a view controller, and there is a consumer protocol
+defined as part of the UI layer that the view controller adopts for this
+purpose. A view controller that integrates UI updates from multiple model
+services may be consuming from multiple mediators and may thus implement
+multiple consumer protocols.
+
+Mediators can be either Objective-C or C++ objects, and there isn't a universal
+superclass for them.
+
+### Service Objects.
+
+Coordinators are created with access to a number of “service objects” that they
+may connect to mediators and view controllers. By default a coordinator passes
+pointers to all of these objects into their children.
+
+The inventory of service objects includes:
+
+* **[WebStateList:](/ios/chrome/browser/web_state_list/)** An observable,
+  mutable, ordered collection of [WebState](/ios/public/web_state/)s. A WebState
+  is the model-layer representation of a browser tab.
+  
+* **BrowserState:** The [ChromeBrowserState](/ios/chrome/browser/chrome_browser/state/)
+  object as used in the old architecture.
+  
+* **[Dispatcher:](/ios/shared/chrome/browser/ui/commands/)** A decoupled
+  interface over which application-level method calls may be made; see below.
+  
+* **[Broadcaster:](/ios/shared/chrome/browser/ui/broadcaster)** A decoupled
+  interface over which some properties of the user interface may be observed.
+
+Some of these service objects are contained inside a [Browser](/ios/shared/chrome/browser/ui/browser_list)
+object, which represents a collection of user tabs, equivalent to a window on
+desktop platforms.
+
+The Dispatcher merits special discussion. The role of the dispatcher is to
+opaquely route Objective-C method calls. The dispatcher can register objects to
+handle specific method calls, or an entire protocol. These protocols are
+conventionally called [‘commands’](/ios/clean/chrome/browser/ui/commands/).
+
+Coordinators typically register themselves or the mediators they own to handle
+commands related to the area of their responsibility. The dispatcher is handed
+into the view controller that a coordinator creates in the guise of a generic
+Objective-C object that conforms to the command protocols that the view
+controller needs. This allows the view controller to call methods on objects
+that its coordinator doesn’t directly know about.
+
+## Strictures.
+
+Within this basic structure, and given the overall principles of decoupling,
+encapsulation, and separation of layers, there are a number of restrictions that
+all of these classes of objects should adhere to.
+
+### View Controllers
+
+* Cannot directly create or interact with any other specific classes of view
+  controllers. View controllers that contain other view controllers should have
+  public properties or methods for these embedded view controllers to be
+  provided; internally they are limited to adding, removing, positioning, and
+  resizing any such view controllers.
+
+* Cannot depend on any model-layer objects or types.
+
+* Should only operate on the main thread.
+
+* Should not be their own transitioning delegates; an object from the
+  coordinator layer (usually the coordinator) should handle that.
+
+* Should handle action methods for their own UI, or, if appropriate, have the
+  command-handling object they are given (a pointer to the dispatcher) be the
+  target for control actions.
+ 
+* Should only take input from other parts of the application by having them
+  pushed over the consumer interface (or interfaces).
+
+* Should only send messages to the rest of the application directly via the
+  dispatcher.
+
+### The UI layer
+
+View controllers, the consumer protocols they adopt, and associated classes and
+assets constitute the “UI layer” of the application, and it is a separate source
+set in the gn configurations. The UI layer should have no dependencies on
+coordinators, mediators, or model objects.
+
+### Coordinators
+
+* Should only pass a minimal set of service objects into mediators or other
+  objects that require them. Rather than passing a `Browser` or `BrowserState`,
+  they should pass specific services or sub-objects. Rather than passing the
+  Dispatcher, they should cast it into an `i`d object that conforms to the
+  protocol(s) the object requires.
+ 
+* Should only handle command method whose execution is mostly a matter of
+  creating, starting, or stopping other coordinators. Interactions with
+  model-layer objects should be handled by mediators. Coordinators can have
+  methods dispatch to their mediator.
+
+* Should only operate on the main thread.
+
+* If custom view controller presentation or animation is required, the
+  coordinator can be the presented view controller’s transitioning delegate, or
+  it can create a helper object for that.
+
+### Mediators
+
+* Should have any services they need passed into them by the coordinator. They
+  shouldn’t depend on singletons, or on aggregate service objects such as
+  `BrowserState`.
+
+* Can communicate with their consumer _only_ over the consumer interface and
+  _only_ on the main thread. It is the mediator’s responsibility to manage any
+  model updates that occur on other threads and consolidate them into updates
+  sent to the consumer on the main thread.
+
+* Can only send Foundation value types, collections, or data classes containing
+  them to their consumer.
+ 
+* Cannot expose any kind of delegate interface for the consumer to call.
+  Mediators push updates to consumers, and view controllers call command methods
+  to induce model changes.
+  
+### Dispatchers
+
+* Should only be used for "command-ish" methods. The dispatcher shouldn’t be
+  used to implement what are functionally delegation, observation, or
+  notification relationships. Other tools (such as the Broadcaster, and
+  model-layer observers) exist for those purposes.
+
diff --git a/ios/clean/chrome/browser/ui/settings/BUILD.gn b/ios/clean/chrome/browser/ui/settings/BUILD.gn
index e5b4bf6..da44106 100644
--- a/ios/clean/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/settings/BUILD.gn
@@ -4,18 +4,43 @@
 
 source_set("settings") {
   sources = [
+    "material_cell_catalog_coordinator.h",
+    "material_cell_catalog_coordinator.mm",
     "settings_coordinator.h",
     "settings_coordinator.mm",
+    "settings_main_page_coordinator.h",
+    "settings_main_page_coordinator.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
 
   deps = [
+    "//base:base",
+    "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/ui/settings",
     "//ios/clean/chrome/browser/ui/actions",
     "//ios/clean/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/browser_list",
     "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
+    "//ios/shared/chrome/browser/ui/settings",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "material_cell_catalog_coordinator_unittest.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":settings",
+    "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/ui/settings",
+    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/shared/chrome/browser/ui/coordinators:test_support",
+    "//testing/gtest",
   ]
 }
diff --git a/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.h b/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.h
new file mode 100644
index 0000000..2f1db71
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.h
@@ -0,0 +1,15 @@
+// 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 IOS_CLEAN_CHROME_BROWSER_UI_SETTINGS_MATERIAL_CELL_CATALOG_COORDINATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_SETTINGS_MATERIAL_CELL_CATALOG_COORDINATOR_H_
+
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator.h"
+
+// A coordinator for the Material Cell Catalog in Settings. That screen is
+// usually pushed onto a navigation controller.
+@interface MaterialCellCatalogCoordinator : BrowserCoordinator
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_SETTINGS_MATERIAL_CELL_CATALOG_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.mm b/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.mm
new file mode 100644
index 0000000..f23ed65
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.mm
@@ -0,0 +1,25 @@
+// 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.
+
+#import "ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.h"
+
+#import "ios/chrome/browser/ui/settings/material_cell_catalog_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface MaterialCellCatalogCoordinator ()
+@property(nonatomic, strong) UIViewController* viewController;
+@end
+
+@implementation MaterialCellCatalogCoordinator
+@synthesize viewController = _viewController;
+
+- (void)start {
+  self.viewController = [[MaterialCellCatalogViewController alloc] init];
+  [super start];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator_unittest.mm
new file mode 100644
index 0000000..f860231
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator_unittest.mm
@@ -0,0 +1,20 @@
+// 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.
+
+#import "ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.h"
+
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+typedef BrowserCoordinatorTest MaterialCellCatalogCoordinatorTest;
+
+TEST_F(MaterialCellCatalogCoordinatorTest, Start) {
+  MaterialCellCatalogCoordinator* coordinator =
+      [[MaterialCellCatalogCoordinator alloc] init];
+  [coordinator start];
+}
diff --git a/ios/clean/chrome/browser/ui/settings/settings_coordinator.h b/ios/clean/chrome/browser/ui/settings/settings_coordinator.h
index e1fbadc..502733e 100644
--- a/ios/clean/chrome/browser/ui/settings/settings_coordinator.h
+++ b/ios/clean/chrome/browser/ui/settings/settings_coordinator.h
@@ -11,6 +11,7 @@
 
 // A coordinator for the Settings UI, which is usually presented modally
 // on top of whatever other UI is currently active.
+// The Browser of this coordinator must be non-incognito.
 @interface SettingsCoordinator : BrowserCoordinator
 @end
 
diff --git a/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm b/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
index 9712317..751023cf 100644
--- a/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/settings/settings_coordinator.mm
@@ -4,8 +4,11 @@
 
 #import "ios/clean/chrome/browser/ui/settings/settings_coordinator.h"
 
+#include "base/logging.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/clean/chrome/browser/ui/commands/settings_commands.h"
+#import "ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.h"
 #import "ios/shared/chrome/browser/ui/browser_list/browser.h"
 #import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
@@ -14,7 +17,20 @@
 #error "This file requires ARC support."
 #endif
 
-@interface SettingsCoordinator ()<SettingsNavigationControllerDelegate>
+namespace {
+// Traverses the coordinator hierarchy in a pre-order depth-first traversal.
+// |block| is called on all children of |coordinator|.
+void TraverseCoordinatorHierarchy(BrowserCoordinator* coordinator,
+                                  void (^block)(BrowserCoordinator*)) {
+  for (BrowserCoordinator* child in coordinator.children) {
+    block(child);
+    TraverseCoordinatorHierarchy(child, block);
+  }
+}
+}  // namespace
+
+@interface SettingsCoordinator ()<SettingsNavigationControllerDelegate,
+                                  UINavigationControllerDelegate>
 @property(nonatomic, strong) SettingsNavigationController* viewController;
 @end
 
@@ -24,9 +40,18 @@
 #pragma mark - BrowserCoordinator
 
 - (void)start {
-  self.viewController = [SettingsNavigationController
-      newSettingsMainControllerWithBrowserState:self.browser->browser_state()
-                                       delegate:self];
+  DCHECK(!self.browser->browser_state()->IsOffTheRecord());
+  SettingsMainPageCoordinator* mainPageCoordinator =
+      [[SettingsMainPageCoordinator alloc] init];
+  [self addChildCoordinator:mainPageCoordinator];
+  [mainPageCoordinator start];
+  self.viewController = [[SettingsNavigationController alloc]
+      initWithRootViewController:mainPageCoordinator.viewController
+                    browserState:self.browser->browser_state()
+                        delegate:self];
+  self.viewController.delegate = self;
+  mainPageCoordinator.viewController.navigationItem.rightBarButtonItem =
+      [self.viewController doneButton];
   [super start];
 }
 
@@ -48,4 +73,25 @@
   [static_cast<id>(self.browser->dispatcher()) closeSettings];
 }
 
+#pragma mark - UINavigationControllerDelegate
+
+- (void)navigationController:(UINavigationController*)navigationController
+       didShowViewController:(UIViewController*)viewController
+                    animated:(BOOL)animated {
+  UIViewController* fromViewController =
+      [navigationController.transitionCoordinator
+          viewControllerForKey:UITransitionContextFromViewControllerKey];
+  if ([navigationController.viewControllers
+          containsObject:fromViewController]) {
+    return;
+  }
+  // Clean the coordinator hierarchy from coordinators whose view controller was
+  // popped via the UI (system back button or swipe to go back.)
+  TraverseCoordinatorHierarchy(self, ^(BrowserCoordinator* child) {
+    if ([child.viewController isEqual:fromViewController]) {
+      [child stop];
+    }
+  });
+}
+
 @end
diff --git a/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.h b/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.h
new file mode 100644
index 0000000..bdb8d445
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.h
@@ -0,0 +1,15 @@
+// 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 IOS_CLEAN_CHROME_BROWSER_UI_SETTINGS_SETTINGS_MAIN_PAGE_COORDINATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_SETTINGS_SETTINGS_MAIN_PAGE_COORDINATOR_H_
+
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator.h"
+
+// A coordinator for the Settings main screen. That screen is usually pushed
+// onto a navigation controller.
+@interface SettingsMainPageCoordinator : BrowserCoordinator
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_SETTINGS_SETTINGS_MAIN_PAGE_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.mm b/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.mm
new file mode 100644
index 0000000..89ef68f9
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.mm
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/settings/settings_main_page_coordinator.h"
+
+#include "base/logging.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
+#import "ios/clean/chrome/browser/ui/settings/material_cell_catalog_coordinator.h"
+#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+#import "ios/shared/chrome/browser/ui/settings/settings_main_page_commands.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface SettingsMainPageCoordinator ()<SettingsMainPageCommands>
+@property(nonatomic, strong) SettingsCollectionViewController* viewController;
+@end
+
+@implementation SettingsMainPageCoordinator
+@synthesize viewController = _viewController;
+
+- (void)start {
+  DCHECK(!self.browser->browser_state()->IsOffTheRecord());
+  self.viewController = [[SettingsCollectionViewController alloc]
+      initWithBrowserState:self.browser->browser_state()];
+  [self.browser->dispatcher()
+      startDispatchingToTarget:self
+                   forProtocol:@protocol(SettingsMainPageCommands)];
+  self.viewController.dispatcher = static_cast<id>(self.browser->dispatcher());
+  [super start];
+}
+
+#pragma mark - SettingsMainPageViewControllerDelegate
+
+- (void)showMaterialCellCatalog {
+  MaterialCellCatalogCoordinator* coordinator =
+      [[MaterialCellCatalogCoordinator alloc] init];
+  [self addChildCoordinator:coordinator];
+  [coordinator start];
+}
+
+#pragma mark - BrowserCoordinator
+
+- (void)childCoordinatorDidStart:(BrowserCoordinator*)coordinator {
+  [self.viewController.navigationController
+      pushViewController:coordinator.viewController
+                animated:YES];
+}
+
+- (void)childCoordinatorWillStop:(BrowserCoordinator*)childCoordinator {
+  [self.viewController.navigationController
+      popToViewController:self.viewController
+                 animated:YES];
+}
+
+@end
diff --git a/ios/clean/chrome/test/BUILD.gn b/ios/clean/chrome/test/BUILD.gn
index b68f0ac..f4a6f6aa 100644
--- a/ios/clean/chrome/test/BUILD.gn
+++ b/ios/clean/chrome/test/BUILD.gn
@@ -27,6 +27,7 @@
     "//ios/clean/chrome/browser/ui/omnibox:unit_tests",
     "//ios/clean/chrome/browser/ui/recent_tabs:unit_tests",
     "//ios/clean/chrome/browser/ui/root:unit_tests",
+    "//ios/clean/chrome/browser/ui/settings:unit_tests",
     "//ios/clean/chrome/browser/ui/tab:unit_tests",
     "//ios/clean/chrome/browser/ui/tab_collection:unit_tests",
     "//ios/clean/chrome/browser/ui/tab_grid:unit_tests",
diff --git a/ios/shared/chrome/browser/ui/settings/BUILD.gn b/ios/shared/chrome/browser/ui/settings/BUILD.gn
new file mode 100644
index 0000000..ac535bc
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/settings/BUILD.gn
@@ -0,0 +1,20 @@
+# 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.
+
+source_set("settings") {
+  sources = [
+    "settings_main_page_commands.h",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    # "//base",
+    # "//ios/chrome/app:tests_fake_hook",
+    # "//ios/chrome/browser",
+    # "//ios/chrome/browser/browser_state",
+    # "//ios/chrome/browser/tabs:tabs_internal",
+    # "//ios/shared/chrome/browser/ui/browser_list",
+  ]
+}
diff --git a/ios/shared/chrome/browser/ui/settings/settings_main_page_commands.h b/ios/shared/chrome/browser/ui/settings/settings_main_page_commands.h
new file mode 100644
index 0000000..145944d
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/settings/settings_main_page_commands.h
@@ -0,0 +1,14 @@
+// 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 IOS_SHARED_CHROME_BROWSER_UI_SETTINGS_SETTINGS_MAIN_PAGE_COMMANDS_H_
+#define IOS_SHARED_CHROME_BROWSER_UI_SETTINGS_SETTINGS_MAIN_PAGE_COMMANDS_H_
+
+// Command protocol for commands related to the Settings Main Page.
+@protocol SettingsMainPageCommands
+// Called when the Material Cell Catalog cell is tapped.
+- (void)showMaterialCellCatalog;
+@end
+
+#endif  // IOS_SHARED_CHROME_BROWSER_UI_SETTINGS_SETTINGS_MAIN_PAGE_COMMANDS_H_
diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc
index e3d994f..c048bf3 100644
--- a/media/audio/audio_input_controller.cc
+++ b/media/audio/audio_input_controller.cc
@@ -359,11 +359,11 @@
 
   // Finally, keep the stream pointer around, update the state and notify.
   stream_ = stream_to_control;
-  handler_->OnCreated(this);
 
-  // Check the current muted state and start the repeating timer to keep that
-  // updated.
-  CheckMutedState();
+  // Send initial muted state along with OnCreated, to avoid races.
+  is_muted_ = stream_->IsMuted();
+  handler_->OnCreated(this, is_muted_);
+
   check_muted_state_timer_.Start(
       FROM_HERE, base::TimeDelta::FromSeconds(kCheckMutedStateIntervalSeconds),
       this, &AudioInputController::CheckMutedState);
diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h
index b037996..2b8669c2 100644
--- a/media/audio/audio_input_controller.h
+++ b/media/audio/audio_input_controller.h
@@ -104,15 +104,16 @@
   // following methods are all called on the audio thread.
   class MEDIA_EXPORT EventHandler {
    public:
-    virtual void OnCreated(AudioInputController* controller) = 0;
+    // The initial "muted" state of the underlying stream is sent along with the
+    // OnCreated callback, to avoid the stream being treated as unmuted until an
+    // OnMuted callback has had time to be processed.
+    virtual void OnCreated(AudioInputController* controller,
+                           bool initially_muted) = 0;
     virtual void OnError(AudioInputController* controller,
                          ErrorCode error_code) = 0;
     virtual void OnLog(AudioInputController* controller,
                        const std::string& message) = 0;
-    // Initially, an AudioInputController is considered not muted. If the
-    // underlying stream is actually muted, an OnMuted callback will follow
-    // shortly after OnCreated. It is also called whenever the muted state of
-    // the underlying stream changes.
+    // Called whenever the muted state of the underlying stream changes.
     virtual void OnMuted(AudioInputController* controller, bool is_muted) = 0;
 
    protected:
diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc
index fc290e6..0b8ef921 100644
--- a/media/audio/audio_input_controller_unittest.cc
+++ b/media/audio/audio_input_controller_unittest.cc
@@ -61,7 +61,8 @@
  public:
   MockAudioInputControllerEventHandler() {}
 
-  MOCK_METHOD1(OnCreated, void(AudioInputController* controller));
+  MOCK_METHOD2(OnCreated,
+               void(AudioInputController* controller, bool initially_muted));
   MOCK_METHOD2(OnError, void(AudioInputController* controller,
                              AudioInputController::ErrorCode error_code));
   MOCK_METHOD2(OnLog,
@@ -136,7 +137,7 @@
       audio_manager_.get(), &event_handler, &sync_writer, nullptr, params,
       AudioDeviceDescription::kDefaultDeviceId, false);
   ASSERT_TRUE(controller.get());
-  EXPECT_CALL(event_handler, OnCreated(controller.get())).Times(Exactly(1));
+  EXPECT_CALL(event_handler, OnCreated(controller.get(), _)).Times(Exactly(1));
   EXPECT_CALL(event_handler, OnLog(controller.get(), _)).Times(Exactly(3));
   EXPECT_CALL(sync_writer, Close()).Times(Exactly(1));
   ResumeAudioThread();
@@ -151,7 +152,7 @@
   int count = 0;
 
   // OnCreated() will be called once.
-  EXPECT_CALL(event_handler, OnCreated(NotNull()))
+  EXPECT_CALL(event_handler, OnCreated(NotNull(), _))
       .Times(Exactly(1));
 
   // Write() should be called ten times.
@@ -186,7 +187,7 @@
   MockSyncWriter sync_writer;
 
   // OnCreated() shall not be called in this test.
-  EXPECT_CALL(event_handler, OnCreated(NotNull())).Times(Exactly(0));
+  EXPECT_CALL(event_handler, OnCreated(NotNull(), _)).Times(Exactly(0));
 
   AudioParameters params(AudioParameters::AUDIO_FAKE,
                          kChannelLayout,
@@ -205,7 +206,7 @@
   MockSyncWriter sync_writer;
 
   // OnCreated() will be called only once.
-  EXPECT_CALL(event_handler, OnCreated(NotNull())).Times(Exactly(1));
+  EXPECT_CALL(event_handler, OnCreated(NotNull(), _)).Times(Exactly(1));
   EXPECT_CALL(event_handler, OnLog(_, _)).Times(AnyNumber());
   // This callback should still only be called once.
   EXPECT_CALL(sync_writer, Close()).Times(Exactly(1));
@@ -252,7 +253,8 @@
   base::RunLoop unmute_run_loop;
   base::RunLoop mute_run_loop;
   base::RunLoop setup_run_loop;
-  EXPECT_CALL(event_handler, OnCreated(_)).Times(Exactly(1));
+  EXPECT_CALL(event_handler, OnCreated(_, false))
+      .WillOnce(InvokeWithoutArgs([&] { setup_run_loop.QuitWhenIdle(); }));
   EXPECT_CALL(event_handler, OnLog(_, _)).Times(Exactly(3));
   EXPECT_CALL(sync_writer, Close()).Times(Exactly(1));
   EXPECT_CALL(event_handler, OnMuted(_, true))
@@ -265,7 +267,7 @@
       audio_manager_.get(), &event_handler, &sync_writer, nullptr, params,
       AudioDeviceDescription::kDefaultDeviceId, false);
   ASSERT_TRUE(controller.get());
-  setup_run_loop.RunUntilIdle();
+  RunLoopWithTimeout(&setup_run_loop, timeout);
 
   FakeAudioInputStream::SetGlobalMutedState(true);
   RunLoopWithTimeout(&mute_run_loop, timeout);
@@ -288,11 +290,10 @@
 
   base::RunLoop unmute_run_loop;
   base::RunLoop setup_run_loop;
-  EXPECT_CALL(event_handler, OnCreated(_)).Times(Exactly(1));
+  EXPECT_CALL(event_handler, OnCreated(_, true))
+      .WillOnce(InvokeWithoutArgs([&] { setup_run_loop.QuitWhenIdle(); }));
   EXPECT_CALL(event_handler, OnLog(_, _)).Times(Exactly(3));
   EXPECT_CALL(sync_writer, Close()).Times(Exactly(1));
-  EXPECT_CALL(event_handler, OnMuted(_, true))
-      .WillOnce(InvokeWithoutArgs([&] { setup_run_loop.QuitWhenIdle(); }));
   EXPECT_CALL(event_handler, OnMuted(_, false))
       .WillOnce(InvokeWithoutArgs([&] { unmute_run_loop.Quit(); }));
 
diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc
index eb31a57..3d6819d 100644
--- a/media/audio/audio_input_device.cc
+++ b/media/audio/audio_input_device.cc
@@ -149,11 +149,11 @@
           this, enabled));
 }
 
-void AudioInputDevice::OnStreamCreated(
-    base::SharedMemoryHandle handle,
-    base::SyncSocket::Handle socket_handle,
-    int length,
-    int total_segments) {
+void AudioInputDevice::OnStreamCreated(base::SharedMemoryHandle handle,
+                                       base::SyncSocket::Handle socket_handle,
+                                       int length,
+                                       int total_segments,
+                                       bool initially_muted) {
   DCHECK(task_runner()->BelongsToCurrentThread());
   DCHECK(base::SharedMemory::IsHandleValid(handle));
 #if defined(OS_WIN)
@@ -175,6 +175,10 @@
 
   DCHECK(!audio_callback_);
   DCHECK(!audio_thread_);
+
+  if (initially_muted)
+    callback_->OnCaptureMuted(true);
+
   audio_callback_.reset(new AudioInputDevice::AudioThreadCallback(
       audio_parameters_, handle, length, total_segments, callback_,
       base::BindRepeating(&AudioInputDevice::SetLastCallbackTimeToNow, this)));
diff --git a/media/audio/audio_input_device.h b/media/audio/audio_input_device.h
index 3072e2f..d656256 100644
--- a/media/audio/audio_input_device.h
+++ b/media/audio/audio_input_device.h
@@ -104,7 +104,8 @@
   void OnStreamCreated(base::SharedMemoryHandle handle,
                        base::SyncSocket::Handle socket_handle,
                        int length,
-                       int total_segments) override;
+                       int total_segments,
+                       bool initially_muted) override;
   void OnError() override;
   void OnMuted(bool is_muted) override;
   void OnIPCClosed() override;
diff --git a/media/audio/audio_input_device_unittest.cc b/media/audio/audio_input_device_unittest.cc
index 1642ad0..3862398 100644
--- a/media/audio/audio_input_device_unittest.cc
+++ b/media/audio/audio_input_device_unittest.cc
@@ -100,7 +100,7 @@
 
 ACTION_P5(ReportOnStreamCreated, device, handle, socket, length, segments) {
   static_cast<AudioInputIPCDelegate*>(device)->OnStreamCreated(
-      handle, socket, length, segments);
+      handle, socket, length, segments, false);
 }
 
 TEST(AudioInputDeviceTest, CreateStream) {
diff --git a/media/audio/audio_input_ipc.h b/media/audio/audio_input_ipc.h
index 058d4d0..1f51ebe 100644
--- a/media/audio/audio_input_ipc.h
+++ b/media/audio/audio_input_ipc.h
@@ -30,7 +30,8 @@
   virtual void OnStreamCreated(base::SharedMemoryHandle handle,
                                base::SyncSocket::Handle socket_handle,
                                int length,
-                               int total_segments) = 0;
+                               int total_segments,
+                               bool initially_muted) = 0;
 
   // Called when state of an audio stream has changed.
   virtual void OnError() = 0;
diff --git a/media/base/audio_capturer_source.h b/media/base/audio_capturer_source.h
index 6e34ddc..1107882 100644
--- a/media/base/audio_capturer_source.h
+++ b/media/base/audio_capturer_source.h
@@ -41,7 +41,8 @@
     // Signals an error has occurred.
     virtual void OnCaptureError(const std::string& message) = 0;
 
-    // Signals the muted state has changed.
+    // Signals the muted state has changed. May be called before
+    // OnCaptureStarted.
     virtual void OnCaptureMuted(bool is_muted) = 0;
 
    protected:
diff --git a/services/resource_coordinator/memory_instrumentation/coordinator_impl.cc b/services/resource_coordinator/memory_instrumentation/coordinator_impl.cc
index 7d7a3ed..33e4938 100644
--- a/services/resource_coordinator/memory_instrumentation/coordinator_impl.cc
+++ b/services/resource_coordinator/memory_instrumentation/coordinator_impl.cc
@@ -96,6 +96,17 @@
   g_coordinator_impl = nullptr;
 }
 
+base::ProcessId CoordinatorImpl::GetProcessIdForClientIdentity(
+    service_manager::Identity identity) const {
+  // TODO(primiano): the browser process registers bypassing mojo and ends up
+  // with an invalid identity. Fix that (crbug.com/733165) and remove the
+  // special case in the else branch below.
+  if (!identity.IsValid()) {
+    return base::GetCurrentProcId();
+  }
+  return process_map_->GetProcessId(identity);
+}
+
 service_manager::Identity CoordinatorImpl::GetClientIdentityForCurrentRequest()
     const {
   return bindings_.dispatch_context();
@@ -154,14 +165,16 @@
 }
 
 void CoordinatorImpl::RegisterClientProcess(
-    mojom::ClientProcessPtr client_process_ptr) {
+    mojom::ClientProcessPtr client_process_ptr,
+    mojom::ProcessType process_type) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   mojom::ClientProcess* client_process = client_process_ptr.get();
   client_process_ptr.set_connection_error_handler(
       base::Bind(&CoordinatorImpl::UnregisterClientProcess,
                  base::Unretained(this), client_process));
-  auto client_info = base::MakeUnique<ClientInfo>(
-      GetClientIdentityForCurrentRequest(), std::move(client_process_ptr));
+  auto client_info =
+      base::MakeUnique<ClientInfo>(GetClientIdentityForCurrentRequest(),
+                                   std::move(client_process_ptr), process_type);
   auto iterator_and_inserted =
       clients_.emplace(client_process, std::move(client_info));
   DCHECK(iterator_and_inserted.second);
@@ -231,20 +244,15 @@
 
   if (process_memory_dump) {
     const service_manager::Identity& client_identity = iter->second->identity;
-    base::ProcessId pid = base::kNullProcessId;
+    const mojom::ProcessType& process_type = iter->second->process_type;
+    const base::ProcessId pid = GetProcessIdForClientIdentity(client_identity);
 
-    // TODO(primiano): the browser process registers bypassing mojo and ends up
-    // with an invalid identity. Fix that (crbug.com/733165) and remove the
-    // special case in the else branch below.
-    if (client_identity.IsValid()) {
-      pid = process_map_->GetProcessId(client_identity);
-    } else {
-      pid = base::GetCurrentProcId();
-    }
+    auto response = base::MakeUnique<QueuedMemoryDumpRequest::Response>(
+        process_type, std::move(process_memory_dump));
 
     if (pid != base::kNullProcessId) {
-      queued_memory_dump_requests_.front().process_memory_dumps.push_back(
-          std::make_pair(pid, std::move(process_memory_dump)));
+      queued_memory_dump_requests_.front().responses.push_back(
+          std::make_pair(pid, std::move(response)));
     } else {
       VLOG(1) << "Couldn't find a PID for client \"" << client_identity.name()
               << "." << client_identity.instance() << "\"";
@@ -276,9 +284,9 @@
   // opening /proc pseudo files.
 
   std::map<base::ProcessId, OSMemDump> os_dumps;
-  for (auto& result : request->process_memory_dumps) {
-    const base::ProcessId pid = result.first;
-    const OSMemDump dump = result.second->os_dump;
+  for (auto& response : request->responses) {
+    const base::ProcessId pid = response.first;
+    const OSMemDump dump = response.second->dump_ptr->os_dump;
 
     // TODO(hjd): We should have a better way to tell if os_dump is filled.
     if (dump.resident_set_kb > 0) {
@@ -286,7 +294,7 @@
       os_dumps[pid] = dump;
     }
 
-    for (auto& extra : result.second->extra_processes_dumps) {
+    for (auto& extra : response.second->dump_ptr->extra_processes_dumps) {
       const base::ProcessId extra_pid = extra.first;
       const OSMemDump extra_dump = extra.second;
       DCHECK_EQ(0u, os_dumps.count(extra_pid));
@@ -295,14 +303,14 @@
   }
 
   std::map<base::ProcessId, mojom::ProcessMemoryDumpPtr> finalized_pmds;
-  for (auto& result : request->process_memory_dumps) {
-    const base::ProcessId pid = result.first;
+  for (auto& response : request->responses) {
+    const base::ProcessId pid = response.first;
     mojom::ProcessMemoryDumpPtr& pmd = finalized_pmds[pid];
     if (!pmd)
       pmd = mojom::ProcessMemoryDump::New();
 
-    pmd->process_type = result.second->process_type;
-    pmd->chrome_dump = result.second->chrome_dump;
+    pmd->process_type = response.second->process_type;
+    pmd->chrome_dump = response.second->dump_ptr->chrome_dump;
     pmd->os_dump = CreatePublicOSDump(os_dumps[pid]);
   }
 
@@ -342,6 +350,13 @@
   }
 }
 
+CoordinatorImpl::QueuedMemoryDumpRequest::Response::Response(
+    mojom::ProcessType process_type,
+    mojom::RawProcessMemoryDumpPtr dump_ptr)
+    : process_type(process_type), dump_ptr(std::move(dump_ptr)) {}
+
+CoordinatorImpl::QueuedMemoryDumpRequest::Response::~Response() {}
+
 CoordinatorImpl::QueuedMemoryDumpRequest::QueuedMemoryDumpRequest(
     const base::trace_event::MemoryDumpRequestArgs& args,
     const RequestGlobalMemoryDumpCallback callback)
@@ -351,8 +366,11 @@
 
 CoordinatorImpl::ClientInfo::ClientInfo(
     const service_manager::Identity& identity,
-    mojom::ClientProcessPtr client)
-    : identity(identity), client(std::move(client)) {}
+    mojom::ClientProcessPtr client,
+    mojom::ProcessType process_type)
+    : identity(identity),
+      client(std::move(client)),
+      process_type(process_type) {}
 CoordinatorImpl::ClientInfo::~ClientInfo() {}
 
 }  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/memory_instrumentation/coordinator_impl.h b/services/resource_coordinator/memory_instrumentation/coordinator_impl.h
index bf81cef..d68c3e3 100644
--- a/services/resource_coordinator/memory_instrumentation/coordinator_impl.h
+++ b/services/resource_coordinator/memory_instrumentation/coordinator_impl.h
@@ -43,7 +43,8 @@
       mojom::CoordinatorRequest) override;
 
   // mojom::Coordinator implementation.
-  void RegisterClientProcess(mojom::ClientProcessPtr) override;
+  void RegisterClientProcess(mojom::ClientProcessPtr,
+                             mojom::ProcessType) override;
   void UnregisterClientProcess(mojom::ClientProcess*);
   void RequestGlobalMemoryDump(const base::trace_event::MemoryDumpRequestArgs&,
                                const RequestGlobalMemoryDumpCallback&) override;
@@ -51,6 +52,9 @@
  protected:
   // virtual for testing.
   virtual service_manager::Identity GetClientIdentityForCurrentRequest() const;
+  // virtual for testing.
+  virtual base::ProcessId GetProcessIdForClientIdentity(
+      service_manager::Identity identity) const;
   ~CoordinatorImpl() override;
 
  private:
@@ -66,18 +70,29 @@
     const base::trace_event::MemoryDumpRequestArgs args;
     const RequestGlobalMemoryDumpCallback callback;
 
+    struct Response {
+      Response(mojom::ProcessType, mojom::RawProcessMemoryDumpPtr);
+      ~Response();
+
+      const mojom::ProcessType process_type;
+      const mojom::RawProcessMemoryDumpPtr dump_ptr;
+    };
+
     // Collects the data received from OnProcessMemoryDumpResponse().
-    std::vector<std::pair<base::ProcessId, mojom::RawProcessMemoryDumpPtr>>
-        process_memory_dumps;
+    std::vector<std::pair<base::ProcessId, std::unique_ptr<Response>>>
+        responses;
   };
 
-  // Holds the identy and remote reference of registered clients.
+  // Holds the identity and remote reference of registered clients.
   struct ClientInfo {
-    ClientInfo(const service_manager::Identity&, mojom::ClientProcessPtr);
+    ClientInfo(const service_manager::Identity&,
+               mojom::ClientProcessPtr,
+               mojom::ProcessType);
     ~ClientInfo();
 
     const service_manager::Identity identity;
     const mojom::ClientProcessPtr client;
+    const mojom::ProcessType process_type;
   };
 
   // Callback of RequestProcessMemoryInternalDump.
diff --git a/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc b/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc
index 421ba4e..b661d0f 100644
--- a/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc
+++ b/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/trace_event/memory_dump_request_args.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
+#include "services/resource_coordinator/memory_instrumentation/process_map.h"
 #include "services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom.h"
 
 #include "testing/gmock/include/gmock/gmock.h"
@@ -24,6 +25,12 @@
 using ::testing::Ne;
 using ::testing::NotNull;
 using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Contains;
+using ::testing::Property;
+using ::testing::Pointee;
+using ::testing::Field;
+using ::testing::Eq;
 
 using RequestGlobalMemoryDumpCallback =
     memory_instrumentation::CoordinatorImpl::RequestGlobalMemoryDumpCallback;
@@ -37,23 +44,35 @@
  public:
   FakeCoordinatorImpl() : CoordinatorImpl(nullptr) {}
   ~FakeCoordinatorImpl() override {}
-  service_manager::Identity GetClientIdentityForCurrentRequest()
-      const override {
-    return service_manager::Identity();
-  }
+
+  MOCK_CONST_METHOD0(GetClientIdentityForCurrentRequest,
+                     service_manager::Identity());
+  MOCK_CONST_METHOD1(GetProcessIdForClientIdentity,
+                     base::ProcessId(service_manager::Identity));
 };
 
 class CoordinatorImplTest : public testing::Test {
  public:
   CoordinatorImplTest() {}
   void SetUp() override {
-    coordinator_.reset(new FakeCoordinatorImpl);
+    coordinator_.reset(new NiceMock<FakeCoordinatorImpl>);
   }
 
   void TearDown() override { coordinator_.reset(); }
 
-  void RegisterClientProcess(mojom::ClientProcessPtr client_process) {
-    coordinator_->RegisterClientProcess(std::move(client_process));
+  void RegisterClientProcess(mojom::ClientProcessPtr client_process,
+                             base::ProcessId pid,
+                             mojom::ProcessType process_type) {
+    auto identity = service_manager::Identity(std::to_string(pid));
+
+    ON_CALL(*coordinator_, GetClientIdentityForCurrentRequest())
+        .WillByDefault(Return(identity));
+
+    ON_CALL(*coordinator_, GetProcessIdForClientIdentity(identity))
+        .WillByDefault(Return(pid));
+
+    coordinator_->RegisterClientProcess(std::move(client_process),
+                                        process_type);
   }
 
   void RequestGlobalMemoryDump(MemoryDumpRequestArgs args,
@@ -62,17 +81,26 @@
   }
 
  private:
-  std::unique_ptr<CoordinatorImpl> coordinator_;
+  std::unique_ptr<NiceMock<FakeCoordinatorImpl>> coordinator_;
   base::MessageLoop message_loop_;
 };
 
 class MockClientProcess : public mojom::ClientProcess {
  public:
-  MockClientProcess(CoordinatorImplTest* test_coordinator) : binding_(this) {
+  MockClientProcess(CoordinatorImplTest* test_coordinator)
+      : MockClientProcess(test_coordinator,
+                          base::GetCurrentProcId(),
+                          mojom::ProcessType::OTHER) {}
+
+  MockClientProcess(CoordinatorImplTest* test_coordinator,
+                    base::ProcessId pid,
+                    mojom::ProcessType process_type)
+      : binding_(this) {
     // Register to the coordinator.
     mojom::ClientProcessPtr client_process;
     binding_.Bind(mojo::MakeRequest(&client_process));
-    test_coordinator->RegisterClientProcess(std::move(client_process));
+    test_coordinator->RegisterClientProcess(std::move(client_process), pid,
+                                            process_type);
 
     ON_CALL(*this, RequestProcessMemoryDump(_, _))
         .WillByDefault(
@@ -187,4 +215,59 @@
   run_loop.Run();
 }
 
+TEST_F(CoordinatorImplTest, GlobalMemoryDumpStruct) {
+  base::RunLoop run_loop;
+
+  MockClientProcess client_process_1(this, 1, mojom::ProcessType::BROWSER);
+  MockClientProcess client_process_2(this, 2, mojom::ProcessType::RENDERER);
+  EXPECT_CALL(client_process_1, RequestProcessMemoryDump(_, _))
+      .WillOnce(
+          Invoke([](const MemoryDumpRequestArgs& args,
+                    const MockClientProcess::RequestProcessMemoryDumpCallback&
+                        callback) {
+            auto dump = mojom::RawProcessMemoryDump::New();
+            dump->chrome_dump.malloc_total_kb = 1;
+            dump->os_dump.resident_set_kb = 1;
+            callback.Run(args.dump_guid, true, std::move(dump));
+
+          }));
+  EXPECT_CALL(client_process_2, RequestProcessMemoryDump(_, _))
+      .WillOnce(
+          Invoke([](const MemoryDumpRequestArgs& args,
+                    const MockClientProcess::RequestProcessMemoryDumpCallback&
+                        callback) {
+            auto dump = mojom::RawProcessMemoryDump::New();
+            dump->chrome_dump.malloc_total_kb = 2;
+            dump->os_dump.resident_set_kb = 2;
+            callback.Run(args.dump_guid, true, std::move(dump));
+          }));
+
+  MockGlobalMemoryDumpCallback callback;
+  EXPECT_CALL(callback, OnCall(Ne(0u), true, NotNull()))
+      .WillOnce(Invoke([&run_loop](uint64_t dump_guid, bool success,
+                                   GlobalMemoryDump* dump) {
+
+        EXPECT_EQ(2U, dump->process_dumps.size());
+        EXPECT_THAT(dump->process_dumps,
+                    Contains(Property(
+                        &mojom::ProcessMemoryDumpPtr::get,
+                        Pointee(Field(&mojom::ProcessMemoryDump::process_type,
+                                      Eq(mojom::ProcessType::BROWSER))))));
+
+        EXPECT_THAT(dump->process_dumps,
+                    Contains(Property(
+                        &mojom::ProcessMemoryDumpPtr::get,
+                        Pointee(Field(&mojom::ProcessMemoryDump::process_type,
+                                      Eq(mojom::ProcessType::RENDERER))))));
+
+        run_loop.Quit();
+      }));
+
+  base::trace_event::MemoryDumpRequestArgs args = {
+      0, base::trace_event::MemoryDumpType::SUMMARY_ONLY,
+      base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND};
+  RequestGlobalMemoryDump(args, callback.Get());
+  run_loop.Run();
+}
+
 }  // namespace memory_instrumentation
diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc
index cc230d0..be1193f 100644
--- a/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc
+++ b/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc
@@ -33,7 +33,7 @@
                                   mojo::MakeRequest(&coordinator_));
   mojom::ClientProcessPtr process;
   binding_.Bind(mojo::MakeRequest(&process));
-  coordinator_->RegisterClientProcess(std::move(process));
+  coordinator_->RegisterClientProcess(std::move(process), config.process_type);
 
   // Initialize the public-facing MemoryInstrumentation helper.
   MemoryInstrumentation::CreateInstance(config.connector, config.service_name);
@@ -69,7 +69,6 @@
     const base::Optional<base::trace_event::MemoryDumpCallbackResult>& result) {
   mojom::RawProcessMemoryDumpPtr process_memory_dump(
       mojom::RawProcessMemoryDump::New());
-  process_memory_dump->process_type = process_type_;
   if (result) {
     process_memory_dump->os_dump = result->os_dump;
     process_memory_dump->chrome_dump = result->chrome_dump;
diff --git a/services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom b/services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom
index 1c3900f..05d0fc4 100644
--- a/services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom
+++ b/services/resource_coordinator/public/interfaces/memory_instrumentation/memory_instrumentation.mojom
@@ -68,7 +68,6 @@
 // This struct is used for internal communication between the memory
 // service (Coordinator) and the client library (ClientProcessImpl).
 struct RawProcessMemoryDump {
-  ProcessType process_type;
   RawOSMemDump os_dump;
   ChromeMemDump chrome_dump;
 
@@ -118,7 +117,7 @@
 // processes and polls them whenever a global dump is required.
 interface Coordinator {
   // Registers a client process.
-  RegisterClientProcess(ClientProcess client_process);
+  RegisterClientProcess(ClientProcess client_process, ProcessType process_type);
 
   // Broadcasts a dump request to all registered client processes, injects the
   // dump in the trace buffer (if tracing is enabled) and returns a summarized
diff --git a/third_party/WebKit/Source/modules/cachestorage/InspectorCacheStorageAgent.cpp b/third_party/WebKit/Source/modules/cachestorage/InspectorCacheStorageAgent.cpp
index 81a7cfe..e56047d9 100644
--- a/third_party/WebKit/Source/modules/cachestorage/InspectorCacheStorageAgent.cpp
+++ b/third_party/WebKit/Source/modules/cachestorage/InspectorCacheStorageAgent.cpp
@@ -188,8 +188,7 @@
         responses_.at(responses_.size() - num_responses_left_);
     request_response.request = request.Url().GetString();
     request_response.response = response.StatusText();
-    request_response.response_time =
-        WTF::Time::FromInternalValue(response.ResponseTime()).ToDoubleT();
+    request_response.response_time = response.ResponseTime().ToDoubleT();
 
     if (--num_responses_left_ != 0)
       return;
diff --git a/third_party/WebKit/Source/modules/fetch/FetchResponseData.cpp b/third_party/WebKit/Source/modules/fetch/FetchResponseData.cpp
index efe397d..3aa001a9 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchResponseData.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchResponseData.cpp
@@ -281,8 +281,7 @@
     : type_(type),
       status_(status),
       status_message_(status_message),
-      header_list_(FetchHeaderList::Create()),
-      response_time_(0) {}
+      header_list_(FetchHeaderList::Create()) {}
 
 void FetchResponseData::ReplaceBodyStreamBuffer(BodyStreamBuffer* buffer) {
   if (type_ == kBasicType || type_ == kCORSType) {
diff --git a/third_party/WebKit/Source/modules/fetch/FetchResponseData.h b/third_party/WebKit/Source/modules/fetch/FetchResponseData.h
index 53ef249..03ff6d9 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchResponseData.h
+++ b/third_party/WebKit/Source/modules/fetch/FetchResponseData.h
@@ -11,6 +11,7 @@
 #include "platform/loader/fetch/CrossOriginAccessControl.h"
 #include "platform/weborigin/KURL.h"
 #include "platform/wtf/PassRefPtr.h"
+#include "platform/wtf/Time.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/AtomicString.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
@@ -80,7 +81,7 @@
   // returns |m_buffer|.
   BodyStreamBuffer* InternalBuffer() const;
   String InternalMIMEType() const;
-  int64_t ResponseTime() const { return response_time_; }
+  Time ResponseTime() const { return response_time_; }
   String CacheStorageCacheName() const { return cache_storage_cache_name_; }
   const HTTPHeaderSet& CorsExposedHeaderNames() const {
     return cors_exposed_header_names_;
@@ -95,9 +96,7 @@
     status_message_ = status_message;
   }
   void SetMIMEType(const String& type) { mime_type_ = type; }
-  void SetResponseTime(int64_t response_time) {
-    response_time_ = response_time;
-  }
+  void SetResponseTime(Time response_time) { response_time_ = response_time; }
   void SetCacheStorageCacheName(const String& cache_storage_cache_name) {
     cache_storage_cache_name_ = cache_storage_cache_name;
   }
@@ -130,7 +129,7 @@
   Member<FetchResponseData> internal_response_;
   Member<BodyStreamBuffer> buffer_;
   String mime_type_;
-  int64_t response_time_;
+  Time response_time_;
   String cache_storage_cache_name_;
   HTTPHeaderSet cors_exposed_header_names_;
 };
diff --git a/third_party/WebKit/Source/platform/exported/WebServiceWorkerResponse.cpp b/third_party/WebKit/Source/platform/exported/WebServiceWorkerResponse.cpp
index a322c00..6e61156 100644
--- a/third_party/WebKit/Source/platform/exported/WebServiceWorkerResponse.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebServiceWorkerResponse.cpp
@@ -8,6 +8,7 @@
 #include "platform/network/HTTPHeaderMap.h"
 #include "platform/wtf/HashMap.h"
 #include "platform/wtf/RefCounted.h"
+#include "platform/wtf/Time.h"
 #include "public/platform/WebHTTPHeaderVisitor.h"
 
 namespace blink {
@@ -18,8 +19,7 @@
   WebServiceWorkerResponsePrivate()
       : status(0),
         response_type(kWebServiceWorkerResponseTypeDefault),
-        error(kWebServiceWorkerResponseErrorUnknown),
-        response_time(0) {}
+        error(kWebServiceWorkerResponseErrorUnknown) {}
   WebVector<WebURL> url_list;
   unsigned short status;
   WebString status_text;
@@ -27,7 +27,7 @@
   HTTPHeaderMap headers;
   RefPtr<BlobDataHandle> blob_data_handle;
   WebServiceWorkerResponseError error;
-  int64_t response_time;
+  Time response_time;
   WebString cache_storage_cache_name;
   WebVector<WebString> cors_exposed_header_names;
 };
@@ -135,11 +135,11 @@
   return private_->error;
 }
 
-void WebServiceWorkerResponse::SetResponseTime(int64_t time) {
+void WebServiceWorkerResponse::SetResponseTime(base::Time time) {
   private_->response_time = time;
 }
 
-int64_t WebServiceWorkerResponse::ResponseTime() const {
+base::Time WebServiceWorkerResponse::ResponseTime() const {
   return private_->response_time;
 }
 
diff --git a/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp b/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
index 61cf41e..4bd02d4 100644
--- a/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebURLResponse.cpp
@@ -134,8 +134,8 @@
   resource_response_->SetResourceLoadInfo(value);
 }
 
-void WebURLResponse::SetResponseTime(long long response_time) {
-  resource_response_->SetResponseTime(static_cast<int64_t>(response_time));
+void WebURLResponse::SetResponseTime(base::Time response_time) {
+  resource_response_->SetResponseTime(response_time);
 }
 
 WebString WebURLResponse::MimeType() const {
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.cpp
index ae852e1f..aea3dba8 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.cpp
@@ -100,7 +100,6 @@
       expires_(0.0),
       last_modified_(0.0),
       app_cache_id_(0),
-      response_time_(0),
       connection_info_(
           net::HttpResponseInfo::ConnectionInfo::CONNECTION_INFO_UNKNOWN),
       encoded_data_length_(0),
@@ -140,7 +139,6 @@
       expires_(0.0),
       last_modified_(0.0),
       app_cache_id_(0),
-      response_time_(0),
       encoded_data_length_(0),
       encoded_body_length_(0),
       decoded_body_length_(0) {}
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.h b/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.h
index da5c9ef..813f7172 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.h
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceResponse.h
@@ -36,6 +36,7 @@
 #include "platform/weborigin/KURL.h"
 #include "platform/wtf/RefCounted.h"
 #include "platform/wtf/RefPtr.h"
+#include "platform/wtf/Time.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/CString.h"
 #include "public/platform/WebURLResponse.h"
@@ -328,10 +329,8 @@
     did_service_worker_navigation_preload_ = value;
   }
 
-  int64_t ResponseTime() const { return response_time_; }
-  void SetResponseTime(int64_t response_time) {
-    response_time_ = response_time;
-  }
+  Time ResponseTime() const { return response_time_; }
+  void SetResponseTime(Time response_time) { response_time_ = response_time; }
 
   const AtomicString& RemoteIPAddress() const { return remote_ip_address_; }
   void SetRemoteIPAddress(const AtomicString& value) {
@@ -491,7 +490,7 @@
 
   // The time at which the response headers were received.  For cached
   // responses, this time could be "far" in the past.
-  int64_t response_time_;
+  Time response_time_;
 
   // ALPN negotiated protocol of the socket which fetched this resource.
   AtomicString alpn_negotiated_protocol_;
@@ -564,7 +563,7 @@
   Vector<KURL> url_list_via_service_worker_;
   String cache_storage_cache_name_;
   bool did_service_worker_navigation_preload_;
-  int64_t response_time_;
+  Time response_time_;
   String remote_ip_address_;
   unsigned short remote_port_;
   long long encoded_data_length_;
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceTest.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceTest.cpp
index 2a8c7c9..0c46634 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceTest.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceTest.cpp
@@ -10,6 +10,7 @@
 #include "platform/loader/fetch/ResourceResponse.h"
 #include "platform/testing/TestingPlatformSupport.h"
 #include "platform/testing/URLTestHelpers.h"
+#include "platform/wtf/Time.h"
 #include "platform/wtf/Vector.h"
 #include "public/platform/Platform.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,7 +25,7 @@
   ~MockPlatform() override {}
 
   // From blink::Platform:
-  void CacheMetadata(const WebURL& url, int64_t, const char*, size_t) override {
+  void CacheMetadata(const WebURL& url, Time, const char*, size_t) override {
     cached_urls_.push_back(url);
   }
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
index bcfd463..6025012 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base.py
@@ -108,8 +108,6 @@
         'android': ['kitkat'],
     }
 
-    DEFAULT_BUILD_DIRECTORIES = ('out',)
-
     FALLBACK_PATHS = {}
 
     SUPPORTED_VERSIONS = []
@@ -125,25 +123,6 @@
         return cls.FALLBACK_PATHS[cls.SUPPORTED_VERSIONS[-1]]
 
     @classmethod
-    def _static_build_path(cls, filesystem, build_directory, chromium_base, target, comps):
-        if build_directory:
-            return filesystem.join(build_directory, target, *comps)
-
-        hits = []
-        for directory in cls.DEFAULT_BUILD_DIRECTORIES:
-            base_dir = filesystem.join(chromium_base, directory, target)
-            path = filesystem.join(base_dir, *comps)
-            if filesystem.exists(path):
-                hits.append((filesystem.mtime(path), path))
-
-        if hits:
-            hits.sort(reverse=True)
-            return hits[0][1]  # Return the newest file found.
-
-        # We have to default to something, so pick the last one.
-        return filesystem.join(base_dir, *comps)
-
-    @classmethod
     def determine_full_port_name(cls, host, options, port_name):
         """Return a fully-specified port name that can be used to construct objects."""
         # Subclasses will usually override this.
@@ -1587,14 +1566,15 @@
             directory) for directory in self._options.image_first_tests)
 
     def _build_path(self, *comps):
+        """Returns a path from the build directory."""
         return self._build_path_with_target(self._options.target, *comps)
 
     def _build_path_with_target(self, target, *comps):
-        # Note that we don't do the option caching that the base class does,
-        # because finding the right directory is relatively fast.
         target = target or self.get_option('target')
-        return self._static_build_path(self._filesystem, self.get_option('build_directory'),
-                                       self.path_from_chromium_base(), target, comps)
+        return self._filesystem.join(
+            self.path_from_chromium_base(),
+            self.get_option('build_directory') or 'out',
+            target, *comps)
 
     def _check_driver_build_up_to_date(self, target):
         # FIXME: We should probably get rid of this check altogether as it has
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
index 76f861b0..951edcb 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/base_unittest.py
@@ -477,8 +477,14 @@
         self.assertNotIn('virtual/virtual_passes/passes/virtual_passes/passes/test-virtual-passes.html', tests)
 
     def test_build_path(self):
-        port = self.make_port(options=optparse.Values({'build_directory': '/my-build-directory/'}))
-        self.assertEqual(port._build_path(), '/my-build-directory/Release')
+        # Test for a protected method - pylint: disable=protected-access
+        # Test that optional paths are used regardless of whether they exist.
+        options = optparse.Values({'configuration': 'Release', 'build_directory': 'xcodebuild'})
+        self.assertEqual(self.make_port(options=options)._build_path(), '/mock-checkout/xcodebuild/Release')
+
+        # Test that "out" is used as the default.
+        options = optparse.Values({'configuration': 'Release', 'build_directory': None})
+        self.assertEqual(self.make_port(options=options)._build_path(), '/mock-checkout/out/Release')
 
     def test_dont_require_http_server(self):
         port = self.make_port()
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/factory.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/factory.py
index cdbc1e10..09fcd92 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/factory.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/factory.py
@@ -165,7 +165,7 @@
     """Returns the configuration to used based on args.gn, if possible."""
 
     # TODO(qyearsley): Default to 'out' everywhere.
-    build_directory = getattr(options, 'build_directory', None) or 'out'
+    build_directory = getattr(options, 'build_directory', 'out')
 
     target = options.target
     finder = PathFinder(fs)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py
index 36e85ec..0542200 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/linux_unittest.py
@@ -84,15 +84,6 @@
     def test_operating_system(self):
         self.assertEqual('linux', self.make_port().operating_system())
 
-    def test_build_path(self):
-        # Test that optional paths are used regardless of whether they exist.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': '/foo'})
-        self.assert_build_path(options, ['/mock-checkout/out/Release'], '/foo/Release')
-
-        # Test that optional relative paths are returned unmodified.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': 'foo'})
-        self.assert_build_path(options, ['/mock-checkout/out/Release'], 'foo/Release')
-
     def test_driver_name_option(self):
         self.assertTrue(self.make_port()._path_to_driver().endswith('content_shell'))
         port = self.make_port(options=optparse.Values({'driver_name': 'OtherDriver'}))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py
index 680e06d2..c898e85 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac.py
@@ -54,8 +54,6 @@
     FALLBACK_PATHS['mac10.9'] = ['mac-mac10.9'] + FALLBACK_PATHS['mac10.10']
     FALLBACK_PATHS['retina'] = ['mac-retina', 'mac']
 
-    DEFAULT_BUILD_DIRECTORIES = ('xcodebuild', 'out')
-
     CONTENT_SHELL_NAME = 'Content Shell'
 
     BUILD_REQUIREMENTS_URL = 'https://chromium.googlesource.com/chromium/src/+/master/docs/mac_build_instructions.md'
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
index 5310598..6e2e7c5 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/mac_unittest.py
@@ -46,32 +46,6 @@
     def test_operating_system(self):
         self.assertEqual('mac', self.make_port().operating_system())
 
-    def test_build_path(self):
-        # Test that optional paths are used regardless of whether they exist.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': '/foo'})
-        self.assert_build_path(options, ['/mock-checkout/out/Release'], '/foo/Release')
-
-        # Test that optional relative paths are returned unmodified.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': 'foo'})
-        self.assert_build_path(options, ['/mock-checkout/out/Release'], 'foo/Release')
-
-        # Test that we prefer the legacy dir over the new dir.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': None})
-        self.assert_build_path(
-            options, ['/mock-checkout/xcodebuild/Release', '/mock-checkout/out/Release'], '/mock-checkout/xcodebuild/Release')
-
-    def test_build_path_timestamps(self):
-        options = optparse.Values({'configuration': 'Release', 'build_directory': None})
-        port = self.make_port(options=options)
-        port.host.filesystem.maybe_make_directory('/mock-checkout/out/Release')
-        port.host.filesystem.maybe_make_directory('/mock-checkout/xcodebuild/Release')
-        # Check with 'out' being newer.
-        port.host.filesystem.mtime = lambda f: 5 if '/out/' in f else 4
-        self.assertEqual(port._build_path(), '/mock-checkout/out/Release')
-        # Check with 'xcodebuild' being newer.
-        port.host.filesystem.mtime = lambda f: 5 if '/xcodebuild/' in f else 4
-        self.assertEqual(port._build_path(), '/mock-checkout/xcodebuild/Release')
-
     def test_driver_name_option(self):
         self.assertTrue(self.make_port()._path_to_driver().endswith('Content Shell'))
         port = self.make_port(options=optparse.Values(dict(driver_name='OtherDriver')))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
index 955a9fd4..a5f8e1f 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/port_testcase.py
@@ -219,12 +219,6 @@
                           u'STDOUT: foo\ufffdbar\n'
                           u'STDERR: foo\ufffdbar\n'))
 
-    def assert_build_path(self, options, dirs, expected_path):
-        port = self.make_port(options=options)
-        for directory in dirs:
-            port.host.filesystem.maybe_make_directory(directory)
-        self.assertEqual(port._build_path(), expected_path)
-
     def test_expectations_files(self):
         port = self.make_port()
         self.assertEqual(port.expectations_files(), [
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py
index 3f5c227b..54c6a02 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win.py
@@ -56,8 +56,6 @@
     FALLBACK_PATHS = {'win10': ['win']}
     FALLBACK_PATHS['win7'] = ['win7'] + FALLBACK_PATHS['win10']
 
-    DEFAULT_BUILD_DIRECTORIES = ('build', 'out')
-
     BUILD_REQUIREMENTS_URL = 'https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md'
 
     @classmethod
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
index bbfdc7f..fc3f9de 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/port/win_unittest.py
@@ -100,31 +100,6 @@
         self.assert_baseline_paths('win-win7', 'win7', '/win')
         self.assert_baseline_paths('win-win10', 'win')
 
-    def test_build_path(self):
-        # Test that optional paths are used regardless of whether they exist.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': '/foo'})
-        self.assert_build_path(options, ['/mock-checkout/out/Release'], '/foo/Release')
-
-        # Test that optional relative paths are returned unmodified.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': 'foo'})
-        self.assert_build_path(options, ['/mock-checkout/out/Release'], 'foo/Release')
-
-        # Test that we prefer the legacy dir over the new dir.
-        options = optparse.Values({'configuration': 'Release', 'build_directory': None})
-        self.assert_build_path(options, ['/mock-checkout/build/Release', '/mock-checkout/out'], '/mock-checkout/build/Release')
-
-    def test_build_path_timestamps(self):
-        options = optparse.Values({'configuration': 'Release', 'build_directory': None})
-        port = self.make_port(options=options)
-        port.host.filesystem.maybe_make_directory('/mock-checkout/out/Release')
-        port.host.filesystem.maybe_make_directory('/mock-checkout/build/Release')
-        # Check with 'out' being newer.
-        port.host.filesystem.mtime = lambda f: 5 if '/out/' in f else 4
-        self.assertEqual(port._build_path(), '/mock-checkout/out/Release')
-        # Check with 'build' being newer.
-        port.host.filesystem.mtime = lambda f: 5 if '/build/' in f else 4
-        self.assertEqual(port._build_path(), '/mock-checkout/build/Release')
-
     def test_operating_system(self):
         self.assertEqual('win', self.make_port().operating_system())
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
index d5368f51..fcc8191 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
@@ -147,8 +147,9 @@
                       'multiple search path entries.')),
             optparse.make_option(
                 '--build-directory',
-                help=('Path to the directory under which build files are kept (should not '
-                      'include configuration)')),
+                default='out',
+                help=('Path to the directory where build files are kept, not including '
+                      'configuration. In general this will be "out".')),
             optparse.make_option(
                 '--clobber-old-results',
                 action='store_true',
diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h
index 96abe0e..cc072e9b 100644
--- a/third_party/WebKit/public/platform/Platform.h
+++ b/third_party/WebKit/public/platform/Platform.h
@@ -58,6 +58,7 @@
 #include "WebURLLoader.h"
 #include "WebVector.h"
 #include "base/metrics/user_metrics_action.h"
+#include "base/time/time.h"
 #include "cc/resources/shared_bitmap.h"
 #include "cc/surfaces/frame_sink_id.h"
 #include "mojo/public/cpp/system/data_pipe.h"
@@ -350,7 +351,7 @@
 
   // A suggestion to cache this metadata in association with this URL.
   virtual void CacheMetadata(const WebURL&,
-                             int64_t response_time,
+                             base::Time response_time,
                              const char* data,
                              size_t data_size) {}
 
@@ -358,7 +359,7 @@
   // resource is in CacheStorage.
   virtual void CacheMetadataInCacheStorage(
       const WebURL&,
-      int64_t response_time,
+      base::Time response_time,
       const char* data,
       size_t data_size,
       const blink::WebSecurityOrigin& cache_storage_origin,
diff --git a/third_party/WebKit/public/platform/WebURLResponse.h b/third_party/WebKit/public/platform/WebURLResponse.h
index d00f6f1..e0b76eaa 100644
--- a/third_party/WebKit/public/platform/WebURLResponse.h
+++ b/third_party/WebKit/public/platform/WebURLResponse.h
@@ -32,6 +32,8 @@
 #define WebURLResponse_h
 
 #include <memory>
+
+#include "base/time/time.h"
 #include "net/http/http_response_info.h"
 #include "public/platform/WebCString.h"
 #include "public/platform/WebCommon.h"
@@ -160,7 +162,7 @@
 
   BLINK_PLATFORM_EXPORT void SetHTTPLoadInfo(const WebHTTPLoadInfo&);
 
-  BLINK_PLATFORM_EXPORT void SetResponseTime(long long);
+  BLINK_PLATFORM_EXPORT void SetResponseTime(base::Time);
 
   BLINK_PLATFORM_EXPORT WebString MimeType() const;
   BLINK_PLATFORM_EXPORT void SetMIMEType(const WebString&);
diff --git a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h
index 8241b96..8942e4d 100644
--- a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h
+++ b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h
@@ -5,6 +5,7 @@
 #ifndef WebServiceWorkerResponse_h
 #define WebServiceWorkerResponse_h
 
+#include "base/time/time.h"
 #include "public/platform/WebCommon.h"
 #include "public/platform/WebPrivatePtr.h"
 #include "public/platform/WebString.h"
@@ -74,8 +75,8 @@
   void SetError(WebServiceWorkerResponseError);
   WebServiceWorkerResponseError GetError() const;
 
-  void SetResponseTime(int64_t);
-  int64_t ResponseTime() const;
+  void SetResponseTime(base::Time);
+  base::Time ResponseTime() const;
 
   void SetCacheStorageCacheName(const WebString&);
   const WebString& CacheStorageCacheName() const;
diff --git a/tools/perf/benchmarks/webrtc.py b/tools/perf/benchmarks/webrtc.py
index eb2aeb9f..ffc2e453 100644
--- a/tools/perf/benchmarks/webrtc.py
+++ b/tools/perf/benchmarks/webrtc.py
@@ -27,8 +27,7 @@
         '-*',
         'toplevel',
         # WebRTC
-        # TODO(ehmaldonado): Re-enable once http://crbug.com/725502 is fixed.
-        # 'webrtc',
+        'webmediaplayerms',
     ]
 
     category_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter(
@@ -36,8 +35,7 @@
     options = timeline_based_measurement.Options(category_filter)
     options.SetTimelineBasedMetrics([
         'cpuTimeMetric',
-        # TODO(ehmaldonado): Re-enable once http://crbug.com/725502 is fixed.
-        # 'webrtcRenderingMetric',
+        'webrtcRenderingMetric',
     ])
     return options
 
diff --git a/tools/perf/page_sets/simple_mobile_sites.py b/tools/perf/page_sets/simple_mobile_sites.py
index 032e8049d..9ac21b8 100644
--- a/tools/perf/page_sets/simple_mobile_sites.py
+++ b/tools/perf/page_sets/simple_mobile_sites.py
@@ -44,7 +44,6 @@
       # Why: Scrolls moderately complex pages (up to 60 layers)
       'http://www.ebay.co.uk/',
       'https://www.flickr.com/',
-      'http://www.apple.com/mac/',
       'http://www.nyc.gov',
       'http://m.nytimes.com/'
     ]