diff --git a/DEPS b/DEPS
index 4a602724..86d0c14 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '9b0b32fda4871776eb9afdf9553e523e5c28aa63',
+  'skia_revision': '445b5573613179c10d5d9c28f82aa8ed94390aea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # 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': 'ce94cbb62fa88fc712dde310185343f60f851b9f',
+  'catapult_revision': '123b9d8ec2a233053e908750d1ce04c7c16fc3d8',
   # 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/android_webview/common/crash_reporter/crash_keys.cc b/android_webview/common/crash_reporter/crash_keys.cc
index ed8297a9..06eb307 100644
--- a/android_webview/common/crash_reporter/crash_keys.cc
+++ b/android_webview/common/crash_reporter/crash_keys.cc
@@ -112,10 +112,6 @@
 
       // Temporary for https://crbug.com/685996.
       {"user-cloud-policy-manager-connect-trace", kMediumSize},
-
-      // TODO(bcwhite): Remove after fixing https://crbug.com/736675
-      {"bad_histogram", kMediumSize},
-      {"from_location", kMediumSize},
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 0c9812cc..6d82885 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -83,6 +83,11 @@
 const char kHighContrastToggleAccelNotificationId[] =
     "chrome://settings/accessibility/highcontrast";
 
+// Toast id and duration for voice interaction shortcuts
+const char kSecondaryUserToastId[] = "voice_interaction_secondary_user";
+const char kUnsupportedLocaleToastId[] = "voice_interaction_locale_unsupported";
+const int kToastDurationMs = 2500;
+
 // The notification delegate that will be used to open the keyboard shortcut
 // help page when the notification is clicked.
 class DeprecatedAcceleratorNotificationDelegate
@@ -603,16 +608,29 @@
         base::UserMetricsAction("VoiceInteraction.Started.Assistant"));
   }
 
-  // Show a toast if voice interaction is disabled due to unsupported locales.
-  if (!chromeos::switches::IsVoiceInteractionLocalesSupported()) {
+  // Show a toast if the active user is not primary.
+  if (Shell::Get()->session_controller()->GetPrimaryUserSession() !=
+      Shell::Get()->session_controller()->GetUserSession(0)) {
     ash::ToastData toast(
-        "voice_interaction_locales_unsupported",
+        kSecondaryUserToastId,
         l10n_util::GetStringUTF16(
-            IDS_ASH_VOICE_INTERACTION_LOCALE_UNSUPPORTED_TOAST_MESSAGE),
-        2500, base::Optional<base::string16>());
+            IDS_ASH_VOICE_INTERACTION_SECONDARY_USER_TOAST_MESSAGE),
+        kToastDurationMs, base::Optional<base::string16>());
     ash::Shell::Get()->toast_manager()->Show(toast);
     return;
   }
+
+  // Show a toast if voice interaction is disabled due to unsupported locales.
+  if (!chromeos::switches::IsVoiceInteractionLocalesSupported()) {
+    ash::ToastData toast(
+        kUnsupportedLocaleToastId,
+        l10n_util::GetStringUTF16(
+            IDS_ASH_VOICE_INTERACTION_LOCALE_UNSUPPORTED_TOAST_MESSAGE),
+        kToastDurationMs, base::Optional<base::string16>());
+    ash::Shell::Get()->toast_manager()->Show(toast);
+    return;
+  }
+
   Shell::Get()->app_list()->ToggleVoiceInteractionSession();
 }
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 2bb4fe9..941281a2 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -450,6 +450,9 @@
       <message name="IDS_ASH_VOICE_INTERACTION_LOCALE_UNSUPPORTED_TOAST_MESSAGE" desc="Message content on the toast that appears when the voice interaction shortcut is pressed but the locale is unsupported.">
         The Google Assistant doesn’t speak this language.
       </message>
+      <message name="IDS_ASH_VOICE_INTERACTION_SECONDARY_USER_TOAST_MESSAGE" desc="Message content on the toast that appears when the voice interaction shortcut is pressed but the locale is unsupported.">
+        Assistant is only available for primary profile.
+      </message>
 
       <message name="IDS_ASH_TOAST_DISMISS_BUTTON" desc="The text button shown in toasts to close the toast immediately without waiting timeout.">
         DISMISS
diff --git a/ash/shell.cc b/ash/shell.cc
index e02b64d..224057b 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -695,8 +695,6 @@
   aura::client::GetFocusClient(GetPrimaryRootWindow())->FocusWindow(nullptr);
 
   // Please keep in reverse order as in Init() because it's easy to miss one.
-  split_view_controller_.reset();
-
   if (window_modality_controller_)
     window_modality_controller_.reset();
 
@@ -765,6 +763,10 @@
   window_cycle_controller_.reset();
   window_selector_controller_.reset();
 
+  // |split_view_controller_| needs to be deleted after
+  // |window_selector_controller_|.
+  split_view_controller_.reset();
+
   CloseAllRootWindowChildWindows();
 
   // MruWindowTracker must be destroyed after all windows have been deleted to
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index bc8e9f4..0b46e90 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -210,8 +210,7 @@
           this,
           base::ASCIIToUTF16("Show a web/app notification"))),
       show_views_examples_callback_(show_views_examples_callback) {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   SetBorder(views::CreateEmptyBorder(gfx::Insets(5)));
   views::ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
diff --git a/ash/system/date/date_view.cc b/ash/system/date/date_view.cc
index 213b25e..434e34a 100644
--- a/ash/system/date/date_view.cc
+++ b/ash/system/date/date_view.cc
@@ -262,8 +262,7 @@
     AddChildView(horizontal_label_.get());
   } else {
     RemoveChildView(horizontal_label_.get());
-    views::GridLayout* layout = new views::GridLayout(this);
-    SetLayoutManager(layout);
+    views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
     const int kColumnId = 0;
     views::ColumnSet* columns = layout->AddColumnSet(kColumnId);
     columns->AddPaddingColumn(0, kVerticalClockLeftPadding);
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 552eca22..d20e84a3 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -216,7 +216,7 @@
 }
 
 bool PaletteTray::ContainsPointInScreen(const gfx::Point& point) {
-  if (icon_ && icon_->GetBoundsInScreen().Contains(point))
+  if (GetBoundsInScreen().Contains(point))
     return true;
 
   return bubble_ && bubble_->bubble_view()->GetBoundsInScreen().Contains(point);
diff --git a/ash/system/status_area_widget_delegate.cc b/ash/system/status_area_widget_delegate.cc
index 3c44e5f..ab5e6056f 100644
--- a/ash/system/status_area_widget_delegate.cc
+++ b/ash/system/status_area_widget_delegate.cc
@@ -139,8 +139,7 @@
 void StatusAreaWidgetDelegate::UpdateLayout() {
   // Use a grid layout so that the trays can be centered in each cell, and
   // so that the widget gets laid out correctly when tray sizes change.
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   // Update tray border based on layout.
   bool is_child_on_edge = true;
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index da677bb..a70ccdc 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -90,6 +90,10 @@
   }
 }
 
+void OverviewWindowDragController::ResetWindowSelector() {
+  window_selector_ = nullptr;
+}
+
 void OverviewWindowDragController::UpdatePhantomWindowAndWindowGrid(
     const gfx::Point& location_in_screen) {
   SplitViewController::SnapPosition last_snap_position = snap_position_;
@@ -174,12 +178,8 @@
   // |item_| will be deleted after RemoveWindowSelectorItem().
   aura::Window* window = item_->GetWindow();
   window_selector_->RemoveWindowSelectorItem(item_);
-  item_ = nullptr;
-
-  // Note: SplitViewController::SnapWindow() might end of overview mode if two
-  // windows are snapped to both side of the screen. In this case the deletion
-  // of WindowSelector will then delete OverviewWindowDragController.
   split_view_controller_->SnapWindow(window, snap_position);
+  item_ = nullptr;
 }
 
 }  // namespace ash
diff --git a/ash/wm/overview/overview_window_drag_controller.h b/ash/wm/overview/overview_window_drag_controller.h
index 89f8d94..61a9e06 100644
--- a/ash/wm/overview/overview_window_drag_controller.h
+++ b/ash/wm/overview/overview_window_drag_controller.h
@@ -31,6 +31,12 @@
   void Drag(const gfx::Point& location_in_screen);
   void CompleteDrag();
 
+  // Resets |window_selector_| to nullptr. It's needed since we defer the
+  // deletion of OverviewWindowDragController in WindowSelector destructor and
+  // we need to reset |window_selector_| to nullptr to avoid null pointer
+  // dereference.
+  void ResetWindowSelector();
+
   WindowSelectorItem* item() { return item_; }
 
  private:
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 1a27d7d..3b98dc3 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -30,6 +30,7 @@
 #include "base/command_line.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "components/vector_icons/vector_icons.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -252,7 +253,14 @@
 }
 
 WindowSelector::~WindowSelector() {
-  RemoveAllObservers();
+  DCHECK(observed_windows_.empty());
+  // Don't delete |window_drag_controller_| yet since the stack might be still
+  // using it.
+  if (window_drag_controller_) {
+    window_drag_controller_->ResetWindowSelector();
+    base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
+        FROM_HERE, window_drag_controller_.release());
+  }
 }
 
 // NOTE: The work done in Init() is not done in the constructor because it may
@@ -400,6 +408,7 @@
 void WindowSelector::RemoveAllObservers() {
   for (auto* window : observed_windows_)
     window->RemoveObserver(this);
+  observed_windows_.clear();
 
   Shell::Get()->activation_client()->RemoveObserver(this);
   display::Screen::GetScreen()->RemoveObserver(this);
@@ -498,6 +507,8 @@
   for (std::unique_ptr<WindowGrid>& grid : grid_list_) {
     if (grid->Contains(item->GetWindow())) {
       grid->RemoveItem(item);
+      if (grid->empty())
+        OnGridEmpty(grid.get());
       break;
     }
   }
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index a69f44f2..8ff60b1 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -30,6 +30,11 @@
        delayed_animations_) {
     animation_observer->Shutdown();
   }
+
+  if (window_selector_.get()) {
+    window_selector_->Shutdown();
+    window_selector_.reset();
+  }
 }
 
 // static
@@ -139,7 +144,9 @@
 // windows, so we can remove WindowSelectorDelegate.
 void WindowSelectorController::OnSelectionEnded() {
   window_selector_->Shutdown();
-  window_selector_.reset();
+  // Don't delete |window_selector_| yet since the stack is still using it.
+  base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
+                                                  window_selector_.release());
   last_selection_time_ = base::Time::Now();
   Shell::Get()->NotifyOverviewModeEnded();
 }
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index e25f7b5f..5d049e1 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -921,6 +921,8 @@
 
   // Exit overview.
   ToggleOverview();
+  base::RunLoop().RunUntilIdle();
+
   EXPECT_TRUE(window1->IsVisible());
   EXPECT_TRUE(window2->IsVisible());
 }
@@ -1238,15 +1240,15 @@
   gfx::Rect bounds(0, 0, 400, 400);
   // These windows will be deleted when the test exits and the Shell instance
   // is shut down.
-  aura::Window* window1(CreateWindow(bounds));
-  aura::Window* window2(CreateWindow(bounds));
-  aura::Window* window3(CreatePanelWindow(bounds));
-  aura::Window* window4(CreatePanelWindow(bounds));
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
+  std::unique_ptr<aura::Window> window3(CreatePanelWindow(bounds));
+  std::unique_ptr<aura::Window> window4(CreatePanelWindow(bounds));
 
-  wm::ActivateWindow(window4);
-  wm::ActivateWindow(window3);
-  wm::ActivateWindow(window2);
-  wm::ActivateWindow(window1);
+  wm::ActivateWindow(window4.get());
+  wm::ActivateWindow(window3.get());
+  wm::ActivateWindow(window2.get());
+  wm::ActivateWindow(window1.get());
 
   ToggleOverview();
 }
@@ -2067,4 +2069,35 @@
   ToggleOverview();
 }
 
+// Tests that if there is only one window in the MRU window list in the overview
+// mode, snapping the window to one side of the screen will end the overview
+// mode since there is no more window left in the overview window grid.
+TEST_F(WindowSelectorTest, EmptyWindowsListExitOverview) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      switches::kAshEnableTabletSplitView);
+  Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true);
+  const gfx::Rect bounds(0, 0, 400, 400);
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+
+  ToggleOverview();
+  EXPECT_TRUE(window_selector_controller()->IsSelecting());
+
+  // Drag |window1| selector item to snap to left.
+  const int grid_index = 0;
+  WindowSelectorItem* selector_item1 =
+      GetWindowItemForWindow(grid_index, window1.get());
+  const gfx::Rect selector_item_bounds1 = selector_item1->target_bounds();
+  // Start drag in the middle of the seletor item.
+  const gfx::Point start_location1(selector_item_bounds1.CenterPoint());
+  window_selector()->InitiateDrag(selector_item1, start_location1);
+  const gfx::Point end_location1(0, 0);
+  window_selector()->Drag(selector_item1, end_location1);
+  window_selector()->CompleteDrag(selector_item1);
+
+  EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true);
+  EXPECT_EQ(split_view_controller()->state(),
+            SplitViewController::LEFT_SNAPPED);
+  EXPECT_FALSE(window_selector_controller()->IsSelecting());
+}
+
 }  // namespace ash
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 82f7a7fd..19c9512a 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -239,6 +239,7 @@
     "containers/linked_list.h",
     "containers/mru_cache.h",
     "containers/small_map.h",
+    "containers/span.h",
     "containers/stack_container.h",
     "containers/vector_buffer.h",
     "cpu.cc",
diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc
index 511155b..10c5636 100644
--- a/base/debug/task_annotator.cc
+++ b/base/debug/task_annotator.cc
@@ -8,11 +8,9 @@
 
 #include "base/debug/activity_tracker.h"
 #include "base/debug/alias.h"
-#include "base/metrics/statistics_recorder.h"  // crbug/744734
 #include "base/pending_task.h"
 #include "base/trace_event/trace_event.h"
 #include "base/tracked_objects.h"
-#include "build/build_config.h"  // crbug/744734
 
 namespace base {
 namespace debug {
@@ -35,13 +33,6 @@
                             PendingTask* pending_task) {
   ScopedTaskRunActivity task_activity(*pending_task);
 
-#if defined(OS_ANDROID)
-  // Try to find the source of heap object corruption by validating all
-  // histogram objects before and after each posted task.
-  // TODO(bcwhite): Remove this after successful canary build.
-  base::StatisticsRecorder::ValidateAllHistograms(nullptr);
-#endif
-
   tracked_objects::TaskStopwatch stopwatch;
   stopwatch.Start();
   base::TimeDelta queue_duration =
@@ -70,13 +61,6 @@
   stopwatch.Stop();
   tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(*pending_task,
                                                                stopwatch);
-
-#if defined(OS_ANDROID)
-  // Try to find the source of heap object corruption by validating all
-  // histogram objects before and after each posted task.
-  // TODO(bcwhite): Remove this after successful canary build.
-  base::StatisticsRecorder::ValidateAllHistograms(&pending_task->posted_from);
-#endif
 }
 
 uint64_t TaskAnnotator::GetTaskTraceID(const PendingTask& task) const {
diff --git a/base/files/file_path.h b/base/files/file_path.h
index 31d419c3..a4ced94 100644
--- a/base/files/file_path.h
+++ b/base/files/file_path.h
@@ -451,13 +451,8 @@
   StringType path_;
 };
 
-// This is required by googletest to print a readable output on test failures.
-// This is declared here for use in gtest-based unit tests but is defined in
-// the test_support_base target. Depend on that to use this in your unit test.
-// This should not be used in production code - call ToString() instead.
-void PrintTo(const FilePath& path, std::ostream* out);
-
-std::ostream& operator<<(std::ostream& out, const FilePath& file_path);
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+                                     const FilePath& file_path);
 
 }  // namespace base
 
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc
index dbc47d15f..eba9a96 100644
--- a/base/files/file_path_unittest.cc
+++ b/base/files/file_path_unittest.cc
@@ -1287,12 +1287,11 @@
 }
 #endif
 
-// Test the PrintTo overload for FilePath (used when a test fails to compare two
-// FilePaths).
-TEST_F(FilePathTest, PrintTo) {
+// Test the operator<<(ostream, FilePath).
+TEST_F(FilePathTest, PrintToOstream) {
   std::stringstream ss;
   FilePath fp(FPL("foo"));
-  base::PrintTo(fp, &ss);
+  ss << fp;
   EXPECT_EQ("foo", ss.str());
 }
 
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 84cd3a4..f3683f0 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -559,10 +559,20 @@
   };
 
   uint32_t bad_fields = 0;
-  if (histogram_name().length() > 20 && histogram_name().at(20) == '\0')
+  if (!unlogged_samples_)
+    bad_fields |= 1 << kUnloggedSamplesField;
+  else if (!unlogged_samples_->bucket_ranges())
+    bad_fields |= 1 << kUnloggedBucketRangesField;
+  if (!logged_samples_)
+    bad_fields |= 1 << kLoggedSamplesField;
+  else if (!logged_samples_->bucket_ranges())
+    bad_fields |= 1 << kLoggedBucketRangesField;
+  else if (logged_samples_->id() == 0)
+    bad_fields |= 1 << kIdField;
+  else if (HashMetricName(histogram_name()) != logged_samples_->id())
     bad_fields |= 1 << kHistogramNameField;
-  else if (histogram_name().length() > 40 && histogram_name().at(40) == '\0')
-    bad_fields |= 1 << kHistogramNameField;
+  if (flags() == 0)
+    bad_fields |= 1 << kFlagsField;
   if (dummy_ != kDummyValue)
     bad_fields |= 1 << kDummyField;
 
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc
index c125ff9..fc044f7 100644
--- a/base/metrics/statistics_recorder.cc
+++ b/base/metrics/statistics_recorder.cc
@@ -7,10 +7,8 @@
 #include <memory>
 
 #include "base/at_exit.h"
-#include "base/debug/crash_logging.h"  // crbug/744734
 #include "base/debug/leak_annotations.h"
 #include "base/json/string_escape.h"
-#include "base/location.h"  // crbug/744734
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram.h"
@@ -20,7 +18,6 @@
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
-#include "build/build_config.h"  // crbug/744734
 
 namespace {
 
@@ -302,26 +299,22 @@
 }
 
 // static
-void StatisticsRecorder::ValidateAllHistograms(
-    tracked_objects::Location* location) {
+void StatisticsRecorder::ValidateAllHistograms() {
   ImportGlobalPersistentHistograms();
 
   auto known = GetKnownHistograms(/*include_persistent=*/true);
 
+  HistogramBase* last_invalid_histogram = nullptr;
+  int invalid_count = 0;
   for (HistogramBase* h : known) {
-    const bool is_valid = h->ValidateHistogramContents(!location, 0);
+    const bool is_valid = h->ValidateHistogramContents(false, 0);
     if (!is_valid) {
-#if !defined(OS_NACL)
-      // CrashKey is scoped so can't be inside "if (location)" block so the
-      // "!location" parameter to ValidatehistogramContents causes it to
-      // crash there if location is passed as null.
-      const std::string debug_string = base::StringPrintf(
-          "%s:%d", location->file_name(), location->line_number());
-      base::debug::ScopedCrashKey crash_key("from_location", debug_string);
-      h->ValidateHistogramContents(true, 0);
-#endif
+      ++invalid_count;
+      last_invalid_histogram = h;
     }
   }
+  if (last_invalid_histogram)
+    last_invalid_histogram->ValidateHistogramContents(true, invalid_count);
 }
 
 // static
@@ -455,8 +448,6 @@
     bool include_persistent) {
   std::vector<HistogramBase*> known;
   base::AutoLock auto_lock(lock_.Get());
-  if (!histograms_)
-    return known;
 
   known.reserve(histograms_->size());
   for (const auto& h : *histograms_) {
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h
index e7446bd..0a24b504 100644
--- a/base/metrics/statistics_recorder.h
+++ b/base/metrics/statistics_recorder.h
@@ -28,10 +28,6 @@
 #include "base/strings/string_piece.h"
 #include "base/synchronization/lock.h"
 
-namespace tracked_objects {
-class Location;
-}
-
 namespace base {
 
 class BucketRanges;
@@ -141,8 +137,7 @@
                             HistogramSnapshotManager* snapshot_manager);
 
   // TODO(asvitkine): Remove this after crbug/736675.
-  static void ValidateAllHistograms(
-      tracked_objects::Location* location = nullptr);
+  static void ValidateAllHistograms();
 
   // GetSnapshot copies some of the pointers to registered histograms into the
   // caller supplied vector (Histograms). Only histograms which have |query| as
diff --git a/base/process/launch_fuchsia.cc b/base/process/launch_fuchsia.cc
index c3617157..6c3b9d8 100644
--- a/base/process/launch_fuchsia.cc
+++ b/base/process/launch_fuchsia.cc
@@ -10,12 +10,14 @@
 #include <unistd.h>
 
 #include "base/command_line.h"
+#include "base/fuchsia/default_job.h"
 #include "base/logging.h"
 
 namespace base {
 
 namespace {
 
+// TODO(758683): Replace this with a call to LaunchProcess().
 bool GetAppOutputInternal(const std::vector<std::string>& argv,
                           bool include_stderr,
                           std::string* output,
@@ -28,8 +30,8 @@
     argv_cstr.push_back(arg.c_str());
   argv_cstr.push_back(nullptr);
 
-  launchpad_t* lp;
-  launchpad_create(MX_HANDLE_INVALID, argv_cstr[0], &lp);
+  launchpad_t* lp = nullptr;
+  launchpad_create(GetDefaultJob(), argv_cstr[0], &lp);
   launchpad_load_from_file(lp, argv_cstr[0]);
   launchpad_set_args(lp, argv.size(), argv_cstr.data());
   launchpad_clone(lp, LP_CLONE_MXIO_NAMESPACE | LP_CLONE_MXIO_CWD |
@@ -38,7 +40,7 @@
   int pipe_fd;
   mx_status_t status = launchpad_add_pipe(lp, &pipe_fd, STDOUT_FILENO);
   if (status != MX_OK) {
-    LOG(ERROR) << "launchpad_add_pipe failed: " << status;
+    LOG(ERROR) << "launchpad_add_pipe failed: " << mx_status_get_string(status);
     launchpad_destroy(lp);
     return false;
   }
@@ -52,7 +54,8 @@
   const char* errmsg;
   status = launchpad_go(lp, &proc, &errmsg);
   if (status != MX_OK) {
-    LOG(ERROR) << "launchpad_go failed: " << errmsg << ", status=" << status;
+    LOG(ERROR) << "launchpad_go failed: " << errmsg
+               << ", status=" << mx_status_get_string(status);
     return false;
   }
 
@@ -89,8 +92,12 @@
   // used in a "builder" style. From launchpad_create() to launchpad_go() the
   // status is tracked in the launchpad_t object, and launchpad_go() reports on
   // the final status, and cleans up |lp| (assuming it was even created).
-  launchpad_t* lp;
-  launchpad_create(options.job_handle, argv_cstr[0], &lp);
+  launchpad_t* lp = nullptr;
+  mx_handle_t job = options.job_handle != MX_HANDLE_INVALID ? options.job_handle
+                                                            : GetDefaultJob();
+  DCHECK_NE(MX_HANDLE_INVALID, job);
+
+  launchpad_create(job, argv_cstr[0], &lp);
   launchpad_load_from_file(lp, argv_cstr[0]);
   launchpad_set_args(lp, argv.size(), argv_cstr.data());
 
@@ -109,6 +116,22 @@
     to_clone |= LP_CLONE_MXIO_CWD;
   }
 
+  if (to_clone & LP_CLONE_DEFAULT_JOB) {
+    // Override Fuchsia's built in default job cloning behavior with our own
+    // logic which uses |job| instead of mx_job_default().
+    // This logic is based on the launchpad implementation.
+    mx_handle_t job_duplicate = MX_HANDLE_INVALID;
+    mx_status_t status =
+        mx_handle_duplicate(job, MX_RIGHT_SAME_RIGHTS, &job_duplicate);
+    if (status != MX_OK) {
+      LOG(ERROR) << "mx_handle_duplicate(job): "
+                 << mx_status_get_string(status);
+      return Process();
+    }
+    launchpad_add_handle(lp, job_duplicate, PA_HND(PA_JOB_DEFAULT, 0));
+    to_clone &= ~LP_CLONE_DEFAULT_JOB;
+  }
+
   if (!environ_modifications.empty())
     new_environ = AlterEnvironment(old_environ, environ_modifications);
 
@@ -141,7 +164,8 @@
   const char* errmsg;
   mx_status_t status = launchpad_go(lp, &proc, &errmsg);
   if (status != MX_OK) {
-    LOG(ERROR) << "launchpad_go failed: " << errmsg << ", status=" << status;
+    LOG(ERROR) << "launchpad_go failed: " << errmsg
+               << ", status=" << mx_status_get_string(status);
     return Process();
   }
 
diff --git a/base/process/process_fuchsia.cc b/base/process/process_fuchsia.cc
index 0c1b49b..43efbf0 100644
--- a/base/process/process_fuchsia.cc
+++ b/base/process/process_fuchsia.cc
@@ -8,6 +8,7 @@
 #include <magenta/syscalls.h>
 
 #include "base/debug/activity_tracker.h"
+#include "base/fuchsia/default_job.h"
 #include "base/strings/stringprintf.h"
 
 namespace base {
@@ -50,7 +51,7 @@
   // mx_job_default() might not contain it, so this call can fail.
   ScopedMxHandle handle;
   mx_status_t status = mx_object_get_child(
-      mx_job_default(), pid, MX_RIGHT_SAME_RIGHTS, handle.receive());
+      GetDefaultJob(), pid, MX_RIGHT_SAME_RIGHTS, handle.receive());
   if (status != MX_OK) {
     DLOG(ERROR) << "mx_object_get_child failed: " << status;
     return Process();
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
index 8a23048..e721899 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
@@ -8,6 +8,9 @@
 import android.app.Application;
 import android.app.Instrumentation;
 import android.content.Context;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.internal.runner.RunnerArgs;
@@ -27,10 +30,22 @@
  * This class is the equivalent of BaseChromiumInstrumentationTestRunner in JUnit3. Please
  * beware that is this not a class runner. It is declared in test apk AndroidManifest.xml
  * <instrumentation>
+ *
+ * TODO(yolandyan): remove this class after all tests are converted to JUnit4. Use class runner
+ * for test listing.
  */
 public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
     private static final String LIST_ALL_TESTS_FLAG =
             "org.chromium.base.test.BaseChromiumAndroidJUnitRunner.TestList";
+    private static final String LIST_TESTS_PACKAGE_FLAG =
+            "org.chromium.base.test.BaseChromiumAndroidJUnitRunner.TestListPackage";
+    /**
+     * This flag is supported by AndroidJUnitRunner.
+     *
+     * See the following page for detail
+     * https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html
+     */
+    private static final String ARGUMENT_TEST_PACKAGE = "package";
 
     /**
      * The following arguments are corresponding to AndroidJUnitRunner command line arguments.
@@ -84,6 +99,23 @@
         }
     }
 
+    // TODO(yolandyan): Move this to test harness side once this class gets removed
+    private void addTestListPackage(Bundle bundle) {
+        PackageManager pm = getContext().getPackageManager();
+        InstrumentationInfo info;
+        try {
+            info = pm.getInstrumentationInfo(getComponentName(), PackageManager.GET_META_DATA);
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, String.format("Could not find component %s", getComponentName()));
+            throw new RuntimeException(e);
+        }
+        Bundle metaDataBundle = info.metaData;
+        if (metaDataBundle != null && metaDataBundle.getString(LIST_TESTS_PACKAGE_FLAG) != null) {
+            bundle.putString(
+                    ARGUMENT_TEST_PACKAGE, metaDataBundle.getString(LIST_TESTS_PACKAGE_FLAG));
+        }
+    }
+
     private void listTests() {
         Bundle results = new Bundle();
         TestListInstrumentationRunListener listener = new TestListInstrumentationRunListener();
@@ -92,11 +124,13 @@
             executorBuilder.addRunListener(listener);
             Bundle junit3Arguments = new Bundle(InstrumentationRegistry.getArguments());
             junit3Arguments.putString(ARGUMENT_NOT_ANNOTATION, "org.junit.runner.RunWith");
+            addTestListPackage(junit3Arguments);
             TestRequest listJUnit3TestRequest = createListTestRequest(junit3Arguments);
             results = executorBuilder.build().execute(listJUnit3TestRequest);
 
             Bundle junit4Arguments = new Bundle(InstrumentationRegistry.getArguments());
             junit4Arguments.putString(ARGUMENT_ANNOTATION, "org.junit.runner.RunWith");
+            addTestListPackage(junit4Arguments);
 
             // Do not use Log runner from android test support.
             //
diff --git a/base/test/test_file_util.cc b/base/test/test_file_util.cc
index 40b25f01..8dafc58a 100644
--- a/base/test/test_file_util.cc
+++ b/base/test/test_file_util.cc
@@ -20,9 +20,4 @@
   return false;
 }
 
-// Declared in base/files/file_path.h.
-void PrintTo(const FilePath& path, std::ostream* out) {
-  *out << path.value();
-}
-
 }  // namespace base
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py
old mode 100644
new mode 100755
index a116781..d861b5e
--- a/build/android/apk_operations.py
+++ b/build/android/apk_operations.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 # 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.
@@ -37,25 +38,27 @@
   return getattr(colorama.Fore, color) + text + colorama.Fore.RESET
 
 
-def _InstallApk(apk, install_dict, devices_obj):
+def _InstallApk(devices, apk, install_dict):
   def install(device):
     if install_dict:
       installer.Install(device, install_dict, apk=apk)
     else:
       device.Install(apk)
-  devices_obj.pMap(install)
+
+  logging.info('Installing %sincremental apk.', '' if install_dict else 'non-')
+  device_utils.DeviceUtils.parallel(devices).pMap(install)
 
 
-def _UninstallApk(install_dict, devices_obj, apk_package):
+def _UninstallApk(devices, install_dict, package_name):
   def uninstall(device):
     if install_dict:
-      installer.Uninstall(device, apk_package)
+      installer.Uninstall(device, package_name)
     else:
-      device.Uninstall(apk_package)
-  devices_obj.pMap(uninstall)
+      device.Uninstall(package_name)
+  device_utils.DeviceUtils.parallel(devices).pMap(uninstall)
 
 
-def _LaunchUrl(devices_obj, input_args, device_args_file, url, apk):
+def _LaunchUrl(devices, input_args, device_args_file, url, apk):
   if input_args and device_args_file is None:
     raise Exception('This apk does not support any flags.')
   if url:
@@ -81,17 +84,17 @@
                                     activity=view_activity, data=url,
                                     package=apk.GetPackageName())
       device.StartActivity(launch_intent)
-  devices_obj.pMap(launch)
+  device_utils.DeviceUtils.parallel(devices).pMap(launch)
 
 
-def _ChangeFlags(devices, devices_obj, input_args, device_args_file):
+def _ChangeFlags(devices, input_args, device_args_file):
   if input_args is None:
-    _DisplayArgs(devices, devices_obj, device_args_file)
+    _DisplayArgs(devices, device_args_file)
   else:
     flags = shlex.split(input_args)
     def update(device):
       flag_changer.FlagChanger(device, device_args_file).ReplaceFlags(flags)
-    devices_obj.pMap(update)
+    device_utils.DeviceUtils.parallel(devices).pMap(update)
 
 
 def _TargetCpuToTargetArch(target_cpu):
@@ -102,13 +105,13 @@
   return target_cpu
 
 
-def _RunGdb(apk_name, apk_package, device, target_cpu, extra_args, verbose):
+def _RunGdb(device, package_name, output_directory, target_cpu, extra_args,
+            verbose):
   gdb_script_path = os.path.dirname(__file__) + '/adb_gdb'
   cmd = [
       gdb_script_path,
-      '--program-name=%s' % os.path.splitext(apk_name)[0],
-      '--package-name=%s' % apk_package,
-      '--output-directory=%s' % constants.GetOutDirectory(),
+      '--package-name=%s' % package_name,
+      '--output-directory=%s' % output_directory,
       '--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath(),
       '--device=%s' % device.serial,
       # Use one lib dir per device so that changing between devices does require
@@ -136,16 +139,17 @@
     yield result
 
 
-def _RunMemUsage(devices, devices_obj, apk_package):
+def _RunMemUsage(devices, package_name):
   def mem_usage_helper(d):
     ret = []
-    proc_map = d.GetPids(apk_package)
+    proc_map = d.GetPids(package_name)
     for name, pids in proc_map.iteritems():
       for pid in pids:
         ret.append((name, pid, d.GetMemoryUsageForPid(pid)))
     return ret
 
-  all_results = devices_obj.pMap(mem_usage_helper).pGet(None)
+  parallel_devices = device_utils.DeviceUtils.parallel(devices)
+  all_results = parallel_devices.pMap(mem_usage_helper).pGet(None)
   for result in _PrintPerDeviceOutput(devices, all_results):
     if not result:
       print 'No processes found.'
@@ -201,7 +205,7 @@
     logging.error('Failed to parse du output:\n%s', output)
 
 
-def _RunDiskUsage(devices, devices_obj, apk_package, verbose):
+def _RunDiskUsage(devices, package_name, verbose):
   # Measuring dex size is a bit complicated:
   # https://source.android.com/devices/tech/dalvik/jit-compiler
   #
@@ -250,7 +254,7 @@
   #     cmd package compile -m speed-profile org.chromium.chrome  # For others
   def disk_usage_helper(d):
     package_output = '\n'.join(d.RunShellCommand(
-        ['dumpsys', 'package', apk_package], check_return=True))
+        ['dumpsys', 'package', package_name], check_return=True))
     # Prints a message but does not return error when apk is not installed.
     if 'Unable to find package:' in package_output:
       return None
@@ -274,7 +278,7 @@
       compilation_filters.add(re.sub(r'\s+', '', m.group(1)))
     compilation_filter = ','.join(sorted(compilation_filters))
 
-    data_dir_sizes = _DuHelper(d, '%s/{*,.*}' % data_dir, run_as=apk_package)
+    data_dir_sizes = _DuHelper(d, '%s/{*,.*}' % data_dir, run_as=package_name)
     # Measure code_cache separately since it can be large.
     code_cache_sizes = {}
     code_cache_dir = next(
@@ -282,7 +286,7 @@
     if code_cache_dir:
       data_dir_sizes.pop(code_cache_dir)
       code_cache_sizes = _DuHelper(d, '%s/{*,.*}' % code_cache_dir,
-                                   run_as=apk_package)
+                                   run_as=package_name)
 
     apk_path_spec = code_path
     if not apk_path_spec.endswith('.apk'):
@@ -324,7 +328,8 @@
       for path, size in sorted(sizes.iteritems()):
         print '    %s: %skb' % (path, size)
 
-  all_results = devices_obj.pMap(disk_usage_helper).pGet(None)
+  parallel_devices = device_utils.DeviceUtils.parallel(devices)
+  all_results = parallel_devices.pMap(disk_usage_helper).pGet(None)
   for result in _PrintPerDeviceOutput(devices, all_results):
     if not result:
       print 'APK is not installed.'
@@ -347,8 +352,9 @@
     print 'Total: %skb (%.1fmb)' % (total, total / 1024.0)
 
 
-def _RunPs(devices, devices_obj, apk_package):
-  all_pids = devices_obj.GetPids(apk_package).pGet(None)
+def _RunPs(devices, package_name):
+  parallel_devices = device_utils.DeviceUtils.parallel(devices)
+  all_pids = parallel_devices.GetPids(package_name).pGet(None)
   for proc_map in _PrintPerDeviceOutput(devices, all_pids):
     if not proc_map:
       print 'No processes found.'
@@ -357,9 +363,11 @@
         print name, ','.join(pids)
 
 
-def _RunShell(devices, devices_obj, apk_package, cmd):
+def _RunShell(devices, package_name, cmd):
   if cmd:
-    outputs = devices_obj.RunShellCommand(cmd, run_as=apk_package).pGet(None)
+    parallel_devices = device_utils.DeviceUtils.parallel(devices)
+    outputs = parallel_devices.RunShellCommand(
+        cmd, run_as=package_name).pGet(None)
     for output in _PrintPerDeviceOutput(devices, outputs):
       for line in output:
         print line
@@ -368,25 +376,27 @@
     cmd = [adb_path, '-s', devices[0].serial, 'shell']
     # Pre-N devices do not support -t flag.
     if devices[0].build_version_sdk >= version_codes.NOUGAT:
-      cmd += ['-t', 'run-as', apk_package]
+      cmd += ['-t', 'run-as', package_name]
     else:
       print 'Upon entering the shell, run:'
-      print 'run-as', apk_package
+      print 'run-as', package_name
       print
     os.execv(adb_path, cmd)
 
 
-def _RunCompileDex(devices, devices_obj, apk_package, compilation_filter):
+def _RunCompileDex(devices, package_name, compilation_filter):
   cmd = ['cmd', 'package', 'compile', '-f', '-m', compilation_filter,
-         apk_package]
-  outputs = devices_obj.RunShellCommand(cmd).pGet(None)
+         package_name]
+  parallel_devices = device_utils.DeviceUtils.parallel(devices)
+  outputs = parallel_devices.RunShellCommand(cmd).pGet(None)
   for output in _PrintPerDeviceOutput(devices, outputs):
     for line in output:
       print line
 
 
-# TODO(Yipengw):add "--all" in the MultipleDevicesError message and use it here.
-def _GenerateMissingAllFlagMessage(devices, devices_obj):
+# TODO(agrieve):add "--all" in the MultipleDevicesError message and use it here.
+def _GenerateMissingAllFlagMessage(devices):
+  devices_obj = device_utils.DeviceUtils.parallel(devices)
   descriptions = devices_obj.pMap(lambda d: d.build_description).pGet(None)
   msg = ('More than one device available. Use --all to select all devices, '
          'or use --device to select a device by serial.\n\nAvailable '
@@ -396,102 +406,29 @@
   return msg
 
 
-def _DisplayArgs(devices, devices_obj, device_args_file):
+def _DisplayArgs(devices, device_args_file):
   def flags_helper(d):
     changer = flag_changer.FlagChanger(d, device_args_file)
     return changer.GetCurrentFlags()
 
-  outputs = devices_obj.pMap(flags_helper).pGet(None)
+  parallel_devices = device_utils.DeviceUtils.parallel(devices)
+  outputs = parallel_devices.pMap(flags_helper).pGet(None)
   print 'Existing flags per-device (via /data/local/tmp/%s):' % device_args_file
   for flags in _PrintPerDeviceOutput(devices, outputs, single_line=True):
     quoted_flags = ' '.join(pipes.quote(f) for f in flags)
     print quoted_flags or 'No flags set.'
 
 
-def _AddCommonOptions(parser):
-  parser.add_argument('--all',
-                      action='store_true',
-                      default=False,
-                      help='Operate on all connected devices.',)
-  parser.add_argument('-d',
-                      '--device',
-                      action='append',
-                      default=[],
-                      dest='devices',
-                      help='Target device for script to work on. Enter '
-                           'multiple times for multiple devices.')
-  parser.add_argument('-v',
-                      '--verbose',
-                      action='count',
-                      default=0,
-                      dest='verbose_count',
-                      help='Verbose level (multiple times for more)')
-
-
-def _AddInstallOptions(parser):
-  parser = parser.add_argument_group('install arguments')
-  parser.add_argument('--incremental',
-                      action='store_true',
-                      default=False,
-                      help='Always install an incremental apk.')
-  parser.add_argument('--non-incremental',
-                      action='store_true',
-                      default=False,
-                      help='Always install a non-incremental apk.')
-
-
-def _AddLaunchOptions(parser):
-  parser = parser.add_argument_group('launch arguments')
-  parser.add_argument('url',
-                      nargs='?',
-                      help='The URL this command launches.')
-
-
-def _AddArgsOptions(parser):
-  parser = parser.add_argument_group('argv arguments')
-  parser.add_argument('--args',
-                      help='The flags set by the user.')
-
-
-def _DeviceCachePath(device):
+def _DeviceCachePath(device, output_directory):
   file_name = 'device_cache_%s.json' % device.serial
-  return os.path.join(constants.GetOutDirectory(), file_name)
+  return os.path.join(output_directory, file_name)
 
 
-def _SelectApk(apk_path, incremental_install_json_path, parser, args):
-  if apk_path and not os.path.exists(apk_path):
-    apk_path = None
-  if (incremental_install_json_path and
-      not os.path.exists(incremental_install_json_path)):
-    incremental_install_json_path = None
-
-  if args.command in ('install', 'run'):
-    if args.incremental and args.non_incremental:
-      parser.error('--incremental and --non-incremental cannot both be used.')
-    elif args.non_incremental:
-      if not apk_path:
-        parser.error('Apk has not been built.')
-      incremental_install_json_path = None
-    elif args.incremental:
-      if not incremental_install_json_path:
-        parser.error('Incremental apk has not been built.')
-      apk_path = None
-
-    if apk_path and incremental_install_json_path:
-      parser.error('Both incremental and non-incremental apks exist, please '
-                   'use --incremental or --non-incremental to select one.')
-    elif apk_path:
-      logging.info('Using the non-incremental apk.')
-    elif incremental_install_json_path:
-      logging.info('Using the incremental apk.')
-    else:
-      parser.error('Neither incremental nor non-incremental apk is built.')
-  return apk_path, incremental_install_json_path
-
-
-def _LoadDeviceCaches(devices):
+def _LoadDeviceCaches(devices, output_directory):
+  if not output_directory:
+    return
   for d in devices:
-    cache_path = _DeviceCachePath(d)
+    cache_path = _DeviceCachePath(d, output_directory)
     if os.path.exists(cache_path):
       logging.debug('Using device cache: %s', cache_path)
       with open(cache_path) as f:
@@ -502,187 +439,416 @@
       logging.debug('No cache present for device: %s', d)
 
 
-def _SaveDeviceCaches(devices):
+def _SaveDeviceCaches(devices, output_directory):
+  if not output_directory:
+    return
   for d in devices:
-    cache_path = _DeviceCachePath(d)
+    cache_path = _DeviceCachePath(d, output_directory)
     with open(cache_path, 'w') as f:
       f.write(d.DumpCacheData())
       logging.info('Wrote device cache: %s', cache_path)
 
 
-# target_cpu=None so that old wrapper scripts continue to work without
-# the need for a rebuild.
-def Run(output_directory, apk_path, incremental_install_json_path,
-        command_line_flags_file, target_cpu=None):
-  colorama.init()
-  constants.SetOutputDirectory(output_directory)
+class _Command(object):
+  name = None
+  description = None
+  needs_package_name = False
+  needs_output_directory = False
+  needs_apk_path = False
+  supports_incremental = False
+  accepts_command_line_flags = False
+  accepts_args = False
+  accepts_url = False
+  all_devices_by_default = False
+  calls_exec = False
 
-  parser = argparse.ArgumentParser()
-  command_parsers = parser.add_subparsers(title='Apk operations',
-                                          dest='command')
-  subp = command_parsers.add_parser('install', help='Install the apk.')
-  _AddCommonOptions(subp)
-  _AddInstallOptions(subp)
+  def __init__(self, from_wrapper_script):
+    self._parser = None
+    self._from_wrapper_script = from_wrapper_script
+    self.args = None
+    self.apk_helper = None
+    self.install_dict = None
+    self.devices = None
+    # Do not support incremental install outside the context of wrapper scripts.
+    if not from_wrapper_script:
+      self.supports_incremental = False
 
-  subp = command_parsers.add_parser('uninstall', help='Uninstall the apk.')
-  _AddCommonOptions(subp)
+  def _RegisterExtraArgs(self, subp):
+    pass
 
-  subp = command_parsers.add_parser('launch',
-                                    help='Launches the apk with the given '
-                                    'command-line flags, and optionally the '
-                                    'given URL')
-  _AddCommonOptions(subp)
-  _AddLaunchOptions(subp)
-  _AddArgsOptions(subp)
+  def RegisterArgs(self, parser):
+    subp = parser.add_parser(self.name, help=self.description)
+    self._parser = subp
+    subp.set_defaults(command=self)
+    subp.add_argument('--all',
+                      action='store_true',
+                      default=self.all_devices_by_default,
+                      help='Operate on all connected devices.',)
+    subp.add_argument('-d',
+                      '--device',
+                      action='append',
+                      default=[],
+                      dest='devices',
+                      help='Target device for script to work on. Enter '
+                           'multiple times for multiple devices.')
+    subp.add_argument('-v',
+                      '--verbose',
+                      action='count',
+                      default=0,
+                      dest='verbose_count',
+                      help='Verbose level (multiple times for more)')
+    group = subp.add_argument_group('%s arguments' % self.name)
 
-  subp = command_parsers.add_parser('run', help='Install and launch.')
-  _AddCommonOptions(subp)
-  _AddInstallOptions(subp)
-  _AddLaunchOptions(subp)
-  _AddArgsOptions(subp)
+    if self.needs_package_name:
+      # Always gleaned from apk when using wrapper scripts.
+      group.add_argument('--package-name',
+          help=argparse.SUPPRESS if self._from_wrapper_script else (
+              "App's package name."))
 
-  subp = command_parsers.add_parser('stop', help='Stop apks on all devices')
-  _AddCommonOptions(subp)
+    if self.needs_apk_path or self.needs_package_name:
+      # When passed by wrapper script, don't show in --help.
+      group.add_argument('--apk-path',
+          required=self.needs_apk_path and not self._from_wrapper_script,
+          help=argparse.SUPPRESS if self._from_wrapper_script else (
+              'Path to .apk'))
 
-  subp = command_parsers.add_parser('clear-data',
-                                    help='Clear states for the given package')
-  _AddCommonOptions(subp)
+    if self.supports_incremental:
+      group.add_argument('--incremental',
+                          action='store_true',
+                          default=False,
+                          help='Always install an incremental apk.')
+      group.add_argument('--non-incremental',
+                          action='store_true',
+                          default=False,
+                          help='Always install a non-incremental apk.')
 
-  subp = command_parsers.add_parser('argv',
-                                    help='Display and update flags on devices.')
-  _AddCommonOptions(subp)
-  _AddArgsOptions(subp)
+    # accepts_command_line_flags and accepts_args are mutually exclusive.
+    # argparse will throw if they are both set.
+    if self.accepts_command_line_flags:
+      group.add_argument('--args', help='Command-line flags.')
 
-  subp = command_parsers.add_parser('gdb',
-                                    help='Run build/android/adb_gdb script.')
-  _AddCommonOptions(subp)
-  _AddArgsOptions(subp)
+    if self.accepts_args:
+      group.add_argument('--args', help='Extra arguments.')
 
-  subp = command_parsers.add_parser('logcat',
-                                    help='Run the shell command "adb logcat".')
-  _AddCommonOptions(subp)
+    if self.accepts_url:
+      group.add_argument('url', nargs='?', help='A URL to launch with.')
 
-  subp = command_parsers.add_parser('disk-usage',
-      help='Display disk usage for the APK.')
-  _AddCommonOptions(subp)
+    if not self._from_wrapper_script and self.accepts_command_line_flags:
+      # Provided by wrapper scripts.
+      group.add_argument(
+          '--command-line-flags-file-name',
+          help='Name of the command-line flags file')
 
-  subp = command_parsers.add_parser('mem-usage',
-      help='Display memory usage of currently running APK processes.')
-  _AddCommonOptions(subp)
+    self._RegisterExtraArgs(group)
 
-  subp = command_parsers.add_parser('ps',
-      help='Shows PIDs of any APK processes currently running.')
-  _AddCommonOptions(subp)
+  def ProcessArgs(self, args):
+    devices = device_utils.DeviceUtils.HealthyDevices(
+        device_arg=args.devices,
+        enable_device_files_cache=bool(args.output_directory),
+        default_retries=0)
+    self.args = args
+    self.devices = devices
+    # TODO(agrieve): Device cache should not depend on output directory.
+    #     Maybe put int /tmp?
+    _LoadDeviceCaches(devices, args.output_directory)
 
-  subp = command_parsers.add_parser('shell',
-      help='Same as "adb shell <command>", but runs as the apk\'s uid (via '
-           'run-as). Useful for inspecting the app\'s data directory.')
-  _AddCommonOptions(subp)
-  group = subp.add_argument_group('shell arguments')
-  group.add_argument('cmd', nargs=argparse.REMAINDER, help='Command to run.')
+    try:
+      if len(devices) > 1:
+        if self.calls_exec:
+          self._parser.error(device_errors.MultipleDevicesError(devices))
+        if not args.all and not args.devices:
+          self._parser.error(_GenerateMissingAllFlagMessage(devices))
 
-  subp = command_parsers.add_parser('compile-dex',
-      help='Applicable only for Android N+. Forces .odex files to be compiled '
-           'with the given compilation filter. To see existing filter, use '
-           '"disk-usage" command.')
-  _AddCommonOptions(subp)
-  group = subp.add_argument_group('compile-dex arguments')
-  # Allow only the most useful subset of filters.
-  group.add_argument('compilation_filter',
-                     choices=['verify', 'quicken', 'space-profile', 'space',
-                              'speed-profile', 'speed'],
-                     help='For WebView/Monochrome, use "speed". '
-                          'For other apks, use "speed-profile".')
+      if self.supports_incremental:
+        if args.incremental and args.non_incremental:
+          self._parser.error('Must use only one of --incremental and '
+                             '--non-incremental')
+        elif args.non_incremental:
+          if not args.apk_path:
+            self._parser.error('Apk has not been built.')
+          args.incremental_json = None
+        elif args.incremental:
+          if not args.incremental_json:
+            self._parser.error('Incremental apk has not been built.')
+          args.apk_path = None
+
+        if args.apk_path and args.incremental_json:
+          self._parser.error('Both incremental and non-incremental apks exist. '
+                             'Select using --incremental or --non-incremental')
+        elif not args.apk_path and not args.incremental_json:
+          self._parser.error(
+              'Neither incremental nor non-incremental apk is built.')
+
+      if self.needs_apk_path or args.apk_path:
+        if args.incremental_json:
+          with open(args.incremental_json) as f:
+            self.install_dict = json.load(f)
+          self.apk_helper = apk_helper.ToHelper(
+              os.path.join(args.output_directory,
+                           self.install_dict['apk_path']))
+        else:
+          self.apk_helper = apk_helper.ToHelper(args.apk_path)
+
+      if self.needs_package_name and not args.package_name:
+        if self.apk_helper:
+          args.package_name = self.apk_helper.GetPackageName()
+        else:
+          self._parser.error('One of --package-name or --apk-path is required.')
+
+      # Save cache now if command will not get a chance to afterwards.
+      if self.calls_exec:
+        _SaveDeviceCaches(devices, args.output_directory)
+    except:
+      _SaveDeviceCaches(devices, args.output_directory)
+      raise
+
+
+class _InstallCommand(_Command):
+  name = 'install'
+  description = 'Installs the APK to one or more devices.'
+  needs_apk_path = True
+  supports_incremental = True
+
+  def Run(self):
+    _InstallApk(self.devices, self.apk_helper, self.install_dict)
+
+
+class _UninstallCommand(_Command):
+  name = 'uninstall'
+  description = 'Removes the APK to one or more devices.'
+  needs_package_name = True
+
+  def Run(self):
+    _UninstallApk(self.devices, self.install_dict, self.args.package_name)
+
+
+class _LaunchCommand(_Command):
+  name = 'launch'
+  description = ('Sends a launch intent for the APK after first writing the '
+                 'command-line flags file.')
+  # TODO(agrieve): Launch could be changed to require only package name by
+  #     parsing "dumpsys package" for launch & view activities.
+  needs_apk_path = True
+  accepts_command_line_flags = True
+  accepts_url = True
+  all_devices_by_default = True
+
+  def Run(self):
+    _LaunchUrl(self.devices, self.args.args, self.args.command_line_flags_file,
+               self.args.url, self.apk_helper)
+
+
+class _RunCommand(_Command):
+  name = 'run'
+  description = 'Install and then launch.'
+  needs_apk_path = True
+  supports_incremental = True
+  needs_package_name = True
+  accepts_command_line_flags = True
+  accepts_url = True
+
+  def Run(self):
+    logging.warning('Installing...')
+    _InstallApk(self.devices, self.apk_helper, self.install_dict)
+    logging.warning('Sending launch intent...')
+    _LaunchUrl(self.devices, self.args.args, self.args.command_line_flags_file,
+               self.args.url, self.apk_helper)
+
+
+class _StopCommand(_Command):
+  name = 'stop'
+  description = 'Force-stops the app.'
+  needs_package_name = True
+  all_devices_by_default = True
+
+  def Run(self):
+    device_utils.DeviceUtils.parallel(self.devices).ForceStop(
+        self.args.package_name)
+
+
+class _ClearDataCommand(_Command):
+  name = 'clear-data'
+  descriptions = 'Clears all app data.'
+  needs_package_name = True
+  all_devices_by_default = True
+
+  def Run(self):
+    device_utils.DeviceUtils.parallel(self.devices).ClearApplicationState(
+        self.args.package_name)
+
+
+class _ArgvCommand(_Command):
+  name = 'argv'
+  description = 'Display and optionally update command-line flags file.'
+  needs_package_name = True
+  accepts_command_line_flags = True
+  all_devices_by_default = True
+
+  def Run(self):
+    _ChangeFlags(self.devices, self.args.args,
+                 self.args.command_line_flags_file)
+
+
+class _GdbCommand(_Command):
+  name = 'gdb'
+  description = 'Runs //build/android/adb_gdb with apk-specific args.'
+  needs_package_name = True
+  needs_output_directory = True
+  accepts_args = True
+  calls_exec = True
+
+  def Run(self):
+    extra_args = shlex.split(self.args.args or '')
+    _RunGdb(self.devices[0], self.args.package_name, self.args.output_directory,
+            self.args.target_cpu, extra_args, bool(self.args.verbose_count))
+
+
+class _LogcatCommand(_Command):
+  name = 'logcat'
+  description = 'Runs "adb logcat"'
+  calls_exec = True
+
+  def Run(self):
+    adb_path = adb_wrapper.AdbWrapper.GetAdbPath()
+    cmd = [adb_path, '-s', self.devices[0].serial, 'logcat']
+    os.execv(adb_path, cmd)
+
+
+class _PsCommand(_Command):
+  name = 'ps'
+  description = 'Show PIDs of any APK processes currently running.'
+  needs_package_name = True
+  all_devices_by_default = True
+
+  def Run(self):
+    _RunPs(self.devices, self.args.package_name)
+
+
+class _DiskUsageCommand(_Command):
+  name = 'disk-usage'
+  description = 'Show how much device storage is being consumed by the app.'
+  needs_package_name = True
+  all_devices_by_default = True
+
+  def Run(self):
+    _RunDiskUsage(self.devices, self.args.package_name,
+                  bool(self.args.verbose_count))
+
+
+class _MemUsageCommand(_Command):
+  name = 'mem-usage'
+  description = 'Show memory usage of currently running APK processes.'
+  needs_package_name = True
+  all_devices_by_default = True
+
+  def Run(self):
+    _RunMemUsage(self.devices, self.args.package_name)
+
+
+class _ShellCommand(_Command):
+  name = 'shell'
+  description = ('Same as "adb shell <command>", but runs as the apk\'s uid '
+                 '(via run-as). Useful for inspecting the app\'s data '
+                 'directory.')
+  needs_package_name = True
+
+  @property
+  def calls_exec(self):
+    return not self.args.cmd
+
+  def _RegisterExtraArgs(self, group):
+    group.add_argument(
+        'cmd', nargs=argparse.REMAINDER, help='Command to run.')
+
+  def Run(self):
+    _RunShell(self.devices, self.args.package_name, self.args.cmd)
+
+
+class _CompileDexCommand(_Command):
+  name = 'compile-dex'
+  description = ('Applicable only for Android N+. Forces .odex files to be '
+                 'compiled with the given compilation filter. To see existing '
+                 'filter, use "disk-usage" command.')
+  needs_package_name = True
+  all_devices_by_default = True
+
+  def _RegisterExtraArgs(self, group):
+    group.add_argument(
+        'compilation_filter',
+        choices=['verify', 'quicken', 'space-profile', 'space',
+                 'speed-profile', 'speed'],
+        help='For WebView/Monochrome, use "speed". For other apks, use '
+             '"speed-profile".')
+
+  def Run(self):
+    _RunCompileDex(self.devices, self.args.package_name,
+                   self.args.compilation_filter)
+
+
+_COMMANDS = [
+    _InstallCommand,
+    _UninstallCommand,
+    _LaunchCommand,
+    _StopCommand,
+    _ClearDataCommand,
+    _ArgvCommand,
+    _GdbCommand,
+    _LogcatCommand,
+    _PsCommand,
+    _DiskUsageCommand,
+    _MemUsageCommand,
+    _ShellCommand,
+    _CompileDexCommand,
+]
+
+
+def _ParseArgs(parser, from_wrapper_script):
+  subparsers = parser.add_subparsers()
+  commands = [clazz(from_wrapper_script) for clazz in _COMMANDS]
+  for command in commands:
+    if from_wrapper_script or not command.needs_output_directory:
+      command.RegisterArgs(subparsers)
 
   # Show extended help when no command is passed.
   argv = sys.argv[1:]
   if not argv:
     argv = ['--help']
-  args = parser.parse_args(argv)
+
+  return parser.parse_args(argv)
+
+
+def _RunInternal(parser, output_directory=None):
+  colorama.init()
+  parser.set_defaults(output_directory=output_directory)
+  from_wrapper_script = bool(output_directory)
+  args = _ParseArgs(parser, from_wrapper_script)
   run_tests_helper.SetLogLevel(args.verbose_count)
-  command = args.command
+  args.command.ProcessArgs(args)
+  args.command.Run()
+  _SaveDeviceCaches(args.command.devices, output_directory)
 
+
+# TODO(agrieve): Remove =None from target_cpu on or after October 2017.
+#     It exists only so that stale wrapper scripts continue to work.
+def Run(output_directory, apk_path, incremental_json, command_line_flags_file,
+        target_cpu=None):
+  """Entry point for generated wrapper scripts."""
+  constants.SetOutputDirectory(output_directory)
+  devil_chromium.Initialize(output_directory=output_directory)
+  parser = argparse.ArgumentParser()
+  parser.set_defaults(
+      command_line_flags_file=command_line_flags_file,
+      target_cpu=target_cpu,
+      apk_path=apk_path if os.path.exists(apk_path) else None,
+      incremental_json=(
+          incremental_json if os.path.exists(incremental_json) else None))
+  _RunInternal(parser, output_directory=output_directory)
+
+
+def main():
   devil_chromium.Initialize()
+  _RunInternal(argparse.ArgumentParser(), output_directory=None)
 
-  devices = device_utils.DeviceUtils.HealthyDevices(
-      device_arg=args.devices,
-      enable_device_files_cache=True,
-      default_retries=0)
-  devices_obj = device_utils.DeviceUtils.parallel(devices)
-  _LoadDeviceCaches(devices)
 
-  try:
-    if len(devices) > 1:
-      if command in ('gdb', 'logcat') or command == 'shell' and not args.cmd:
-        raise device_errors.MultipleDevicesError(devices)
-    default_all = command in ('argv', 'stop', 'clear-data', 'disk-usage',
-                              'mem-usage', 'ps', 'compile-dex')
-    if default_all or args.devices:
-      args.all = True
-    if len(devices) > 1 and not args.all:
-      raise Exception(_GenerateMissingAllFlagMessage(devices, devices_obj))
-  except:
-    _SaveDeviceCaches(devices)
-    raise
-
-  apk_name = os.path.basename(apk_path)
-  apk_path, incremental_install_json_path = _SelectApk(
-      apk_path, incremental_install_json_path, parser, args)
-  install_dict = None
-
-  if incremental_install_json_path:
-    with open(incremental_install_json_path) as f:
-      install_dict = json.load(f)
-    apk = apk_helper.ToHelper(
-        os.path.join(output_directory, install_dict['apk_path']))
-  else:
-    apk = apk_helper.ToHelper(apk_path)
-
-  apk_package = apk.GetPackageName()
-
-  # These commands use os.exec(), so we won't get a chance to update the cache
-  # afterwards.
-  if command in ('gdb', 'logcat', 'shell'):
-    _SaveDeviceCaches(devices)
-
-  if command == 'install':
-    _InstallApk(apk, install_dict, devices_obj)
-  elif command == 'uninstall':
-    _UninstallApk(install_dict, devices_obj, apk_package)
-  elif command == 'launch':
-    _LaunchUrl(devices_obj, args.args, command_line_flags_file,
-               args.url, apk)
-  elif command == 'run':
-    logging.warning('Installing...')
-    _InstallApk(apk, install_dict, devices_obj)
-    logging.warning('Sending launch intent...')
-    _LaunchUrl(devices_obj, args.args, command_line_flags_file,
-               args.url, apk)
-  elif command == 'stop':
-    devices_obj.ForceStop(apk_package)
-  elif command == 'clear-data':
-    devices_obj.ClearApplicationState(apk_package)
-  elif command == 'argv':
-    _ChangeFlags(devices, devices_obj, args.args,
-                 command_line_flags_file)
-  elif command == 'gdb':
-    extra_args = shlex.split(args.args or '')
-    _RunGdb(apk_name, apk_package, devices[0], target_cpu, extra_args,
-            args.verbose_count)
-  elif command == 'logcat':
-    adb_path = adb_wrapper.AdbWrapper.GetAdbPath()
-    cmd = [adb_path, '-s', devices[0].serial, 'logcat']
-    os.execv(adb_path, cmd)
-  elif command == 'mem-usage':
-    _RunMemUsage(devices, devices_obj, apk_package)
-  elif command == 'disk-usage':
-    _RunDiskUsage(devices, devices_obj, apk_package, args.verbose_count)
-  elif command == 'ps':
-    _RunPs(devices, devices_obj, apk_package)
-  elif command == 'shell':
-    _RunShell(devices, devices_obj, apk_package, args.cmd)
-  elif command == 'compile-dex':
-    _RunCompileDex(devices, devices_obj, apk_package, args.compilation_filter)
-
-  # Save back to the cache.
-  _SaveDeviceCaches(devices)
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 231370e8f..4da50fdc7 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -609,7 +609,6 @@
         test_package = self._test_instance.test_package
         extras = {}
         extras['log'] = 'true'
-        extras['package'] = '.'.join(test_package.split('.')[:2])
         extras[_EXTRA_TEST_LIST] = dev_test_list_json.name
         target = '%s/%s' % (test_package, junit4_runner_class)
         test_list_run_output = dev.StartInstrumentation(
diff --git a/build/fuchsia/runner_common.py b/build/fuchsia/runner_common.py
index c6463623..af145cc 100755
--- a/build/fuchsia/runner_common.py
+++ b/build/fuchsia/runner_common.py
@@ -9,14 +9,12 @@
 to run, or starts the bootserver to allow running on a hardware device."""
 
 import argparse
-import multiprocessing
 import os
 import re
 import shutil
 import signal
 import subprocess
 import sys
-import tempfile
 
 
 DIR_SOURCE_ROOT = os.path.abspath(
@@ -123,7 +121,7 @@
 def _StripBinary(dry_run, bin_path):
   """Creates a stripped copy of the executable at |bin_path| and returns the
   path to the stripped copy."""
-  strip_path = tempfile.mktemp()
+  strip_path = bin_path + '.bootfs_stripped'
   _RunAndCheck(dry_run, ['/usr/bin/strip', bin_path, '-o', strip_path])
   if not dry_run and not os.path.exists(strip_path):
     raise Exception('strip did not create output file')
@@ -131,15 +129,18 @@
 
 
 def _StripBinaries(dry_run, file_mapping):
-  """Strips all executables in |file_mapping|, and returns a new mapping
-  dictionary, suitable to pass to _WriteManifest()"""
-  new_mapping = file_mapping.copy()
+  """Updates the supplied manifest |file_mapping|, by stripping any executables
+  and updating their entries to point to the stripped location. Returns a
+  mapping from target executables to their un-stripped paths, for use during
+  symbolization."""
+  symbols_mapping = {}
   for target, source in file_mapping.iteritems():
     with open(source, 'rb') as f:
       file_tag = f.read(4)
     if file_tag == '\x7fELF':
-      new_mapping[target] = _StripBinary(dry_run, source)
-  return new_mapping
+      symbols_mapping[target] = source
+      file_mapping[target] = _StripBinary(dry_run, source)
+  return symbols_mapping
 
 
 def _WriteManifest(manifest_file, file_mapping):
@@ -165,7 +166,7 @@
   file_mapping = dict(runtime_deps)
 
   # Generate a script that runs the binaries and shuts down QEMU (if used).
-  autorun_file = tempfile.NamedTemporaryFile()
+  autorun_file = open(bin_name + '.bootfs_autorun', 'w')
   autorun_file.write('#!/bin/sh\n')
   if _IsRunningOnBot():
     # We drop to -smp 1 to avoid counterintuitive observations on the realtime
@@ -211,11 +212,11 @@
       lambda x: _MakeTargetImageName(DIR_SOURCE_ROOT, output_directory, x))
 
   # Strip any binaries in the file list, and generate a manifest mapping.
-  manifest_mapping = _StripBinaries(dry_run, file_mapping)
+  symbols_mapping = _StripBinaries(dry_run, file_mapping)
 
   # Write the target, source mappings to a file suitable for bootfs.
-  manifest_file = tempfile.NamedTemporaryFile()
-  _WriteManifest(manifest_file.file, manifest_mapping)
+  manifest_file = open(bin_name + '.bootfs_manifest', 'w')
+  _WriteManifest(manifest_file, file_mapping)
   manifest_file.flush()
   _DumpFile(dry_run, manifest_file.name, 'manifest')
 
@@ -230,38 +231,45 @@
     return None
 
   # Return both the name of the bootfs file, and the filename mapping.
-  return (bootfs_name, file_mapping)
+  return (bootfs_name, symbols_mapping)
 
 
-def _SymbolizeEntry(entry):
+def _SymbolizeEntries(entries):
   filename_re = re.compile(r'at ([-._a-zA-Z0-9/+]+):(\d+)')
-  raw, frame_id = entry['raw'], entry['frame_id']
-  prefix = '#%s: ' % frame_id
 
-  if entry.has_key('debug_binary') and entry.has_key('pc_offset'):
-    # Invoke addr2line on the host-side binary to resolve the symbol.
+  # Use addr2line to symbolize all the |pc_offset|s in |entries| in one go.
+  # Entries with no |debug_binary| are also processed here, so that we get
+  # consistent output in that case, with the cannot-symbolize case.
+  addr2line_output = None
+  if entries[0].has_key('debug_binary'):
     addr2line_output = subprocess.check_output(
-        ['addr2line', '-Cipf', '--exe=' + entry['debug_binary'],
-         entry['pc_offset']])
+        ['addr2line', '-Cipf', '-p', '--exe=' + entries[0]['debug_binary']] +
+        map(lambda entry: entry['pc_offset'], entries)).splitlines()
+    assert addr2line_output
 
-    # addr2line outputs a second line for inlining information, offset
-    # that to align it properly after the frame index.
-    addr2line_filtered = addr2line_output.strip().replace(
-        '(inlined', ' ' * len(prefix) + '(inlined')
+  # Collate a set of |(frame_id, result)| pairs from the output lines.
+  results = {}
+  for i in xrange(len(entries)):
+    entry = entries[i]
+    raw, frame_id = entry['raw'], entry['frame_id']
+    prefix = '#%s: ' % frame_id
 
-    # Relativize path to DIR_SOURCE_ROOT if we see a filename.
-    def RelativizePath(m):
-      relpath = os.path.relpath(os.path.normpath(m.group(1)), DIR_SOURCE_ROOT)
-      return 'at ' + relpath + ':' + m.group(2)
-    addr2line_filtered = filename_re.sub(RelativizePath, addr2line_filtered)
+    if addr2line_output:
+      # Relativize path to DIR_SOURCE_ROOT if we see a filename.
+      def RelativizePath(m):
+        relpath = os.path.relpath(os.path.normpath(m.group(1)), DIR_SOURCE_ROOT)
+        return 'at ' + relpath + ':' + m.group(2)
+      filtered_line = filename_re.sub(RelativizePath, addr2line_output[i])
 
-    # If symbolization fails just output the raw backtrace.
-    if '??' in addr2line_filtered:
-      addr2line_filtered = raw
-  else:
-    addr2line_filtered = raw
+      # If symbolization fails just output the raw backtrace.
+      if '??' in filtered_line:
+        filtered_line = raw
+    else:
+      filtered_line = raw
 
-  return '%s%s' % (prefix, addr2line_filtered)
+    results[entry['frame_id']] = prefix + filtered_line
+
+  return results
 
 
 def _FindDebugBinary(entry, file_mapping):
@@ -297,34 +305,24 @@
 
   return None
 
-def _ParallelSymbolizeBacktrace(backtrace, file_mapping):
-  # Disable handling of SIGINT during sub-process creation, to prevent
-  # sub-processes from consuming Ctrl-C signals, rather than the parent
-  # process doing so.
-  saved_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
-  p = multiprocessing.Pool(multiprocessing.cpu_count())
 
-  # Restore the signal handler for the parent process.
-  signal.signal(signal.SIGINT, saved_sigint_handler)
-
-  # Resolve the |binary| name in each entry to a host-accessible filename.
+def _SymbolizeBacktrace(backtrace, file_mapping):
+  # Group |backtrace| entries according to the associated binary, and locate
+  # the path to the debug symbols for that binary, if any.
+  batches = {}
   for entry in backtrace:
     debug_binary = _FindDebugBinary(entry, file_mapping)
     if debug_binary:
       entry['debug_binary'] = debug_binary
+    batches.setdefault(debug_binary, []).append(entry)
 
-  symbolized = []
-  try:
-    result = p.map_async(_SymbolizeEntry, backtrace)
-    symbolized = result.get(SYMBOLIZATION_TIMEOUT_SECS)
-    if not symbolized:
-      return []
-  except multiprocessing.TimeoutError:
-    return ['(timeout error occurred during symbolization)']
-  except KeyboardInterrupt:  # SIGINT
-    p.terminate()
+  # Run _SymbolizeEntries on each batch and collate the results.
+  symbolized = {}
+  for batch in batches.itervalues():
+    symbolized.update(_SymbolizeEntries(batch))
 
-  return symbolized
+  # Map each backtrace to its symbolized form, by frame-id, and return the list.
+  return map(lambda entry: symbolized[entry['frame_id']], backtrace)
 
 
 def RunFuchsia(bootfs_and_manifest, use_device, dry_run):
@@ -424,8 +422,8 @@
     frame_id = matched.group('frame_id')
     if backtrace_line == 'end':
       if backtrace_entries:
-        for processed in _ParallelSymbolizeBacktrace(backtrace_entries,
-                                                     bootfs_manifest):
+        for processed in _SymbolizeBacktrace(backtrace_entries,
+                                             bootfs_manifest):
           print processed
       backtrace_entries = []
       continue
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py
index 93a04ceb..738343b 100755
--- a/build/vs_toolchain.py
+++ b/build/vs_toolchain.py
@@ -22,8 +22,8 @@
 json_data_file = os.path.join(script_dir, 'win_toolchain.json')
 
 
-# Use MSVS2015 as the default toolchain.
-CURRENT_DEFAULT_TOOLCHAIN_VERSION = '2015'
+# Use MSVS2017 as the default toolchain.
+CURRENT_DEFAULT_TOOLCHAIN_VERSION = '2017'
 
 
 def SetEnvironmentAndGetRuntimeDllDirs():
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 482fc22..7834546 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -362,7 +362,9 @@
   void set_property_tree_sequence_number(int sequence_number) {
     property_tree_sequence_number_ = sequence_number;
   }
-  int property_tree_sequence_number() { return property_tree_sequence_number_; }
+  int property_tree_sequence_number() const {
+    return property_tree_sequence_number_;
+  }
 
   void SetTransformTreeIndex(int index);
   int transform_tree_index() const;
diff --git a/cc/raster/raster_source.cc b/cc/raster/raster_source.cc
index d28efab..aedd659 100644
--- a/cc/raster/raster_source.cc
+++ b/cc/raster/raster_source.cc
@@ -56,7 +56,8 @@
   raster_canvas->clipRect(SkRect::MakeFromIRect(raster_bounds));
   raster_canvas->translate(raster_transform.translation().x(),
                            raster_transform.translation().y());
-  raster_canvas->scale(raster_transform.scale(), raster_transform.scale());
+  raster_canvas->scale(raster_transform.scale() / recording_scale_factor_,
+                       raster_transform.scale() / recording_scale_factor_);
   PlaybackToCanvas(raster_canvas, target_color_space, settings);
   raster_canvas->restore();
 }
diff --git a/cc/raster/raster_source_unittest.cc b/cc/raster/raster_source_unittest.cc
index 0f33b51..1ee83af 100644
--- a/cc/raster/raster_source_unittest.cc
+++ b/cc/raster/raster_source_unittest.cc
@@ -11,6 +11,7 @@
 #include "cc/raster/playback_image_provider.h"
 #include "cc/test/fake_recording_source.h"
 #include "cc/test/skia_common.h"
+#include "cc/test/test_skcanvas.h"
 #include "cc/tiles/software_image_decode_cache.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkPixelRef.h"
@@ -20,6 +21,10 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size_conversions.h"
 
+using ::testing::_;
+using ::testing::StrictMock;
+using ::testing::Sequence;
+
 namespace cc {
 namespace {
 
@@ -602,5 +607,34 @@
     EXPECT_EQ(SK_ColorRED, bitmap.getColor(24, y));
 }
 
+TEST(RasterSourceTest, RasterTransformWithoutRecordingScale) {
+  gfx::Size size(100, 100);
+  float recording_scale = 2.f;
+  std::unique_ptr<FakeRecordingSource> recording_source =
+      FakeRecordingSource::CreateFilledRecordingSource(size);
+  recording_source->Rerecord();
+  recording_source->SetRecordingScaleFactor(recording_scale);
+  scoped_refptr<RasterSource> raster_source =
+      recording_source->CreateRasterSource();
+
+  StrictMock<MockCanvas> mock_canvas;
+  Sequence s;
+  RasterSource::PlaybackSettings settings;
+  settings.playback_to_shared_canvas = true;
+
+  SkMatrix m;
+  m.setScale(1.f / recording_scale, 1.f / recording_scale);
+
+  EXPECT_CALL(mock_canvas, willSave()).InSequence(s);
+  // The call to raster_canvas->scale() should have values with the recording
+  // scale removed.
+  EXPECT_CALL(mock_canvas, didConcat(m)).InSequence(s);
+  EXPECT_CALL(mock_canvas, willRestore()).InSequence(s);
+
+  raster_source->PlaybackToCanvas(&mock_canvas, ColorSpaceForTesting(),
+                                  gfx::Rect(size), gfx::Rect(size),
+                                  gfx::AxisTransform2d(), settings);
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 8d9799b..e094495 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -245,12 +245,25 @@
   return layer->test_properties()->sorting_context_id != 0;
 }
 
+static inline bool HasLatestSequenceNumber(const Layer* layer, int number) {
+  return layer->property_tree_sequence_number() == number;
+}
+
+static inline bool HasLatestSequenceNumber(const LayerImpl*, int) {
+  return true;
+}
+
 template <typename LayerType>
 void AddClipNodeIfNeeded(const DataForRecursion<LayerType>& data_from_ancestor,
                          LayerType* layer,
                          bool created_transform_node,
                          DataForRecursion<LayerType>* data_for_children) {
   const bool inherits_clip = !ClipParent(layer);
+  // Sanity check the clip parent already built clip node before us.
+  DCHECK(inherits_clip ||
+         HasLatestSequenceNumber(
+             ClipParent(layer),
+             data_from_ancestor.property_trees->sequence_number));
   const int parent_id = inherits_clip ? data_from_ancestor.clip_tree_parent
                                       : ClipParent(layer)->clip_tree_index();
 
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 3f99a82..b9bc301 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -756,6 +756,11 @@
             </intent-filter>
         </receiver>
 
+        <!-- Download foreground service -->
+        <service android:name="org.chromium.chrome.browser.download.DownloadForegroundService"
+            android:exported="false">
+        </service>
+
         <!-- Download broadcast manager service -->
         <service android:name="org.chromium.chrome.browser.download.DownloadBroadcastManager"
             android:exported="false">
diff --git a/chrome/android/java/res/drawable-hdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-hdpi/contextual_search_bar_shadow.png
deleted file mode 100644
index 136cb8d..0000000
--- a/chrome/android/java/res/drawable-hdpi/contextual_search_bar_shadow.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/modern_toolbar_shadow.png b/chrome/android/java/res/drawable-hdpi/modern_toolbar_shadow.png
new file mode 100644
index 0000000..baf7d1c4b
--- /dev/null
+++ b/chrome/android/java/res/drawable-hdpi/modern_toolbar_shadow.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-mdpi/contextual_search_bar_shadow.png
deleted file mode 100644
index 09e2bdc7..0000000
--- a/chrome/android/java/res/drawable-mdpi/contextual_search_bar_shadow.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/modern_toolbar_shadow.png b/chrome/android/java/res/drawable-mdpi/modern_toolbar_shadow.png
new file mode 100644
index 0000000..25ac6592
--- /dev/null
+++ b/chrome/android/java/res/drawable-mdpi/modern_toolbar_shadow.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-xhdpi/contextual_search_bar_shadow.png
deleted file mode 100644
index 19f6936..0000000
--- a/chrome/android/java/res/drawable-xhdpi/contextual_search_bar_shadow.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/modern_toolbar_shadow.png b/chrome/android/java/res/drawable-xhdpi/modern_toolbar_shadow.png
new file mode 100644
index 0000000..da84aae0
--- /dev/null
+++ b/chrome/android/java/res/drawable-xhdpi/modern_toolbar_shadow.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-xxhdpi/contextual_search_bar_shadow.png
deleted file mode 100644
index 65d952d..0000000
--- a/chrome/android/java/res/drawable-xxhdpi/contextual_search_bar_shadow.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/modern_toolbar_shadow.png b/chrome/android/java/res/drawable-xxhdpi/modern_toolbar_shadow.png
new file mode 100644
index 0000000..2206ddd8
--- /dev/null
+++ b/chrome/android/java/res/drawable-xxhdpi/modern_toolbar_shadow.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-xxxhdpi/contextual_search_bar_shadow.png
deleted file mode 100644
index 135c09db..0000000
--- a/chrome/android/java/res/drawable-xxxhdpi/contextual_search_bar_shadow.png
+++ /dev/null
Binary files differ
diff --git a/chrome/android/java/res/layout/bottom_control_container.xml b/chrome/android/java/res/layout/bottom_control_container.xml
index 278b660..3c692e7f 100644
--- a/chrome/android/java/res/layout/bottom_control_container.xml
+++ b/chrome/android/java/res/layout/bottom_control_container.xml
@@ -33,7 +33,7 @@
                 android:id="@+id/bottom_toolbar_shadow"
                 android:layout_width="match_parent"
                 android:layout_height="@dimen/toolbar_shadow_height"
-                android:src="@drawable/toolbar_shadow"
+                android:src="@drawable/modern_toolbar_shadow"
                 android:scaleType="fitXY"
                 android:scaleY="-1"
                 android:contentDescription="@null" />
diff --git a/chrome/android/java/res/layout/download_manager_ui_space_widget.xml b/chrome/android/java/res/layout/download_manager_ui_space_widget.xml
index 22a11a3..c1fa0467 100644
--- a/chrome/android/java/res/layout/download_manager_ui_space_widget.xml
+++ b/chrome/android/java/res/layout/download_manager_ui_space_widget.xml
@@ -25,36 +25,25 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:singleLine="true"
-            android:textAppearance="@style/RobotoMediumStyle"
-            android:textColor="@color/google_blue_500"
-            android:textSize="16sp" />
+            android:textAppearance="@style/BlueLink2" />
 
         <!-- The progress bar uses 20dp of space, vertically, including spacing. -->
         <org.chromium.chrome.browser.widget.MaterialProgressBar
             android:id="@+id/space_bar"
             android:layout_width="match_parent"
-            android:layout_height="4dp"
+            android:layout_height="2dp"
             android:layout_marginTop="8dp"
             android:layout_marginBottom="8dp"
-            chrome:colorBackground="@color/black_alpha_38"
-            chrome:colorProgress="@color/google_blue_500"
-            chrome:colorSecondaryProgress="@color/black_alpha_87" />
+            chrome:colorBackground="@color/google_grey_500"
+            chrome:colorProgress="@color/google_grey_300"
+            chrome:colorSecondaryProgress="@color/google_blue_700" />
 
         <TextView
-            android:id="@+id/size_other_apps"
+            android:id="@+id/size_free_and_other_apps"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:singleLine="true"
-            android:textColor="@color/black_alpha_87"
-            android:textSize="14sp" />
-
-        <TextView
-            android:id="@+id/size_free"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:textColor="@color/black_alpha_54"
-            android:textSize="14sp" />
+            android:textAppearance="@style/BlackDisabledText2" />
 
     </LinearLayout>
 
diff --git a/chrome/android/java/res/layout/history_privacy_disclaimer_header.xml b/chrome/android/java/res/layout/history_privacy_disclaimer_header.xml
index f19f8d1..99d5d1c2 100644
--- a/chrome/android/java/res/layout/history_privacy_disclaimer_header.xml
+++ b/chrome/android/java/res/layout/history_privacy_disclaimer_header.xml
@@ -10,16 +10,14 @@
     android:paddingBottom="16dp" >
 
     <org.chromium.ui.widget.TextViewWithClickableSpans
-        android:id="@+id/signed_in_not_synced"
-        style="@style/PrivacyDisclaimerText" />
-
-    <org.chromium.ui.widget.TextViewWithClickableSpans
-        android:id="@+id/signed_in_synced"
-        style="@style/PrivacyDisclaimerText" />
-
-    <org.chromium.ui.widget.TextViewWithClickableSpans
-        android:id="@+id/other_forms_of_browsing_history"
-        style="@style/PrivacyDisclaimerText" />
+        android:id="@+id/privacy_disclaimer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="6dp"
+        android:layout_marginStart="@dimen/list_item_default_margin"
+        android:layout_marginEnd="@dimen/list_item_default_margin"
+        android:textAppearance="@style/BlackBody"
+        android:lineSpacingExtra="6sp"/>
 
     <LinearLayout
         android:id="@+id/privacy_disclaimer_bottom_space"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 54efa22..c6fe783 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -633,18 +633,6 @@
         <item name="android:scaleType">center</item>
     </style>
 
-    <!-- History UI -->
-    <style name="PrivacyDisclaimerText">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_marginTop">6dp</item>
-        <item name="android:layout_marginStart">@dimen/list_item_default_margin</item>
-        <item name="android:layout_marginEnd">@dimen/list_item_default_margin</item>
-        <item name="android:lineSpacingExtra">6sp</item>
-        <item name="android:textSize">14sp</item>
-        <item name="android:visibility">gone</item>
-    </style>
-
     <!-- Download Home -->
     <style name="DownloadHomeStatusText">
         <item name="android:layout_width">wrap_content</item>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 960694fe..a82fcd16 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -240,7 +240,7 @@
     <dimen name="toolbar_progress_bar_height">2dp</dimen>
     <dimen name="toolbar_button_width">48dp</dimen>
     <dimen name="toolbar_handle_height">3dp</dimen>
-    <dimen name="toolbar_handle_width">24dp</dimen>
+    <dimen name="toolbar_handle_width">20dp</dimen>
     <dimen name="toolbar_handle_corner_radius">1.5dp</dimen>
     <dimen name="toolbar_handle_margin_top">12dp</dimen>
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index 4554684d..8402736 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -88,10 +88,13 @@
         mSceneLayer = createNewContextualSearchSceneLayer();
         mPanelMetrics = new ContextualSearchPanelMetrics();
 
-        mBarShadowHeightPx = ApiCompatibilityUtils.getDrawable(mContext.getResources(),
-                R.drawable.contextual_search_bar_shadow).getIntrinsicHeight();
-        mEndButtonWidthDp = mPxToDp * (float) mContext.getResources().getDimensionPixelSize(
-                R.dimen.contextual_search_end_button_width);
+        mBarShadowHeightPx =
+                ApiCompatibilityUtils
+                        .getDrawable(mContext.getResources(), R.drawable.modern_toolbar_shadow)
+                        .getIntrinsicHeight();
+        mEndButtonWidthDp = mPxToDp
+                * mContext.getResources().getDimensionPixelSize(
+                          R.dimen.contextual_search_end_button_width);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
index cb26d3b..7cdc1d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
@@ -132,7 +132,7 @@
 
         nativeUpdateContextualSearchLayer(mNativePtr, R.drawable.contextual_search_bar_background,
                 searchContextViewId, searchTermViewId, searchCaptionViewId,
-                R.drawable.contextual_search_bar_shadow, R.drawable.googleg, quickActionIconResId,
+                R.drawable.modern_toolbar_shadow, R.drawable.googleg, quickActionIconResId,
                 R.drawable.breadcrumb_arrow, ContextualSearchPanel.CLOSE_ICON_DRAWABLE_ID,
                 R.drawable.progress_bar_background, R.drawable.progress_bar_foreground,
                 searchPromoViewId, R.drawable.contextual_search_promo_ripple,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
index 21698f9..da6389e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
@@ -34,18 +34,14 @@
      * @param notification The new notification to be pinned to.
      */
     public void startOrUpdateForegroundService(int notificationId, Notification notification) {
-        // TODO(jming): Make sure there is not weird UI in switching the pinned notification.
         startForeground(notificationId, notification);
     }
 
     /**
      * Stop the foreground service that is running.
-     * @param isComplete If the download has been complete and, therefore, if its notification
-     *                   should be killed.
      */
-    public void stopDownloadForegroundService(boolean isComplete) {
-        // TODO(jming): Check to make sure the notification does not get killed.
-        stopForeground(isComplete /* killNotification */);
+    public void stopDownloadForegroundService(boolean isCancelled) {
+        stopForeground(isCancelled /* kill notification if cancelled */);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
index 68ca516..083ee31c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
@@ -101,7 +101,7 @@
         // Stop the foreground service.
         // In the pending case, this will stop the foreground immediately after it was started.
         if (!isActive(downloadUpdate.mDownloadStatus)) {
-            stopAndUnbindService(downloadUpdate.mDownloadStatus == DownloadStatus.COMPLETE);
+            stopAndUnbindService(downloadUpdate.mDownloadStatus == DownloadStatus.CANCEL);
             cleanDownloadUpdateQueue();
             return;
         }
@@ -159,9 +159,8 @@
     @VisibleForTesting
     void startAndBindServiceInternal(Context context) {
         DownloadForegroundService.startDownloadForegroundService(context);
-        ContextUtils.getApplicationContext().bindService(
-                new Intent(ContextUtils.getApplicationContext(), DownloadForegroundService.class),
-                mConnection, Context.BIND_AUTO_CREATE);
+        context.bindService(new Intent(context, DownloadForegroundService.class), mConnection,
+                Context.BIND_AUTO_CREATE);
     }
 
     private final ServiceConnection mConnection = new ServiceConnection() {
@@ -197,17 +196,17 @@
     /** Helper code to stop and unbind service. */
 
     @VisibleForTesting
-    void stopAndUnbindService(boolean isComplete) {
+    void stopAndUnbindService(boolean isCancelled) {
         mIsServiceBound = false;
         if (mBoundService != null) {
-            stopAndUnbindServiceInternal(isComplete);
+            stopAndUnbindServiceInternal(isCancelled);
             mBoundService = null;
         }
     }
 
     @VisibleForTesting
-    void stopAndUnbindServiceInternal(boolean isComplete) {
-        mBoundService.stopDownloadForegroundService(isComplete);
+    void stopAndUnbindServiceInternal(boolean isCancelled) {
+        mBoundService.stopDownloadForegroundService(isCancelled);
         ContextUtils.getApplicationContext().unbindService(mConnection);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
index 652abcd1..7301014 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
@@ -17,10 +17,8 @@
 import static org.chromium.chrome.browser.download.DownloadNotificationService.EXTRA_IS_OFF_THE_RECORD;
 import static org.chromium.chrome.browser.download.DownloadNotificationService.EXTRA_IS_SUPPORTED_MIME_TYPE;
 import static org.chromium.chrome.browser.download.DownloadNotificationService.EXTRA_NOTIFICATION_BUNDLE_ICON_ID;
-import static org.chromium.chrome.browser.download.DownloadNotificationService.NOTIFICATION_NAMESPACE;
 
 import android.app.Notification;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -56,27 +54,6 @@
     }
 
     /**
-     * Call from the DownloadNotificationStore when downloads are updated (added, changed, deleted)
-     * to trigger the creation, display, or removal of a corresponding notification.
-     * NOTE: This is currently not being used because DownloadNotificationStore does not yet exist.
-     * @param context of the application.
-     * @param downloadStatus (in progress, paused, successful, failed, deleted, or summary).
-     * @param downloadUpdate information about the download (ie. contentId, fileName, icon, etc).
-     */
-    public static void updateDownloadStatus(
-            Context context, DownloadStatus downloadStatus, DownloadUpdate downloadUpdate) {
-        NotificationManager notificationManager =
-                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-        if (downloadStatus == DownloadStatus.DELETED) {
-            notificationManager.cancel(NOTIFICATION_NAMESPACE, downloadUpdate.getNotificationId());
-        } else {
-            Notification notification = buildNotification(context, downloadStatus, downloadUpdate);
-            notificationManager.notify(
-                    NOTIFICATION_NAMESPACE, downloadUpdate.getNotificationId(), notification);
-        }
-    }
-
-    /**
      * Builds a downloads notification based on the status of the download and its information.
      * @param context of the download.
      * @param downloadStatus (in progress, paused, successful, failed, deleted, or summary).
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 4e9b213..f33a964 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -56,8 +56,6 @@
     public static final String ACTION_DOWNLOAD_OPEN =
             "org.chromium.chrome.browser.download.DOWNLOAD_OPEN";
 
-    public static final String NOTIFICATION_NAMESPACE = "DownloadNotificationService";
-
     public static final String EXTRA_NOTIFICATION_BUNDLE_ICON_ID =
             "Chrome.NotificationBundleIconIdExtra";
     /** Notification Id starting value, to avoid conflicts from IDs used in prior versions. */
@@ -200,9 +198,8 @@
             long timeRemainingInMillis, long startTime, boolean isOffTheRecord,
             boolean canDownloadWhileMetered, boolean isDownloadPending, boolean isTransient,
             Bitmap icon, boolean hasUserGesture) {
-        int notificationId = getNotificationId(id);
         Context context = ContextUtils.getApplicationContext();
-
+        int notificationId = getNotificationId(id);
         DownloadUpdate downloadUpdate = new DownloadUpdate.Builder()
                                                 .setContentId(id)
                                                 .setFileName(fileName)
@@ -236,7 +233,8 @@
     }
 
     public void cancelNotification(int notificationId) {
-        mNotificationManager.cancel(NOTIFICATION_NAMESPACE, notificationId);
+        // TODO(b/65052774): Add back NOTIFICATION_NAMESPACE when able to.
+        mNotificationManager.cancel(notificationId);
     }
 
     /**
@@ -306,9 +304,9 @@
             stopTrackingInProgressDownload(id);
             return;
         }
-        int notificationId = entry == null ? getNotificationId(id) : entry.notificationId;
-        Context context = ContextUtils.getApplicationContext();
 
+        Context context = ContextUtils.getApplicationContext();
+        int notificationId = entry == null ? getNotificationId(id) : entry.notificationId;
         DownloadUpdate downloadUpdate = new DownloadUpdate.Builder()
                                                 .setContentId(id)
                                                 .setFileName(fileName)
@@ -317,10 +315,8 @@
                                                 .setIcon(icon)
                                                 .setNotificationId(notificationId)
                                                 .build();
-
         Notification notification = DownloadNotificationFactory.buildNotification(
                 context, DownloadNotificationFactory.DownloadStatus.PAUSED, downloadUpdate);
-
         updateNotification(notificationId, notification, id,
                 new DownloadSharedPreferenceEntry(id, notificationId, isOffTheRecord,
                         canDownloadWhileMetered, fileName, isAutoResumable, isTransient));
@@ -406,9 +402,8 @@
             fileName = entry.fileName;
         }
 
-        int notificationId = getNotificationId(id);
         Context context = ContextUtils.getApplicationContext();
-
+        int notificationId = getNotificationId(id);
         DownloadUpdate downloadUpdate = new DownloadUpdate.Builder()
                                                 .setContentId(id)
                                                 .setFileName(fileName)
@@ -452,7 +447,8 @@
 
     @VisibleForTesting
     void updateNotification(int id, Notification notification) {
-        mNotificationManager.notify(NOTIFICATION_NAMESPACE, id, notification);
+        // TODO(b/65052774): Add back NOTIFICATION_NAMESPACE when able to.
+        mNotificationManager.notify(id, notification);
     }
 
     private void updateNotification(int notificationId, Notification notification, ContentId id,
@@ -491,11 +487,10 @@
      * already in progress, do nothing.
      */
     void resumeAllPendingDownloads() {
-        Context context = ContextUtils.getApplicationContext();
         List<DownloadSharedPreferenceEntry> entries = mDownloadSharedPreferenceHelper.getEntries();
         for (int i = 0; i < entries.size(); ++i) {
             DownloadSharedPreferenceEntry entry = entries.get(i);
-            if (!canResumeDownload(context, entry)) continue;
+            if (!canResumeDownload(ContextUtils.getApplicationContext(), entry)) continue;
             if (mDownloadsInProgress.contains(entry.id)) continue;
 
             Intent intent = new Intent();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java
index 92557ca..f8f43692 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java
@@ -9,6 +9,9 @@
 import android.os.Environment;
 import android.os.StatFs;
 import android.support.v7.widget.RecyclerView;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -109,8 +112,7 @@
     private View mView;
     private View mViewContainer;
     private TextView mSpaceUsedByDownloadsTextView;
-    private TextView mSpaceUsedByOtherAppsTextView;
-    private TextView mSpaceFreeTextView;
+    private TextView mSpaceFreeAndOtherAppsTextView;
     private MaterialProgressBar mSpaceBar;
     private long mFreeBytes;
 
@@ -120,8 +122,8 @@
                                  .inflate(R.layout.download_manager_ui_space_widget, parent, false);
         mView = mViewContainer.findViewById(R.id.space_widget_content);
         mSpaceUsedByDownloadsTextView = (TextView) mView.findViewById(R.id.size_downloaded);
-        mSpaceUsedByOtherAppsTextView = (TextView) mView.findViewById(R.id.size_other_apps);
-        mSpaceFreeTextView = (TextView) mView.findViewById(R.id.size_free);
+        mSpaceFreeAndOtherAppsTextView =
+                (TextView) mView.findViewById(R.id.size_free_and_other_apps);
         mSpaceBar = (MaterialProgressBar) mView.findViewById(R.id.space_bar);
         mFileSystemBytesTask =
                 new StorageSizeTask(true).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@@ -190,22 +192,28 @@
         Context context = mSpaceUsedByDownloadsTextView.getContext();
         mSpaceUsedByDownloadsTextView.setText(
                 DownloadUtils.getStringForBytes(context, USED_STRINGS, bytesUsedByDownloads));
-        mSpaceUsedByOtherAppsTextView.setText(
-                DownloadUtils.getStringForBytes(context, OTHER_STRINGS, bytesUsedByOtherApps));
-        mSpaceFreeTextView.setText(
-                DownloadUtils.getStringForBytes(context, FREE_STRINGS, mFreeBytes));
+
+        String spaceFree = DownloadUtils.getStringForBytes(context, FREE_STRINGS, mFreeBytes);
+        String spaceUsedByOtherApps =
+                DownloadUtils.getStringForBytes(context, OTHER_STRINGS, bytesUsedByOtherApps);
+        SpannableString spannable = new SpannableString(
+                context.getResources().getString(R.string.download_manager_ui_space_free_and_other,
+                        spaceFree, spaceUsedByOtherApps));
+        ForegroundColorSpan colorSpan = new ForegroundColorSpan(
+                ApiCompatibilityUtils.getColor(context.getResources(), R.color.black_alpha_54));
+        spannable.setSpan(colorSpan, 0, spaceFree.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        mSpaceFreeAndOtherAppsTextView.setText(spannable);
 
         // Set a minimum size for the download size so that it shows up in the progress bar.
         long onePercentOfSystem = fileSystemBytes == 0 ? 0 : fileSystemBytes / 100;
         long fudgedBytesUsedByDownloads = Math.max(bytesUsedByDownloads, onePercentOfSystem);
-        long fudgedbytesUsedByOtherApps = Math.max(0, bytesUsedTotal - fudgedBytesUsedByDownloads);
 
         // Indicate how much space has been used as a progress bar.  The percentage used by
         // downloads is shown by the non-overlapped area of the primary and secondary progressbar.
         int percentageUsedTotal = computePercentage(bytesUsedTotal, fileSystemBytes);
-        int percentageOtherApps = computePercentage(fudgedbytesUsedByOtherApps, fileSystemBytes);
+        int percentageDownloaded = computePercentage(fudgedBytesUsedByDownloads, fileSystemBytes);
         mSpaceBar.setProgress(percentageUsedTotal);
-        mSpaceBar.setSecondaryProgress(percentageOtherApps);
+        mSpaceBar.setSecondaryProgress(percentageDownloaded);
 
         for (Observer observer : mObservers) observer.onSpaceDisplayUpdated(this);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
index 2122fb1..6ee49bf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
@@ -8,6 +8,7 @@
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
 import android.text.method.LinkMovementMethod;
 import android.view.LayoutInflater;
@@ -18,6 +19,7 @@
 import android.widget.TextView;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.ContextUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -51,10 +53,11 @@
     private final HistoryManager mHistoryManager;
     private final ArrayList<HistoryItemView> mItemViews;
     private RecyclerView mRecyclerView;
+    private final SpannableString mSignedInNotSyncedText;
+    private final SpannableString mSignedInSyncedText;
+    private final SpannableString mOtherFormsOfBrowsingHistoryText;
 
-    private TextView mSignedInNotSyncedTextView;
-    private TextView mSignedInSyncedTextView;
-    private TextView mOtherFormsOfBrowsingHistoryTextView;
+    private TextView mPrivacyDisclaimerTextView;
     private View mPrivacyDisclaimerBottomSpace;
     private Button mClearBrowsingDataButton;
     private HeaderItem mPrivacyDisclaimerHeaderItem;
@@ -72,7 +75,6 @@
     private boolean mClearBrowsingDataButtonVisible;
     private long mNextQueryEndTime;
     private String mQueryText = EMPTY_QUERY;
-    private int mDefaultTextMargin;
 
     public HistoryAdapter(SelectionDelegate<HistoryItem> delegate, HistoryManager manager,
             HistoryProvider provider) {
@@ -82,6 +84,20 @@
         mHistoryProvider.setObserver(this);
         mHistoryManager = manager;
         mItemViews = new ArrayList<>();
+
+        mSignedInNotSyncedText = initializePrivacyDisclaimerText(
+                R.string.android_history_no_synced_results, LEARN_MORE_LINK);
+        mSignedInSyncedText = initializePrivacyDisclaimerText(
+                R.string.android_history_has_synced_results, LEARN_MORE_LINK);
+        // For some test, the native library is not loaded, so ChromeFeatureList#isInitialized is
+        // checked to prevent crashing.
+        boolean flagEnabled = ChromeFeatureList.isInitialized()
+                && ChromeFeatureList.isEnabled(ChromeFeatureList.TABS_IN_CBD);
+        int disclaimerTextId = flagEnabled ? R.string.android_history_other_forms_of_history_new
+                                           : R.string.android_history_other_forms_of_history;
+        String disclaimerUrl = flagEnabled ? MY_ACTIVITY_LINK : GOOGLE_HISTORY_LINK;
+        mOtherFormsOfBrowsingHistoryText =
+                initializePrivacyDisclaimerText(disclaimerTextId, disclaimerUrl);
     }
 
     /**
@@ -191,7 +207,7 @@
         }
         initialize();
         updateClearBrowsingDataButtonVisibility();
-        setPrivacyDisclaimerVisibility();
+        setPrivacyDisclaimer();
     }
 
     /**
@@ -273,7 +289,7 @@
     public void hasOtherFormsOfBrowsingData(boolean hasOtherForms, boolean hasSyncedResults) {
         mHasOtherFormsOfBrowsingData = hasOtherForms;
         mHasSyncedData = hasSyncedResults;
-        setPrivacyDisclaimerVisibility();
+        setPrivacyDisclaimer();
     }
 
     @Override
@@ -288,30 +304,15 @@
      * items for them.
      */
     void generateHeaderItems() {
-        ViewGroup privacyDisclaimers =
+        ViewGroup privacyDisclaimerContainer =
                 (ViewGroup) View.inflate(mHistoryManager.getSelectableListLayout().getContext(),
                         R.layout.history_privacy_disclaimer_header, null);
 
-        mSignedInNotSyncedTextView =
-                (TextView) privacyDisclaimers.findViewById(R.id.signed_in_not_synced);
-        setPrivacyDisclaimerText(mSignedInNotSyncedTextView,
-                R.string.android_history_no_synced_results, LEARN_MORE_LINK);
-
-        mSignedInSyncedTextView = (TextView) privacyDisclaimers.findViewById(R.id.signed_in_synced);
-        setPrivacyDisclaimerText(mSignedInSyncedTextView,
-                R.string.android_history_has_synced_results, LEARN_MORE_LINK);
-
-        mOtherFormsOfBrowsingHistoryTextView =
-                (TextView) privacyDisclaimers.findViewById(R.id.other_forms_of_browsing_history);
-        boolean flagEnabled = ChromeFeatureList.isEnabled(ChromeFeatureList.TABS_IN_CBD);
-        int disclaimerTextId = flagEnabled ? R.string.android_history_other_forms_of_history_new
-                                           : R.string.android_history_other_forms_of_history;
-        String disclaimerUrl = flagEnabled ? MY_ACTIVITY_LINK : GOOGLE_HISTORY_LINK;
-        setPrivacyDisclaimerText(
-                mOtherFormsOfBrowsingHistoryTextView, disclaimerTextId, disclaimerUrl);
-
+        mPrivacyDisclaimerTextView =
+                privacyDisclaimerContainer.findViewById(R.id.privacy_disclaimer);
+        mPrivacyDisclaimerTextView.setMovementMethod(LinkMovementMethod.getInstance());
         mPrivacyDisclaimerBottomSpace =
-                privacyDisclaimers.findViewById(R.id.privacy_disclaimer_bottom_space);
+                privacyDisclaimerContainer.findViewById(R.id.privacy_disclaimer_bottom_space);
 
         ViewGroup clearBrowsingDataButtonContainer =
                 (ViewGroup) View.inflate(mHistoryManager.getSelectableListLayout().getContext(),
@@ -326,10 +327,10 @@
             }
         });
 
-        mPrivacyDisclaimerHeaderItem = new HeaderItem(0, privacyDisclaimers);
+        mPrivacyDisclaimerHeaderItem = new HeaderItem(0, privacyDisclaimerContainer);
         mClearBrowsingDataButtonHeaderItem = new HeaderItem(1, clearBrowsingDataButtonContainer);
         updateClearBrowsingDataButtonVisibility();
-        setPrivacyDisclaimerVisibility();
+        setPrivacyDisclaimer();
     }
 
     /**
@@ -342,7 +343,14 @@
         setHeaders(args.toArray(new HeaderItem[args.size()]));
     }
 
-    private void setPrivacyDisclaimerText(final TextView view, int stringId, final String url) {
+    /**
+     * Create a {@SpannableString} for privacy disclaimer.
+     * @param stringId The string resource id.
+     * @param url The url to open when clicked.
+     * @return The {@SpannableString} with the specified string resource and url.
+     */
+    private SpannableString initializePrivacyDisclaimerText(int stringId, final String url) {
+        final Resources resources = ContextUtils.getApplicationContext().getResources();
         NoUnderlineClickableSpan link = new NoUnderlineClickableSpan() {
             @Override
             public void onClick(View view) {
@@ -352,15 +360,12 @@
             @Override
             public void updateDrawState(TextPaint textPaint) {
                 super.updateDrawState(textPaint);
-                textPaint.setColor(ApiCompatibilityUtils.getColor(
-                        view.getResources(), R.color.google_blue_700));
+                textPaint.setColor(
+                        ApiCompatibilityUtils.getColor(resources, R.color.google_blue_700));
             }
         };
-        SpannableString spannable = SpanApplier.applySpans(
-                view.getResources().getString(stringId),
-                new SpanApplier.SpanInfo("<link>", "</link>", link));
-        view.setText(spannable);
-        view.setMovementMethod(LinkMovementMethod.getInstance());
+        return SpanApplier.applySpans(
+                resources.getString(stringId), new SpanApplier.SpanInfo("<link>", "</link>", link));
     }
 
     /**
@@ -373,17 +378,24 @@
     }
 
     /**
-     * Set visibility for privacy disclaimer layout and views.
+     * Set text of privacy disclaimer and visibility of its container.
      */
-    void setPrivacyDisclaimerVisibility() {
+    void setPrivacyDisclaimer() {
         boolean isSignedIn = ChromeSigninController.get().isSignedIn();
         boolean shouldShowPrivacyDisclaimers =
                 hasPrivacyDisclaimers() && mHistoryManager.shouldShowInfoHeaderIfAvailable();
-        mSignedInNotSyncedTextView.setVisibility(
-                !mHasSyncedData && isSignedIn ? View.VISIBLE : View.GONE);
-        mSignedInSyncedTextView.setVisibility(mHasSyncedData ? View.VISIBLE : View.GONE);
-        mOtherFormsOfBrowsingHistoryTextView.setVisibility(
-                mHasOtherFormsOfBrowsingData ? View.VISIBLE : View.GONE);
+
+        SpannableStringBuilder builder = new SpannableStringBuilder();
+        if (!mHasSyncedData && isSignedIn) builder.append(mSignedInNotSyncedText);
+        if (mHasSyncedData) builder.append(mSignedInSyncedText);
+        if (mHasOtherFormsOfBrowsingData) {
+            // If mHasOtherFormsOfBrowsingData is true, one of the other conditions must have
+            // already been met so a space is needed to separate the sentences.
+            builder.append(" ");
+            builder.append(mOtherFormsOfBrowsingHistoryText);
+        }
+        mPrivacyDisclaimerTextView.setText(builder);
+
         // Prevent from refreshing the recycler view if header visibility is not changed.
         if (mPrivacyDisclaimersVisible == shouldShowPrivacyDisclaimers) return;
         mPrivacyDisclaimersVisible = shouldShowPrivacyDisclaimers;
@@ -401,26 +413,24 @@
         if (mIsInitialized) setHeaders();
     }
 
-    private int getDefaultTextMargin(Resources resources) {
-        if (mDefaultTextMargin == 0) {
-            mDefaultTextMargin = resources.getDimensionPixelSize(R.dimen.list_item_default_margin);
-        }
-        return mDefaultTextMargin;
+    @VisibleForTesting
+    String getSignedInNotSyncedTextForTests() {
+        return mSignedInNotSyncedText.toString();
     }
 
     @VisibleForTesting
-    TextView getSignedInNotSyncedViewForTests() {
-        return mSignedInNotSyncedTextView;
+    String getSignedInSyncedTextForTests() {
+        return mSignedInSyncedText.toString();
     }
 
     @VisibleForTesting
-    TextView getSignedInSyncedViewForTests() {
-        return mSignedInSyncedTextView;
+    String getOtherFormsOfBrowsingHistoryTextForTests() {
+        return mOtherFormsOfBrowsingHistoryText.toString();
     }
 
     @VisibleForTesting
-    TextView getOtherFormsOfBrowsingHistoryViewForTests() {
-        return mOtherFormsOfBrowsingHistoryTextView;
+    String getPrivacyDisclaimerTextForTests() {
+        return mPrivacyDisclaimerTextView.getText().toString();
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
index 30a8fb4b..dd76f09 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -223,7 +223,7 @@
                     .putBoolean(PREF_SHOW_HISTORY_INFO, mShouldShowInfoHeader)
                     .apply();
             mToolbar.updateInfoMenuItem(shouldShowInfoButton(), shouldShowInfoHeaderIfAvailable());
-            mHistoryAdapter.setPrivacyDisclaimerVisibility();
+            mHistoryAdapter.setPrivacyDisclaimer();
         }
         return false;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
index 583eb72..742efa0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -13,6 +13,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.offlinepages.DeletePageResult;
 import org.chromium.content_public.browser.WebContents;
 
@@ -147,6 +148,15 @@
     }
 
     /**
+     * @Return the string representing the origin of the tab.
+     */
+    @CalledByNative
+    private static String getEncodedOriginApp(Tab tab) {
+        return new OfflinePageOrigin(ContextUtils.getApplicationContext(), tab)
+                .encodeAsJsonString();
+    }
+
+    /**
      * Adds an observer to offline page model changes.
      * @param observer The observer to be added.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
index 7f7cb95..2a6b690 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
@@ -49,6 +49,7 @@
     private static final String FIELD_TRIAL_NAME = "UpdateMenuItem";
     private static final String ENABLED_VALUE = "true";
     private static final String CUSTOM_SUMMARY = "custom_summary";
+    private static final String MIN_REQUIRED_STORAGE_MB = "min_required_storage_for_update_mb";
 
     // UMA constants for logging whether the menu item was clicked.
     private static final int ITEM_NOT_CLICKED = 0;
@@ -117,8 +118,7 @@
                     mUpdateUrl = MarketURLGetter.getMarketUrl(activity);
                     mLatestVersion =
                             VersionNumberGetter.getInstance().getLatestKnownVersion(activity);
-                    mUpdateAvailable = true;
-                    recordInternalStorageSize();
+                    mUpdateAvailable = checkForSufficientStorage();
                 } else {
                     mUpdateAvailable = false;
                 }
@@ -389,7 +389,28 @@
         return value;
     }
 
-    private void recordInternalStorageSize() {
+    /**
+     * Returns an integer value for a Finch parameter, or the default value if no parameter exists
+     * in the current configuration.  Also checks for a command-line switch with the same name.
+     * @param paramName The name of the Finch parameter (or command-line switch) to get a value for.
+     * @param defaultValue The default value to return when there's no param or switch.
+     * @return An integer value -- either the param or the default.
+     */
+    private static int getIntParamValueOrDefault(String paramName, int defaultValue) {
+        String value = CommandLine.getInstance().getSwitchValue(paramName);
+        if (TextUtils.isEmpty(value)) {
+            value = VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_NAME, paramName);
+        }
+        if (TextUtils.isEmpty(value)) return defaultValue;
+
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
+    private boolean checkForSufficientStorage() {
         assert !ThreadUtils.runningOnUiThread();
 
         File path = Environment.getDataDirectory();
@@ -402,6 +423,13 @@
         }
         RecordHistogram.recordLinearCountHistogram(
                 "GoogleUpdate.InfoBar.InternalStorageSizeAvailable", (int) size, 1, 200, 100);
+        RecordHistogram.recordLinearCountHistogram(
+                "GoogleUpdate.InfoBar.DeviceFreeSpace", (int) size, 1, 1000, 50);
+
+        int minRequiredStorage = getIntParamValueOrDefault(MIN_REQUIRED_STORAGE_MB, -1);
+        if (minRequiredStorage == -1) return true;
+
+        return size >= minRequiredStorage;
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
index bb307732..07d2dee9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
@@ -660,6 +660,8 @@
         updateToolbarBackground(mVisualState);
         updateVisualsForToolbarState();
 
+        mBottomToolbarTopShadow.setImageResource(R.drawable.toolbar_shadow);
+
         invalidate();
         requestLayout();
     }
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 8f5c5a1..bf5597d 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2096,6 +2096,9 @@
       <message name="IDS_OPEN_DOWNLOADED_LABEL" desc="A text label on the snackbar widget to open the downloaded file.">
         Open
       </message>
+      <message name="IDS_DOWNLOAD_MANAGER_UI_SPACE_FREE_AND_OTHER" desc="Formatted string indicating how much storage is available to use on this device and how much storage space on this device has been used by other apps.">
+        <ph name="SPACE_FREE">%1$s<ex>4.8 GB available</ex></ph> (<ph name="SPACE_OTHER">%2$s<ex>5.22 GB other apps</ex></ph>)
+      </message>
       <message name="IDS_DOWNLOAD_MANAGER_UI_SPACE_FREE_KB" desc="String indicating how much storage is available to use on the device, in kilobytes.">
         <ph name="kilobytes">%1$3.2f<ex>12.5</ex></ph> KB available
       </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
index dd411fc8..647781a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
@@ -65,14 +65,14 @@
         void startAndBindServiceInternal(Context context) {}
 
         @Override
-        void stopAndUnbindService(boolean isComplete) {
-            mIsNotificationKilled = isComplete;
+        void stopAndUnbindService(boolean isCancelled) {
+            mIsNotificationKilled = isCancelled;
             mIsServiceBound = false;
-            super.stopAndUnbindService(isComplete);
+            super.stopAndUnbindService(isCancelled);
         }
 
         @Override
-        void stopAndUnbindServiceInternal(boolean isComplete) {}
+        void stopAndUnbindServiceInternal(boolean isCancelled) {}
 
         @Override
         void startOrUpdateForegroundService(int notificationId, Notification notification) {
@@ -210,14 +210,14 @@
         assertFalse(mDownloadServiceManager.mIsServiceBound);
         assertFalse(mDownloadServiceManager.mIsNotificationKilled);
 
-        // Service restarts and then completes, so notification is killed.
+        // Service restarts and then is cancelled, so notification is killed.
         mDownloadServiceManager.updateDownloadStatus(
                 mContext, DownloadStatus.IN_PROGRESS, FAKE_DOWNLOAD_1, mNotification);
         assertTrue(mDownloadServiceManager.mIsServiceBound);
         mDownloadServiceManager.onServiceConnected();
 
         mDownloadServiceManager.updateDownloadStatus(
-                mContext, DownloadStatus.COMPLETE, FAKE_DOWNLOAD_1, mNotification);
+                mContext, DownloadStatus.CANCEL, FAKE_DOWNLOAD_1, mNotification);
         assertFalse(mDownloadServiceManager.mIsServiceBound);
         assertTrue(mDownloadServiceManager.mIsNotificationKilled);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
index 34af9361..73cc61e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
@@ -29,6 +29,7 @@
 import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -40,6 +41,7 @@
 import org.chromium.chrome.browser.widget.selection.SelectableItemViewHolder;
 import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
 import org.chromium.chrome.test.util.ChromeRestriction;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.signin.SigninTestUtil;
 import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
 import org.chromium.components.signin.ChromeSigninController;
@@ -225,11 +227,7 @@
     public void testPrivacyDisclaimers_SignedOut() {
         ChromeSigninController signinController = ChromeSigninController.get();
         signinController.setSignedInAccountName(null);
-
-        assertEquals(View.GONE, mAdapter.getSignedInNotSyncedViewForTests().getVisibility());
-        assertEquals(View.GONE, mAdapter.getSignedInSyncedViewForTests().getVisibility());
-        assertEquals(View.GONE,
-                mAdapter.getOtherFormsOfBrowsingHistoryViewForTests().getVisibility());
+        assertTrue(mAdapter.getPrivacyDisclaimerTextForTests().isEmpty());
     }
 
     @SmallTest
@@ -239,10 +237,8 @@
 
         setHasOtherFormsOfBrowsingData(false, false);
 
-        assertEquals(View.VISIBLE, mAdapter.getSignedInNotSyncedViewForTests().getVisibility());
-        assertEquals(View.GONE, mAdapter.getSignedInSyncedViewForTests().getVisibility());
-        assertEquals(View.GONE,
-                mAdapter.getOtherFormsOfBrowsingHistoryViewForTests().getVisibility());
+        assertEquals(mAdapter.getSignedInNotSyncedTextForTests(),
+                mAdapter.getPrivacyDisclaimerTextForTests());
 
         signinController.setSignedInAccountName(null);
     }
@@ -254,25 +250,23 @@
 
         setHasOtherFormsOfBrowsingData(false, true);
 
-        assertEquals(View.GONE, mAdapter.getSignedInNotSyncedViewForTests().getVisibility());
-        assertEquals(View.VISIBLE, mAdapter.getSignedInSyncedViewForTests().getVisibility());
-        assertEquals(View.GONE,
-                mAdapter.getOtherFormsOfBrowsingHistoryViewForTests().getVisibility());
+        assertEquals(mAdapter.getSignedInSyncedTextForTests(),
+                mAdapter.getPrivacyDisclaimerTextForTests());
 
         signinController.setSignedInAccountName(null);
     }
 
     @SmallTest
+    @Features(@Features.Register(ChromeFeatureList.TABS_IN_CBD))
     public void testPrivacyDisclaimers_SignedInSyncedAndOtherForms() {
         ChromeSigninController signinController = ChromeSigninController.get();
         signinController.setSignedInAccountName("test@gmail.com");
 
         setHasOtherFormsOfBrowsingData(true, true);
 
-        assertEquals(View.GONE, mAdapter.getSignedInNotSyncedViewForTests().getVisibility());
-        assertEquals(View.VISIBLE, mAdapter.getSignedInSyncedViewForTests().getVisibility());
-        assertEquals(View.VISIBLE,
-                mAdapter.getOtherFormsOfBrowsingHistoryViewForTests().getVisibility());
+        String expected = String.format("%1$s %2$s", mAdapter.getSignedInSyncedTextForTests(),
+                mAdapter.getOtherFormsOfBrowsingHistoryTextForTests());
+        assertEquals(expected, mAdapter.getPrivacyDisclaimerTextForTests());
 
         signinController.setSignedInAccountName(null);
     }
@@ -568,7 +562,7 @@
             @Override
             public void run() {
                 mAdapter.setClearBrowsingDataButtonVisibilityForTest(false);
-                mAdapter.setPrivacyDisclaimerVisibility();
+                mAdapter.setPrivacyDisclaimer();
             }
         });
 
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc
index d656a8ad..b2329c1 100644
--- a/chrome/app/chrome_crash_reporter_client_win.cc
+++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -169,7 +169,6 @@
 
       // TODO(asvitkine): Remove after fixing https://crbug.com/736675
       {"bad_histogram", kMediumSize},
-      {"from_location", kMediumSize},
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 4f27643..cfdff8e6 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10422,6 +10422,9 @@
           <message name="IDS_USER_MANAGER_ADD_PROFILE_PROFILES_LOCKED_ERROR" desc="Error message displayed when trying to add a profile while all profiles are locked.">
             Please unlock your profile before adding a person.
           </message>
+          <message name="IDS_USER_MANAGER_PROMPT_MESSAGE" desc="Prompt message displayed on UserManager when force-sign-in policy is enabled.">
+            Please unlock your profile with your corporate account.
+          </message>
           <message name="IDS_LOGIN_POD_USER_REMOVE_WARNING_NONSYNC" desc="Main text shown as a warning when attempting to remove an user.">
             This will permanently delete your browsing data from this device.
           </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8f0c0e3c..34885f2 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2219,6 +2219,7 @@
       "offline_pages/offline_page_mhtml_archiver.cc",
       "offline_pages/offline_page_mhtml_archiver.h",
       "offline_pages/offline_page_model_factory.h",
+      "offline_pages/offline_page_origin_utils.h",
       "offline_pages/offline_page_request_interceptor.cc",
       "offline_pages/offline_page_request_interceptor.h",
       "offline_pages/offline_page_request_job.cc",
@@ -2274,6 +2275,7 @@
         "offline_pages/android/offline_page_bridge.cc",
         "offline_pages/android/offline_page_bridge.h",
         "offline_pages/android/offline_page_model_factory.cc",
+        "offline_pages/android/offline_page_origin_utils_android.cc",
         "offline_pages/android/offline_page_utils_android.cc",
         "offline_pages/android/prefetch_background_task_android.cc",
         "offline_pages/android/prefetch_background_task_android.h",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index b94dc53e..7ebfd648 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1684,6 +1684,7 @@
     "arc/arc_session_manager_unittest.cc",
     "arc/arc_support_host_unittest.cc",
     "arc/arc_util_unittest.cc",
+    "arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc",
     "arc/downloads_watcher/arc_downloads_watcher_service_unittest.cc",
     "arc/extensions/arc_support_message_host_unittest.cc",
     "arc/fileapi/arc_content_file_system_async_file_util_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.h b/chrome/browser/chromeos/arc/arc_session_manager.h
index da3ff410..a1afa7d2 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.h
+++ b/chrome/browser/chromeos/arc/arc_session_manager.h
@@ -254,6 +254,9 @@
   // require ToS acceptance. Returns false in other cases, including one when
   // ARC is not currently running.
   bool is_directly_started() const { return directly_started_; }
+  void set_directly_started_for_testing(bool directly_started) {
+    directly_started_ = directly_started;
+  }
 
   // Injectors for testing.
   void SetArcSessionRunnerForTesting(
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
index e26a8b42..1a173d4 100644
--- a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
+++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
@@ -81,15 +81,19 @@
     : arc_bridge_service_(bridge_service),
       account_id_(multi_user_util::GetAccountIdFromProfile(
           Profile::FromBrowserContext(context))),
-      binding_(this) {
+      binding_(this),
+      first_app_launch_delay_recorder_(
+          base::BindRepeating(&RecordAppLaunchDelay)) {
   arc_bridge_service_->boot_phase_monitor()->AddObserver(this);
-  ArcSessionManager::Get()->AddObserver(this);
+  auto* arc_session_manager = ArcSessionManager::Get();
+  DCHECK(arc_session_manager);
+  arc_session_manager->AddObserver(this);
 }
 
 ArcBootPhaseMonitorBridge::~ArcBootPhaseMonitorBridge() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   arc_bridge_service_->boot_phase_monitor()->RemoveObserver(this);
-  ArcSessionManager* arc_session_manager = ArcSessionManager::Get();
+  auto* arc_session_manager = ArcSessionManager::Get();
   DCHECK(arc_session_manager);
   arc_session_manager->RemoveObserver(this);
 }
@@ -102,7 +106,7 @@
 
   if (boot_completed_) {
     VLOG(2) << "ARC has already fully started. Recording the UMA now.";
-    RecordAppLaunchDelay(base::TimeDelta());
+    first_app_launch_delay_recorder_.Run(base::TimeDelta());
     return;
   }
   app_launch_time_ = base::TimeTicks::Now();
@@ -137,8 +141,10 @@
     VLOG(2) << "ArcInstanceThrottle created in OnBootCompleted()";
   }
 
-  if (!app_launch_time_.is_null())
-    RecordAppLaunchDelay(base::TimeTicks::Now() - app_launch_time_);
+  if (!app_launch_time_.is_null()) {
+    first_app_launch_delay_recorder_.Run(base::TimeTicks::Now() -
+                                         app_launch_time_);
+  }
 }
 
 void ArcBootPhaseMonitorBridge::OnArcInitialStart() {
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
index ee84e39..1bc4bb1 100644
--- a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
+++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
@@ -34,6 +35,9 @@
       public mojom::BootPhaseMonitorHost,
       public ArcSessionManager::Observer {
  public:
+  using FirstAppLaunchDelayRecorder =
+      base::RepeatingCallback<void(base::TimeDelta)>;
+
   // Returns singleton instance for the given BrowserContext,
   // or nullptr if the browser |context| is not allowed to use ARC.
   static ArcBootPhaseMonitorBridge* GetForBrowserContext(
@@ -65,6 +69,16 @@
   void OnArcSessionStopped(ArcStopReason stop_reason) override;
   void OnArcSessionRestarting() override;
 
+  void RecordFirstAppLaunchDelayUMAForTesting() {
+    RecordFirstAppLaunchDelayUMAInternal();
+  }
+
+  ArcInstanceThrottle* throttle_for_testing() const { return throttle_.get(); }
+  void set_first_app_launch_delay_recorder_for_testing(
+      const FirstAppLaunchDelayRecorder& first_app_launch_delay_recorder) {
+    first_app_launch_delay_recorder_ = first_app_launch_delay_recorder;
+  }
+
  private:
   void RecordFirstAppLaunchDelayUMAInternal();
   void Reset();
@@ -74,6 +88,7 @@
   ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
   const AccountId account_id_;
   mojo::Binding<mojom::BootPhaseMonitorHost> binding_;
+  FirstAppLaunchDelayRecorder first_app_launch_delay_recorder_;
 
   // The following variables must be reset every time when the instance stops or
   // restarts.
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc
new file mode 100644
index 0000000..5c2616b
--- /dev/null
+++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc
@@ -0,0 +1,203 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h"
+
+#include "base/threading/platform_thread.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_session_manager_client.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/test/fake_arc_session.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace arc {
+
+namespace {
+
+class ArcBootPhaseMonitorBridgeTest : public testing::Test {
+ public:
+  ArcBootPhaseMonitorBridgeTest() = default;
+  ~ArcBootPhaseMonitorBridgeTest() override = default;
+
+  void SetUp() override {
+    chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
+        base::MakeUnique<chromeos::FakeSessionManagerClient>());
+    chromeos::DBusThreadManager::Initialize();
+
+    record_uma_counter_ = 0;
+    testing_profile_ = base::MakeUnique<TestingProfile>();
+    arc_session_manager_ = base::MakeUnique<ArcSessionManager>(
+        base::MakeUnique<ArcSessionRunner>(base::Bind(FakeArcSession::Create)));
+    bridge_service_ = base::MakeUnique<ArcBridgeService>();
+
+    boot_phase_monitor_bridge_ = base::MakeUnique<ArcBootPhaseMonitorBridge>(
+        testing_profile_.get(), bridge_service_.get());
+    boot_phase_monitor_bridge_->set_first_app_launch_delay_recorder_for_testing(
+        base::BindRepeating(&ArcBootPhaseMonitorBridgeTest::RecordUMA,
+                            base::Unretained(this)));
+  }
+
+  void TearDown() override {
+    boot_phase_monitor_bridge_->Shutdown();
+    boot_phase_monitor_bridge_.reset();
+
+    bridge_service_.reset();
+    arc_session_manager_.reset();
+    testing_profile_.reset();
+  }
+
+ protected:
+  ArcSessionManager* arc_session_manager() const {
+    return arc_session_manager_.get();
+  }
+  ArcBootPhaseMonitorBridge* boot_phase_monitor_bridge() const {
+    return boot_phase_monitor_bridge_.get();
+  }
+  size_t record_uma_counter() const { return record_uma_counter_; }
+  base::TimeDelta last_time_delta() const { return last_time_delta_; }
+
+ private:
+  void RecordUMA(base::TimeDelta delta) {
+    last_time_delta_ = delta;
+    ++record_uma_counter_;
+  }
+
+  content::TestBrowserThreadBundle thread_bundle_;
+  std::unique_ptr<TestingProfile> testing_profile_;
+  std::unique_ptr<ArcSessionManager> arc_session_manager_;
+  std::unique_ptr<ArcBridgeService> bridge_service_;
+  std::unique_ptr<ArcBootPhaseMonitorBridge> boot_phase_monitor_bridge_;
+
+  size_t record_uma_counter_;
+  base::TimeDelta last_time_delta_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcBootPhaseMonitorBridgeTest);
+};
+
+// Tests that ArcBootPhaseMonitorBridge can be constructed and destructed.
+TEST_F(ArcBootPhaseMonitorBridgeTest, TestConstructDestruct) {}
+
+// Tests that the throttle is created on BootCompleted.
+TEST_F(ArcBootPhaseMonitorBridgeTest, TestThrottleCreation) {
+  // Tell |arc_session_manager_| that this is not opt-in boot.
+  arc_session_manager()->set_directly_started_for_testing(true);
+
+  // Initially, |throttle_| is null.
+  EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+  // OnBootCompleted() creates it.
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+  // ..but it's removed when the session stops.
+  boot_phase_monitor_bridge()->OnArcSessionStopped(ArcStopReason::SHUTDOWN);
+  EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+}
+
+// Tests the same but with OnSessionRestarting().
+TEST_F(ArcBootPhaseMonitorBridgeTest, TestThrottleCreation_Restart) {
+  // Tell |arc_session_manager_| that this is not opt-in boot.
+  arc_session_manager()->set_directly_started_for_testing(true);
+
+  EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+  // Call OnArcSessionRestarting() instead, and confirm that |throttle_| is
+  // gone.
+  boot_phase_monitor_bridge()->OnArcSessionRestarting();
+  EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+  // Also make sure that |throttle| is created again once restarting id done.
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+}
+
+// Tests that the throttle is created on ArcInitialStart when opting in.
+TEST_F(ArcBootPhaseMonitorBridgeTest, TestThrottleCreation_OptIn) {
+  // Tell |arc_session_manager_| that this *is* opt-in boot.
+  arc_session_manager()->set_directly_started_for_testing(false);
+
+  // Initially, |throttle_| is null.
+  EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+  // OnArcInitialStart(), which is called when the user accepts ToS, creates
+  // |throttle_|.
+  boot_phase_monitor_bridge()->OnArcInitialStart();
+  EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+  // ..and OnBootCompleted() does not delete it.
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+  // ..but it's removed when the session stops.
+  boot_phase_monitor_bridge()->OnArcSessionStopped(ArcStopReason::SHUTDOWN);
+  EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing());
+}
+
+// Tests that the UMA recording function is never called unless
+// RecordFirstAppLaunchDelayUMA is called.
+TEST_F(ArcBootPhaseMonitorBridgeTest, TestRecordUMA_None) {
+  EXPECT_EQ(0U, record_uma_counter());
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_EQ(0U, record_uma_counter());
+  boot_phase_monitor_bridge()->OnArcSessionStopped(ArcStopReason::SHUTDOWN);
+  EXPECT_EQ(0U, record_uma_counter());
+}
+
+// Tests that RecordFirstAppLaunchDelayUMA() actually calls the UMA recording
+// function (but only after OnBootCompleted.)
+TEST_F(ArcBootPhaseMonitorBridgeTest, TestRecordUMA_AppLaunchBeforeBoot) {
+  EXPECT_EQ(0U, record_uma_counter());
+  // Calling RecordFirstAppLaunchDelayUMA() before boot shouldn't immediately
+  // record UMA.
+  boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting();
+  EXPECT_EQ(0U, record_uma_counter());
+  // Sleep for 1ms just to make sure 0 won't be passed to RecordUMA().
+  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
+  // UMA recording should be done on BootCompleted.
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_EQ(1U, record_uma_counter());
+  // In this case, |delta| passed to the UMA recording function should be >0.
+  EXPECT_LT(base::TimeDelta(), last_time_delta());
+}
+
+// Tests the same with calling RecordFirstAppLaunchDelayUMA() after boot.
+TEST_F(ArcBootPhaseMonitorBridgeTest, TestRecordUMA_AppLaunchAfterBoot) {
+  EXPECT_EQ(0U, record_uma_counter());
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_EQ(0U, record_uma_counter());
+  // Calling RecordFirstAppLaunchDelayUMA() after boot should immediately record
+  // UMA.
+  boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting();
+  EXPECT_EQ(1U, record_uma_counter());
+  // In this case, |delta| passed to the UMA recording function should be 0.
+  EXPECT_TRUE(last_time_delta().is_zero());
+}
+
+// Tests the same with calling RecordFirstAppLaunchDelayUMA() twice.
+TEST_F(ArcBootPhaseMonitorBridgeTest,
+       TestRecordUMA_AppLaunchesBeforeAndAfterBoot) {
+  EXPECT_EQ(0U, record_uma_counter());
+  boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting();
+  EXPECT_EQ(0U, record_uma_counter());
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_EQ(1U, record_uma_counter());
+  EXPECT_LT(base::TimeDelta(), last_time_delta());
+  // Call the record function again and check that the counter is not changed.
+  boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting();
+  EXPECT_EQ(1U, record_uma_counter());
+}
+
+// Tests the same with calling RecordFirstAppLaunchDelayUMA() twice after boot.
+TEST_F(ArcBootPhaseMonitorBridgeTest, TestRecordUMA_AppLaunchesAfterBoot) {
+  EXPECT_EQ(0U, record_uma_counter());
+  boot_phase_monitor_bridge()->OnBootCompleted();
+  EXPECT_EQ(0U, record_uma_counter());
+  boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting();
+  EXPECT_EQ(1U, record_uma_counter());
+  EXPECT_TRUE(last_time_delta().is_zero());
+  // Call the record function again and check that the counter is not changed.
+  boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting();
+  EXPECT_EQ(1U, record_uma_counter());
+}
+
+}  // namespace
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_instance_throttle.cc b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_instance_throttle.cc
index ecd5272..74797d0 100644
--- a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_instance_throttle.cc
+++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_instance_throttle.cc
@@ -20,13 +20,16 @@
 }  // namespace
 
 ArcInstanceThrottle::ArcInstanceThrottle() {
+  if (!ash::Shell::HasInstance())  // for unit testing.
+    return;
   ash::Shell::Get()->activation_client()->AddObserver(this);
   ThrottleInstance(ash::wm::GetActiveWindow());
 }
 
 ArcInstanceThrottle::~ArcInstanceThrottle() {
-  if (ash::Shell::HasInstance())
-    ash::Shell::Get()->activation_client()->RemoveObserver(this);
+  if (!ash::Shell::HasInstance())
+    return;
+  ash::Shell::Get()->activation_client()->RemoveObserver(this);
 }
 
 void ArcInstanceThrottle::OnWindowActivated(ActivationReason reason,
diff --git a/chrome/browser/chromeos/enrollment_dialog_view.cc b/chrome/browser/chromeos/enrollment_dialog_view.cc
index 75d6357e..0892055 100644
--- a/chrome/browser/chromeos/enrollment_dialog_view.cc
+++ b/chrome/browser/chromeos/enrollment_dialog_view.cc
@@ -94,6 +94,8 @@
       target_uri_(target_uri),
       connect_(connect),
       added_cert_(false) {
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
   chrome::RecordDialogCreation(chrome::DialogIdentifier::ENROLLMENT);
 }
 
@@ -164,7 +166,7 @@
   label->SetMultiLine(true);
   label->SetAllowCharacterBreak(true);
 
-  views::GridLayout* grid_layout = views::GridLayout::CreatePanel(this);
+  views::GridLayout* grid_layout = views::GridLayout::CreateAndInstall(this);
 
   views::ColumnSet* columns = grid_layout->AddColumnSet(0);
   columns->AddColumn(views::GridLayout::FILL,  // Horizontal resize.
diff --git a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
index b8e0852..1f0f17fb 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
@@ -948,4 +948,82 @@
   EXPECT_FALSE(volume.get());
 }
 
+TEST_F(VolumeManagerTest, OnRenameEvent_Started) {
+  LoggingObserver observer;
+  volume_manager()->AddObserver(&observer);
+
+  volume_manager()->OnRenameEvent(
+      chromeos::disks::DiskMountManager::RENAME_STARTED,
+      chromeos::RENAME_ERROR_NONE, "device1");
+
+  ASSERT_EQ(1U, observer.events().size());
+  const LoggingObserver::Event& event = observer.events()[0];
+  EXPECT_EQ(LoggingObserver::Event::RENAME_STARTED, event.type);
+  EXPECT_EQ("device1", event.device_path);
+  EXPECT_TRUE(event.success);
+
+  volume_manager()->RemoveObserver(&observer);
+}
+
+TEST_F(VolumeManagerTest, OnRenameEvent_StartFailed) {
+  LoggingObserver observer;
+  volume_manager()->AddObserver(&observer);
+
+  volume_manager()->OnRenameEvent(
+      chromeos::disks::DiskMountManager::RENAME_STARTED,
+      chromeos::RENAME_ERROR_UNKNOWN, "device1");
+
+  ASSERT_EQ(1U, observer.events().size());
+  const LoggingObserver::Event& event = observer.events()[0];
+  EXPECT_EQ(LoggingObserver::Event::RENAME_STARTED, event.type);
+  EXPECT_EQ("device1", event.device_path);
+  EXPECT_FALSE(event.success);
+
+  volume_manager()->RemoveObserver(&observer);
+}
+
+TEST_F(VolumeManagerTest, OnRenameEvent_Completed) {
+  LoggingObserver observer;
+  volume_manager()->AddObserver(&observer);
+
+  volume_manager()->OnRenameEvent(
+      chromeos::disks::DiskMountManager::RENAME_COMPLETED,
+      chromeos::RENAME_ERROR_NONE, "device1");
+
+  ASSERT_EQ(1U, observer.events().size());
+  const LoggingObserver::Event& event = observer.events()[0];
+  EXPECT_EQ(LoggingObserver::Event::RENAME_COMPLETED, event.type);
+  EXPECT_EQ("device1", event.device_path);
+  EXPECT_TRUE(event.success);
+
+  // When "rename" is successfully done, VolumeManager requests to mount it.
+  ASSERT_EQ(1U, disk_mount_manager_->mount_requests().size());
+  const FakeDiskMountManager::MountRequest& mount_request =
+      disk_mount_manager_->mount_requests()[0];
+  EXPECT_EQ("device1", mount_request.source_path);
+  EXPECT_EQ("", mount_request.source_format);
+  EXPECT_EQ(chromeos::MOUNT_TYPE_DEVICE, mount_request.type);
+
+  volume_manager()->RemoveObserver(&observer);
+}
+
+TEST_F(VolumeManagerTest, OnRenameEvent_CompletedFailed) {
+  LoggingObserver observer;
+  volume_manager()->AddObserver(&observer);
+
+  volume_manager()->OnRenameEvent(
+      chromeos::disks::DiskMountManager::RENAME_COMPLETED,
+      chromeos::RENAME_ERROR_UNKNOWN, "device1");
+
+  ASSERT_EQ(1U, observer.events().size());
+  const LoggingObserver::Event& event = observer.events()[0];
+  EXPECT_EQ(LoggingObserver::Event::RENAME_COMPLETED, event.type);
+  EXPECT_EQ("device1", event.device_path);
+  EXPECT_FALSE(event.success);
+
+  EXPECT_EQ(0U, disk_mount_manager_->mount_requests().size());
+
+  volume_manager()->RemoveObserver(&observer);
+}
+
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc b/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc
index 5d55bc35..439f432 100644
--- a/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc
+++ b/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc
@@ -67,8 +67,7 @@
             views::View* forward,
             views::View* reload,
             views::View* location_bar) {
-    GridLayout* layout = new GridLayout(this);
-    SetLayoutManager(layout);
+    GridLayout* layout = GridLayout::CreateAndInstall(this);
 
     const int related_horizontal_spacing =
         ChromeLayoutProvider::Get()->GetDistanceMetric(
@@ -210,8 +209,7 @@
   toolbar_row->Init(back_, forward_, reload_, location_bar_);
 
   // Layout.
-  GridLayout* layout = new GridLayout(this);
-  SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
 
   views::ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
diff --git a/chrome/browser/chromeos/options/vpn_config_view.cc b/chrome/browser/chromeos/options/vpn_config_view.cc
index d0635f6..ee3ed01 100644
--- a/chrome/browser/chromeos/options/vpn_config_view.cc
+++ b/chrome/browser/chromeos/options/vpn_config_view.cc
@@ -31,6 +31,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/models/combobox_model.h"
 #include "ui/events/event.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/combobox/combobox.h"
 #include "ui/views/controls/label.h"
@@ -236,7 +237,6 @@
       enable_group_name_(false),
       user_passphrase_required_(false),
       title_(0),
-      layout_(NULL),
       server_textfield_(NULL),
       service_text_(NULL),
       service_textfield_(NULL),
@@ -495,6 +495,10 @@
 }
 
 void VPNConfigView::Init() {
+  const views::LayoutProvider* provider = views::LayoutProvider::Get();
+  SetBorder(views::CreateEmptyBorder(
+      provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS)));
+
   const NetworkState* vpn = NULL;
   if (!service_path_.empty()) {
     vpn = NetworkHandler::Get()->network_state_handler()->
@@ -502,13 +506,12 @@
     DCHECK(vpn && vpn->type() == shill::kTypeVPN);
   }
 
-  layout_ = views::GridLayout::CreatePanel(this);
-  views::LayoutProvider* provider = views::LayoutProvider::Get();
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   // Observer any changes to the certificate list.
   CertLibrary::Get()->AddObserver(this);
 
-  views::ColumnSet* column_set = layout_->AddColumnSet(0);
+  views::ColumnSet* column_set = layout->AddColumnSet(0);
   // Label.
   column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1,
                         views::GridLayout::USE_PREF, 0, 0);
@@ -538,15 +541,15 @@
   enable_group_name_ = true;
 
   // Server label and input.
-  layout_->StartRow(0, 0);
+  layout->StartRow(0, 0);
   views::View* server_label =
       new views::Label(l10n_util::GetStringUTF16(
           IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVER_HOSTNAME));
-  layout_->AddView(server_label);
+  layout->AddView(server_label);
   server_textfield_ = new views::Textfield();
   server_textfield_->set_controller(this);
-  layout_->AddView(server_textfield_);
-  layout_->AddPaddingRow(
+  layout->AddView(server_textfield_);
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
   if (!service_path_.empty()) {
     server_label->SetEnabled(false);
@@ -554,26 +557,26 @@
   }
 
   // Service label and name or input.
-  layout_->StartRow(0, 0);
-  layout_->AddView(new views::Label(l10n_util::GetStringUTF16(
+  layout->StartRow(0, 0);
+  layout->AddView(new views::Label(l10n_util::GetStringUTF16(
       IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVICE_NAME)));
   if (service_path_.empty()) {
     service_textfield_ = new views::Textfield();
     service_textfield_->set_controller(this);
-    layout_->AddView(service_textfield_);
+    layout->AddView(service_textfield_);
     service_text_ = NULL;
   } else {
     service_text_ = new views::Label();
     service_text_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    layout_->AddView(service_text_);
+    layout->AddView(service_text_);
     service_textfield_ = NULL;
   }
-  layout_->AddPaddingRow(
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
 
   // Provider type label and select.
-  layout_->StartRow(0, 0);
-  layout_->AddView(new views::Label(l10n_util::GetStringUTF16(
+  layout->StartRow(0, 0);
+  layout->AddView(new views::Label(l10n_util::GetStringUTF16(
       IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PROVIDER_TYPE)));
   if (service_path_.empty()) {
     provider_type_combobox_model_.reset(
@@ -581,131 +584,129 @@
     provider_type_combobox_ = new views::Combobox(
         provider_type_combobox_model_.get());
     provider_type_combobox_->set_listener(this);
-    layout_->AddView(provider_type_combobox_);
+    layout->AddView(provider_type_combobox_);
     provider_type_text_label_ = NULL;
   } else {
     provider_type_text_label_ = new views::Label();
     provider_type_text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    layout_->AddView(provider_type_text_label_);
+    layout->AddView(provider_type_text_label_);
     provider_type_combobox_ = NULL;
   }
-  layout_->AddPaddingRow(
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
 
   // PSK passphrase label, input and visible button.
-  layout_->StartRow(0, 0);
+  layout->StartRow(0, 0);
   psk_passphrase_label_ =  new views::Label(l10n_util::GetStringUTF16(
       IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PSK_PASSPHRASE));
-  layout_->AddView(psk_passphrase_label_);
+  layout->AddView(psk_passphrase_label_);
   psk_passphrase_textfield_ = new PassphraseTextfield();
   psk_passphrase_textfield_->set_controller(this);
-  layout_->AddView(psk_passphrase_textfield_);
-  layout_->AddView(
-      new ControlledSettingIndicatorView(psk_passphrase_ui_data_));
-  layout_->AddPaddingRow(
+  layout->AddView(psk_passphrase_textfield_);
+  layout->AddView(new ControlledSettingIndicatorView(psk_passphrase_ui_data_));
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
 
   // Server CA certificate
   if (service_path_.empty()) {
-    layout_->StartRow(0, 0);
+    layout->StartRow(0, 0);
     server_ca_cert_label_ = new views::Label(l10n_util::GetStringUTF16(
         IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_SERVER_CA));
-    layout_->AddView(server_ca_cert_label_);
+    layout->AddView(server_ca_cert_label_);
     server_ca_cert_combobox_model_.reset(
         new internal::VpnServerCACertComboboxModel());
     server_ca_cert_combobox_ = new views::Combobox(
         server_ca_cert_combobox_model_.get());
-    layout_->AddView(server_ca_cert_combobox_);
-    layout_->AddView(new ControlledSettingIndicatorView(ca_cert_ui_data_));
-    layout_->AddPaddingRow(0, provider->GetDistanceMetric(
-                                  views::DISTANCE_RELATED_CONTROL_VERTICAL));
+    layout->AddView(server_ca_cert_combobox_);
+    layout->AddView(new ControlledSettingIndicatorView(ca_cert_ui_data_));
+    layout->AddPaddingRow(0, provider->GetDistanceMetric(
+                                 views::DISTANCE_RELATED_CONTROL_VERTICAL));
   } else {
     server_ca_cert_label_ = NULL;
     server_ca_cert_combobox_ = NULL;
   }
 
   // User certificate label and input.
-  layout_->StartRow(0, 0);
+  layout->StartRow(0, 0);
   user_cert_label_ = new views::Label(l10n_util::GetStringUTF16(
       IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_CERT));
-  layout_->AddView(user_cert_label_);
+  layout->AddView(user_cert_label_);
   user_cert_combobox_model_.reset(
       new internal::VpnUserCertComboboxModel());
   user_cert_combobox_ = new views::Combobox(user_cert_combobox_model_.get());
   user_cert_combobox_->set_listener(this);
-  layout_->AddView(user_cert_combobox_);
-  layout_->AddView(new ControlledSettingIndicatorView(user_cert_ui_data_));
-  layout_->AddPaddingRow(
+  layout->AddView(user_cert_combobox_);
+  layout->AddView(new ControlledSettingIndicatorView(user_cert_ui_data_));
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
 
   // Username label and input.
-  layout_->StartRow(0, 0);
-  layout_->AddView(new views::Label(l10n_util::GetStringUTF16(
+  layout->StartRow(0, 0);
+  layout->AddView(new views::Label(l10n_util::GetStringUTF16(
       IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USERNAME)));
   username_textfield_ = new views::Textfield();
   username_textfield_->set_controller(this);
   username_textfield_->SetEnabled(username_ui_data_.IsEditable());
-  layout_->AddView(username_textfield_);
-  layout_->AddView(new ControlledSettingIndicatorView(username_ui_data_));
-  layout_->AddPaddingRow(
+  layout->AddView(username_textfield_);
+  layout->AddView(new ControlledSettingIndicatorView(username_ui_data_));
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
 
   // User passphrase label, input and visble button.
-  layout_->StartRow(0, 0);
-  layout_->AddView(new views::Label(l10n_util::GetStringUTF16(
+  layout->StartRow(0, 0);
+  layout->AddView(new views::Label(l10n_util::GetStringUTF16(
       IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_PASSPHRASE)));
   user_passphrase_textfield_ = new PassphraseTextfield();
   user_passphrase_textfield_->set_controller(this);
   user_passphrase_textfield_->SetEnabled(user_passphrase_ui_data_.IsEditable());
-  layout_->AddView(user_passphrase_textfield_);
-  layout_->AddView(
-      new ControlledSettingIndicatorView(user_passphrase_ui_data_));
-  layout_->AddPaddingRow(
+  layout->AddView(user_passphrase_textfield_);
+  layout->AddView(new ControlledSettingIndicatorView(user_passphrase_ui_data_));
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
 
   // OTP label and input.
-  layout_->StartRow(0, 0);
+  layout->StartRow(0, 0);
   otp_label_ = new views::Label(l10n_util::GetStringUTF16(
       IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_OTP));
-  layout_->AddView(otp_label_);
+  layout->AddView(otp_label_);
   otp_textfield_ = new views::Textfield();
   otp_textfield_->set_controller(this);
-  layout_->AddView(otp_textfield_);
-  layout_->AddPaddingRow(
+  layout->AddView(otp_textfield_);
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
 
   // Group Name label and input.
-  layout_->StartRow(0, 0);
+  layout->StartRow(0, 0);
   group_name_label_ = new views::Label(l10n_util::GetStringUTF16(
       IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_GROUP_NAME));
-  layout_->AddView(group_name_label_);
+  layout->AddView(group_name_label_);
   group_name_textfield_ =
       new views::Textfield();
   group_name_textfield_->set_controller(this);
-  layout_->AddView(group_name_textfield_);
-  layout_->AddView(new ControlledSettingIndicatorView(group_name_ui_data_));
-  layout_->AddPaddingRow(
+  layout->AddView(group_name_textfield_);
+  layout->AddView(new ControlledSettingIndicatorView(group_name_ui_data_));
+  layout->AddPaddingRow(
       0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL));
 
   // Save credentials
-  layout_->StartRow(0, 0);
+  layout->StartRow(0, 0);
   save_credentials_checkbox_ = new views::Checkbox(
       l10n_util::GetStringUTF16(
           IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SAVE_CREDENTIALS));
   save_credentials_checkbox_->SetEnabled(
       save_credentials_ui_data_.IsEditable());
-  layout_->SkipColumns(1);
-  layout_->AddView(save_credentials_checkbox_);
-  layout_->AddView(
+  layout->SkipColumns(1);
+  layout->AddView(save_credentials_checkbox_);
+  layout->AddView(
       new ControlledSettingIndicatorView(save_credentials_ui_data_));
 
   // Error label.
-  layout_->StartRow(0, 0);
-  layout_->SkipColumns(1);
+  layout->StartRow(0, 0);
+  layout->SkipColumns(1);
   error_label_ = new views::Label();
   error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   error_label_->SetEnabledColor(SK_ColorRED);
-  layout_->AddView(error_label_);
+  layout->AddView(error_label_);
 
   // Set or hide the UI, update comboboxes and error labels.
   Refresh();
diff --git a/chrome/browser/chromeos/options/vpn_config_view.h b/chrome/browser/chromeos/options/vpn_config_view.h
index b583a731..03d9b4a 100644
--- a/chrome/browser/chromeos/options/vpn_config_view.h
+++ b/chrome/browser/chromeos/options/vpn_config_view.h
@@ -26,7 +26,6 @@
 
 namespace views {
 class Checkbox;
-class GridLayout;
 class Label;
 }
 
@@ -159,7 +158,6 @@
 
   int title_;
 
-  views::GridLayout* layout_;
   views::Textfield* server_textfield_;
   views::Label* service_text_;
   views::Textfield* service_textfield_;
diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc
index 4293aace..08e42ac 100644
--- a/chrome/browser/chromeos/options/wifi_config_view.cc
+++ b/chrome/browser/chromeos/options/wifi_config_view.cc
@@ -36,6 +36,7 @@
 #include "ui/base/material_design/material_design_controller.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/combobox/combobox.h"
@@ -904,6 +905,10 @@
 }
 
 void WifiConfigView::Init(bool show_8021x) {
+  views::LayoutProvider* provider = views::LayoutProvider::Get();
+  SetBorder(views::CreateEmptyBorder(
+      provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS)));
+
   const NetworkState* network = GetNetworkState();
   if (network) {
     if (network->type() == shill::kTypeWifi) {
@@ -938,8 +943,7 @@
       ParseUIProperty(&passphrase_ui_data_, network, ::onc::wifi::kPassphrase);
   }
 
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
-  views::LayoutProvider* provider = views::LayoutProvider::Get();
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   const int column_view_set_id = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
diff --git a/chrome/browser/chromeos/options/wimax_config_view.cc b/chrome/browser/chromeos/options/wimax_config_view.cc
index 10f2128..6a1bbd4 100644
--- a/chrome/browser/chromeos/options/wimax_config_view.cc
+++ b/chrome/browser/chromeos/options/wimax_config_view.cc
@@ -27,6 +27,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/events/event.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/label.h"
@@ -196,6 +197,10 @@
 }
 
 void WimaxConfigView::Init() {
+  const views::LayoutProvider* provider = views::LayoutProvider::Get();
+  SetBorder(views::CreateEmptyBorder(
+      provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS)));
+
   const NetworkState* wimax = NetworkHandler::Get()->network_state_handler()->
       GetNetworkState(service_path_);
   DCHECK(wimax && wimax->type() == shill::kTypeWimax);
@@ -207,8 +212,7 @@
   WifiConfigView::ParseUIProperty(
       &passphrase_ui_data_, wimax, ::onc::wifi::kPassphrase);
 
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
-  views::LayoutProvider* provider = views::LayoutProvider::Get();
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   const int column_view_set_id = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
diff --git a/chrome/browser/chromeos/profiles/multiprofiles_session_aborted_dialog.cc b/chrome/browser/chromeos/profiles/multiprofiles_session_aborted_dialog.cc
index 3ed1efe..8d742ca 100644
--- a/chrome/browser/chromeos/profiles/multiprofiles_session_aborted_dialog.cc
+++ b/chrome/browser/chromeos/profiles/multiprofiles_session_aborted_dialog.cc
@@ -118,8 +118,7 @@
   constexpr int kTopInset = 10;
   constexpr int kOtherInset = 40;
   // Create the views and layout manager and set them up.
-  views::GridLayout* grid_layout = new views::GridLayout(this);
-  SetLayoutManager(grid_layout);
+  views::GridLayout* grid_layout = views::GridLayout::CreateAndInstall(this);
   SetBorder(views::CreateEmptyBorder(kTopInset, kOtherInset, kOtherInset,
                                      kOtherInset));
 
@@ -148,7 +147,6 @@
   grid_layout->StartRow(0, 0);
   grid_layout->AddView(label);
 
-  SetLayoutManager(grid_layout);
   Layout();
 }
 
diff --git a/chrome/browser/chromeos/ui/request_pin_view.cc b/chrome/browser/chromeos/ui/request_pin_view.cc
index fbb2000..21794797 100644
--- a/chrome/browser/chromeos/ui/request_pin_view.cc
+++ b/chrome/browser/chromeos/ui/request_pin_view.cc
@@ -162,7 +162,10 @@
 }
 
 void RequestPinView::Init() {
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
+
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   int column_view_set_id = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc
index 60a83d8f..115ecca 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.cc
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc
@@ -17,6 +17,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/offline_pages/offline_page_mhtml_archiver.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
 #include "chrome/browser/offline_pages/offline_page_utils.h"
@@ -282,6 +283,18 @@
   return ToJavaOfflinePageItem(env, offline_page);
 }
 
+// static
+std::string OfflinePageBridge::GetEncodedOriginApp(
+    const content::WebContents* web_contents) {
+  TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
+  if (!tab)
+    return "";
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return ConvertJavaStringToUTF8(
+      env,
+      Java_OfflinePageBridge_getEncodedOriginApp(env, tab->GetJavaObject()));
+}
+
 OfflinePageBridge::OfflinePageBridge(JNIEnv* env,
                                      content::BrowserContext* browser_context,
                                      OfflinePageModel* offline_page_model)
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.h b/chrome/browser/offline_pages/android/offline_page_bridge.h
index 2fd6758..800e5b8 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.h
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.h
@@ -17,6 +17,7 @@
 
 namespace content {
 class BrowserContext;
+class WebContents;
 }
 
 namespace offline_pages {
@@ -33,6 +34,9 @@
       JNIEnv* env,
       const OfflinePageItem& offline_page);
 
+  static std::string GetEncodedOriginApp(
+      const content::WebContents* web_contents);
+
   OfflinePageBridge(JNIEnv* env,
                     content::BrowserContext* browser_context,
                     OfflinePageModel* offline_page_model);
diff --git a/chrome/browser/offline_pages/android/offline_page_origin_utils_android.cc b/chrome/browser/offline_pages/android/offline_page_origin_utils_android.cc
new file mode 100644
index 0000000..31d32cd
--- /dev/null
+++ b/chrome/browser/offline_pages/android/offline_page_origin_utils_android.cc
@@ -0,0 +1,16 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/offline_pages/offline_page_origin_utils.h"
+
+#include "chrome/browser/offline_pages/android/offline_page_bridge.h"
+#include "content/public/browser/web_contents.h"
+
+namespace offline_pages {
+// static
+std::string OfflinePageOriginUtils::GetEncodedOriginAppFor(
+    content::WebContents* web_contents) {
+  return android::OfflinePageBridge::GetEncodedOriginApp(web_contents);
+}
+}  // namespace offline_pages
diff --git a/chrome/browser/offline_pages/offline_page_origin_utils.h b/chrome/browser/offline_pages/offline_page_origin_utils.h
new file mode 100644
index 0000000..2f26b86
--- /dev/null
+++ b/chrome/browser/offline_pages/offline_page_origin_utils.h
@@ -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.
+
+#ifndef CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_ORIGIN_UTILS_H_
+#define CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_ORIGIN_UTILS_H_
+
+#include <string>
+
+namespace content {
+class WebContents;
+}
+
+namespace offline_pages {
+
+// Utility class for retrieving origin for a download request - whether it
+// came on behalf of some app via CCT or Chrome. Methods are platform-specific.
+class OfflinePageOriginUtils {
+ public:
+  // Retrieves the encoded origin from the |web_contents|.
+  static std::string GetEncodedOriginAppFor(content::WebContents* web_contents);
+};
+}  // namespace offline_pages
+
+#endif  // CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_ORIGIN_UTILS_H_
diff --git a/chrome/browser/offline_pages/offline_page_utils.cc b/chrome/browser/offline_pages/offline_page_utils.cc
index f017a45e..435fc437 100644
--- a/chrome/browser/offline_pages/offline_page_utils.cc
+++ b/chrome/browser/offline_pages/offline_page_utils.cc
@@ -14,10 +14,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "chrome/browser/offline_pages/offline_page_tab_helper.h"
 #include "chrome/browser/net/net_error_tab_helper.h"
 #include "chrome/browser/offline_pages/offline_page_mhtml_archiver.h"
 #include "chrome/browser/offline_pages/offline_page_model_factory.h"
+#include "chrome/browser/offline_pages/offline_page_origin_utils.h"
+#include "chrome/browser/offline_pages/offline_page_tab_helper.h"
 #include "chrome/browser/offline_pages/request_coordinator_factory.h"
 #include "components/offline_pages/core/background/request_coordinator.h"
 #include "components/offline_pages/core/background/save_page_request.h"
@@ -300,7 +301,9 @@
                                         const std::string& name_space,
                                         const GURL& url,
                                         DownloadUIActionFlags ui_action) {
-  ScheduleDownload(web_contents, name_space, url, ui_action, "");
+  std::string origin =
+      OfflinePageOriginUtils::GetEncodedOriginAppFor(web_contents);
+  ScheduleDownload(web_contents, name_space, url, ui_action, origin);
 }
 
 // static
diff --git a/chrome/browser/plugins/pdf_plugin_placeholder_observer.cc b/chrome/browser/plugins/pdf_plugin_placeholder_observer.cc
index 65fc578..e8ec95a 100644
--- a/chrome/browser/plugins/pdf_plugin_placeholder_observer.cc
+++ b/chrome/browser/plugins/pdf_plugin_placeholder_observer.cc
@@ -5,8 +5,29 @@
 #include "chrome/browser/plugins/pdf_plugin_placeholder_observer.h"
 
 #include "chrome/common/render_messages.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/download_item.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/download_url_parameters.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "net/traffic_annotation/network_traffic_annotation.h"
+#include "ppapi/features/features.h"
+
+namespace {
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+void OnDownloadStarted(content::DownloadItem* item,
+                       content::DownloadInterruptReason interrupt_reason) {
+  if (item && interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NONE)
+    item->SetOpenWhenComplete(true);
+}
+#endif  // BUILDFLAG(ENABLE_PLUGINS)
+
+}  // namespace
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(PDFPluginPlaceholderObserver);
 
@@ -36,11 +57,59 @@
     return;
   }
 
-  web_contents()->OpenURL(content::OpenURLParams(
-      url,
-      content::Referrer::SanitizeForRequest(
-          url, content::Referrer(web_contents()->GetURL(),
-                                 blink::kWebReferrerPolicyDefault)),
-      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_AUTO_BOOKMARK,
-      false));
+  content::Referrer referrer = content::Referrer::SanitizeForRequest(
+      url, content::Referrer(web_contents()->GetURL(),
+                             blink::kWebReferrerPolicyDefault));
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+  content::StoragePartition* storage_partition =
+      content::BrowserContext::GetStoragePartition(
+          web_contents()->GetBrowserContext(),
+          render_frame_host->GetSiteInstance());
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("pdf_plugin_placeholder", R"(
+        semantics {
+          sender: "PDF Plugin Placeholder"
+          description:
+            "When the PDF Viewer is unavailable, a placeholder is shown for "
+            "embedded PDFs. This placeholder allows the user to download and "
+            "open the PDF file via a button."
+          trigger:
+            "The user clicks the 'View PDF' button in the PDF placeholder."
+          data: "None."
+          destination: WEBSITE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "This feature can be disabled via 'Download PDF files instead of "
+            "automatically opening them in Chrome' in settings under content. "
+            "The feature is disabled by default."
+          chrome_policy {
+            AlwaysOpenPdfExternally {
+              AlwaysOpenPdfExternally: false
+            }
+          }
+        })");
+  std::unique_ptr<content::DownloadUrlParameters> params =
+      base::MakeUnique<content::DownloadUrlParameters>(
+          url, web_contents()->GetRenderProcessHost()->GetID(),
+          web_contents()->GetRenderViewHost()->GetRoutingID(),
+          render_frame_host->GetRoutingID(),
+          storage_partition->GetURLRequestContext(), traffic_annotation);
+  params->set_referrer(referrer);
+  params->set_callback(base::Bind(&OnDownloadStarted));
+
+  content::BrowserContext::GetDownloadManager(
+      web_contents()->GetBrowserContext())
+      ->DownloadUrl(std::move(params));
+
+#else   // !BUILDFLAG(ENABLE_PLUGINS)
+  content::OpenURLParams open_url_params(
+      url, referrer, WindowOpenDisposition::CURRENT_TAB,
+      ui::PAGE_TRANSITION_AUTO_BOOKMARK, false);
+  // On Android, PDFs downloaded with a user gesture are auto-opened.
+  open_url_params.user_gesture = true;
+  web_contents()->OpenURL(open_url_params);
+#endif  // BUILDFLAG(ENABLE_PLUGINS)
 }
diff --git a/chrome/browser/resources/md_user_manager/user_manager.html b/chrome/browser/resources/md_user_manager/user_manager.html
index c838cf33..95537c92 100644
--- a/chrome/browser/resources/md_user_manager/user_manager.html
+++ b/chrome/browser/resources/md_user_manager/user_manager.html
@@ -56,6 +56,7 @@
      * manager. */
 
     #outer-container {
+      -webkit-box-orient: vertical;
       min-height: 0;
       overflow-x: hidden;
       overflow-y: auto;
@@ -308,11 +309,23 @@
       };
       @apply(--action-button);
     }
+
+    #user-manager-prompt-message {
+      font-size: 19px;
+      margin-bottom: 45px;
+      text-align: center;
+    }
+
+    #user-manager-prompt-message:empty {
+      display: none;
+    }
+
   </style>
 </head>
 <body>
   <user-manager-pages>
     <div id="outer-container">
+      <div id="user-manager-prompt-message">$i18n{userManagerPromptMessage}</div>
       <user-manager-tutorial></user-manager-tutorial>
       <div id="oobe" class="faded">
         <div id="inner-container">
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index b0f6c6d5..8344b6b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2857,6 +2857,8 @@
         "cocoa/multi_key_equivalent_button.mm",
         "cocoa/new_tab_button.h",
         "cocoa/new_tab_button.mm",
+        "cocoa/nsview_additions.h",
+        "cocoa/nsview_additions.mm",
         "cocoa/omnibox/omnibox_popup_cell.h",
         "cocoa/omnibox/omnibox_popup_cell.mm",
         "cocoa/omnibox/omnibox_popup_matrix.h",
diff --git a/chrome/browser/ui/ash/app_list/app_list_browsertest.cc b/chrome/browser/ui/ash/app_list/app_list_browsertest.cc
new file mode 100644
index 0000000..0ad0ae1
--- /dev/null
+++ b/chrome/browser/ui/ash/app_list/app_list_browsertest.cc
@@ -0,0 +1,99 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/app_browsertest_util.h"
+#include "chrome/browser/chromeos/ash_config.h"
+#include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
+#include "chrome/browser/ui/ash/app_list/app_list_service_ash.h"
+#include "chrome/browser/ui/ash/app_list/test/app_list_service_ash_test_api.h"
+#include "chrome/browser/ui/extensions/app_launch_params.h"
+#include "chrome/browser/ui/extensions/application_launch.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/test/test_utils.h"
+#include "ui/app_list/presenter/app_list_presenter_impl.h"
+#include "ui/app_list/views/app_list_item_view.h"
+#include "ui/app_list/views/apps_grid_view.h"
+#include "ui/aura/window.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/events/test/event_generator.h"
+#include "ui/wm/core/window_util.h"
+
+using AppListTest = InProcessBrowserTest;
+using AppListControllerDelegateAshTest = extensions::PlatformAppBrowserTest;
+
+// Test that clicking on app list context menus doesn't close the app list.
+IN_PROC_BROWSER_TEST_F(AppListTest, ClickingContextMenuDoesNotDismiss) {
+  // Show the app list on the primary display.
+  AppListServiceAsh* service = AppListServiceAsh::GetInstance();
+  app_list::AppListPresenterImpl* presenter = service->GetAppListPresenter();
+  presenter->Show(display::Screen::GetScreen()->GetPrimaryDisplay().id());
+  aura::Window* window = presenter->GetWindow();
+  ASSERT_TRUE(window);
+
+  // Show a context menu for the first app list item view.
+  AppListServiceAshTestApi test_api;
+  app_list::AppsGridView* grid_view = test_api.GetRootGridView();
+  app_list::AppListItemView* item_view = grid_view->GetItemViewAt(0);
+  item_view->ShowContextMenu(gfx::Point(), ui::MENU_SOURCE_MOUSE);
+
+  // Find the context menu as a transient child of the app list.
+  aura::Window* transient_parent =
+      chromeos::GetAshConfig() == ash::Config::MASH ? window->parent() : window;
+  const std::vector<aura::Window*>& transient_children =
+      wm::GetTransientChildren(transient_parent);
+  ASSERT_EQ(1u, transient_children.size());
+  aura::Window* menu = transient_children[0];
+
+  // Press the left mouse button on the menu window, AppListPresenterDelegateMus
+  // should not close the app list nor the context menu on this pointer event.
+  ui::test::EventGenerator menu_event_generator(menu);
+  menu_event_generator.set_current_location(menu->GetBoundsInScreen().origin());
+  menu_event_generator.PressLeftButton();
+
+  // Check that the window and the app list are still open.
+  ASSERT_EQ(window, presenter->GetWindow());
+  EXPECT_EQ(1u, wm::GetTransientChildren(transient_parent).size());
+}
+
+// Test AppListControllerDelegateAsh::IsAppOpen for extension apps.
+IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsExtensionAppOpen) {
+  AppListControllerDelegateAsh delegate(nullptr);
+  EXPECT_FALSE(delegate.IsAppOpen("fake_extension_app_id"));
+
+  base::FilePath extension_path = test_data_dir_.AppendASCII("app");
+  const extensions::Extension* extension_app = LoadExtension(extension_path);
+  ASSERT_NE(nullptr, extension_app);
+  EXPECT_FALSE(delegate.IsAppOpen(extension_app->id()));
+  {
+    content::WindowedNotificationObserver app_loaded_observer(
+        content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
+        content::NotificationService::AllSources());
+    OpenApplication(AppLaunchParams(
+        profile(), extension_app, extensions::LAUNCH_CONTAINER_WINDOW,
+        WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
+    app_loaded_observer.Wait();
+  }
+  EXPECT_TRUE(delegate.IsAppOpen(extension_app->id()));
+}
+
+// Test AppListControllerDelegateAsh::IsAppOpen for platform apps.
+IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsPlatformAppOpen) {
+  AppListControllerDelegateAsh delegate(nullptr);
+  EXPECT_FALSE(delegate.IsAppOpen("fake_platform_app_id"));
+
+  const extensions::Extension* app = InstallPlatformApp("minimal");
+  EXPECT_FALSE(delegate.IsAppOpen(app->id()));
+  {
+    content::WindowedNotificationObserver app_loaded_observer(
+        content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
+        content::NotificationService::AllSources());
+    LaunchPlatformApp(app);
+    app_loaded_observer.Wait();
+  }
+  EXPECT_TRUE(delegate.IsAppOpen(app->id()));
+}
diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash_browsertest.cc b/chrome/browser/ui/ash/app_list/app_list_controller_ash_browsertest.cc
deleted file mode 100644
index 92ed5965..0000000
--- a/chrome/browser/ui/ash/app_list/app_list_controller_ash_browsertest.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/apps/app_browsertest_util.h"
-#include "chrome/browser/extensions/extension_browsertest.h"
-#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h"
-#include "chrome/browser/ui/extensions/app_launch_params.h"
-#include "chrome/browser/ui/extensions/application_launch.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/test/test_utils.h"
-
-using AppListControllerDelegateAshTest = extensions::PlatformAppBrowserTest;
-
-// Test AppListControllerDelegateAsh::IsAppOpen for extension apps.
-IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsExtensionAppOpen) {
-  AppListControllerDelegateAsh delegate(nullptr);
-  EXPECT_FALSE(delegate.IsAppOpen("fake_extension_app_id"));
-
-  base::FilePath extension_path = test_data_dir_.AppendASCII("app");
-  const extensions::Extension* extension_app = LoadExtension(extension_path);
-  ASSERT_NE(nullptr, extension_app);
-  EXPECT_FALSE(delegate.IsAppOpen(extension_app->id()));
-  {
-    content::WindowedNotificationObserver app_loaded_observer(
-        content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-        content::NotificationService::AllSources());
-    OpenApplication(AppLaunchParams(
-        profile(), extension_app, extensions::LAUNCH_CONTAINER_WINDOW,
-        WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST));
-    app_loaded_observer.Wait();
-  }
-  EXPECT_TRUE(delegate.IsAppOpen(extension_app->id()));
-}
-
-// Test AppListControllerDelegateAsh::IsAppOpen for platform apps.
-IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsPlatformAppOpen) {
-  AppListControllerDelegateAsh delegate(nullptr);
-  EXPECT_FALSE(delegate.IsAppOpen("fake_platform_app_id"));
-
-  const extensions::Extension* app = InstallPlatformApp("minimal");
-  EXPECT_FALSE(delegate.IsAppOpen(app->id()));
-  {
-    content::WindowedNotificationObserver app_loaded_observer(
-        content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
-        content::NotificationService::AllSources());
-    LaunchPlatformApp(app);
-    app_loaded_observer.Wait();
-  }
-  EXPECT_TRUE(delegate.IsAppOpen(app->id()));
-}
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
index 0eb2dc3..9ff2aea8 100644
--- a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
+++ b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
@@ -7,10 +7,12 @@
 #include "ui/app_list/presenter/app_list_presenter_impl.h"
 #include "ui/app_list/presenter/app_list_view_delegate_factory.h"
 #include "ui/app_list/views/app_list_view.h"
+#include "ui/aura/window.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/views/mus/mus_client.h"
 #include "ui/views/mus/pointer_watcher_event_router.h"
+#include "ui/wm/core/window_util.h"
 
 namespace {
 
@@ -115,11 +117,28 @@
     const ui::PointerEvent& event,
     const gfx::Point& location_in_screen,
     gfx::NativeView target) {
+  // Looks for touch pressed and pointer down events outside the app list.
+  if (event.type() != ui::ET_TOUCH_PRESSED &&
+      event.type() != ui::ET_POINTER_DOWN) {
+    return;
+  }
+
+  // Bail if there is no app list, or if the event targets the app list.
   views::Widget* target_widget =
       views::Widget::GetTopLevelWidgetForNativeView(target);
-  // Dismiss app list on a mouse click or touch outside of the app list window.
-  if ((event.type() == ui::ET_TOUCH_PRESSED ||
-       event.type() == ui::ET_POINTER_DOWN) &&
-      (!target || (view_ && target_widget != view_->GetWidget())))
-    presenter_->Dismiss();
+  views::Widget* app_list_widget = view_ ? view_->GetWidget() : nullptr;
+  if (!app_list_widget || target_widget == app_list_widget)
+    return;
+
+  // Bail if the event targets a context menu in the app list window. In mash,
+  // app list context menus are configured such that the parent of the app list
+  // window is the transient parent of the context menu window's parent. Yikes!
+  aura::Window* app_list = app_list_widget->GetNativeWindow();
+  if (app_list && app_list->parent() && target && target->parent() &&
+      wm::HasTransientAncestor(target->parent(), app_list->parent())) {
+    return;
+  }
+
+  // Dismiss the app list for all other event targets, including null.
+  presenter_->Dismiss();
 }
diff --git a/chrome/browser/ui/cocoa/nsview_additions.h b/chrome/browser/ui/cocoa/nsview_additions.h
new file mode 100644
index 0000000..b9018cf
--- /dev/null
+++ b/chrome/browser/ui/cocoa/nsview_additions.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_NSVIEW_ADDITIONS_H_
+#define CHROME_BROWSER_UI_COCOA_NSVIEW_ADDITIONS_H_
+
+#import <AppKit/AppKit.h>
+
+@interface NSView (ChromeBrowserAdditions)
+
+// If the UI is in RTL mode, swaps NSViewMinXMargin and NSViewMaxXMargin.
++ (NSAutoresizingMaskOptions)cr_localizedAutoresizingMask:
+    (NSAutoresizingMaskOptions)mask;
+
+// If the UI is in RTL mode, flips the rect in the receiver's bounds.
+- (NSRect)cr_localizedRect:(NSRect)rect;
+
+@end
+
+#endif  // CHROME_BROWSER_UI_COCOA_NSVIEW_ADDITIONS_H_
diff --git a/chrome/browser/ui/cocoa/nsview_additions.mm b/chrome/browser/ui/cocoa/nsview_additions.mm
new file mode 100644
index 0000000..232d476
--- /dev/null
+++ b/chrome/browser/ui/cocoa/nsview_additions.mm
@@ -0,0 +1,30 @@
+// 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 "chrome/browser/ui/cocoa/nsview_additions.h"
+
+#import "chrome/browser/ui/cocoa/l10n_util.h"
+
+@implementation NSView (ChromeBrowserAdditions)
+
++ (NSAutoresizingMaskOptions)cr_localizedAutoresizingMask:
+    (NSAutoresizingMaskOptions)mask {
+  if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) {
+    // If exactly one of NSViewMinXMargin and NSViewMaxXMargin are set…
+    if (((mask & NSViewMinXMargin) != 0) != ((mask & NSViewMaxXMargin) != 0)) {
+      // …then swap it with the opposite one.
+      mask ^= NSViewMinXMargin | NSViewMaxXMargin;
+    }
+  }
+  return mask;
+}
+
+- (NSRect)cr_localizedRect:(NSRect)rect {
+  if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) {
+    rect.origin.x = NSWidth(self.bounds) - NSWidth(rect) - NSMinX(rect);
+  }
+  return rect;
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/nsview_additions_unittest.mm b/chrome/browser/ui/cocoa/nsview_additions_unittest.mm
new file mode 100644
index 0000000..a3faa33
--- /dev/null
+++ b/chrome/browser/ui/cocoa/nsview_additions_unittest.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 "chrome/browser/ui/cocoa/nsview_additions.h"
+
+#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h"
+#import "chrome/browser/ui/cocoa/test/scoped_force_rtl_mac.h"
+
+namespace {
+
+class NSViewAdditionsTest : public ui::CocoaTest {};
+
+TEST_F(NSViewAdditionsTest, LocalizedAutoresizingMask) {
+  const struct {
+    NSAutoresizingMaskOptions ltr;
+    NSAutoresizingMaskOptions rtl;
+  } kCases[] = {
+      // A mask without x margin options shouldn't be changed.
+      {NSViewWidthSizable | NSViewHeightSizable,
+       NSViewWidthSizable | NSViewHeightSizable},
+
+      // …nor should a mask with both x margin options.
+      {NSViewMinXMargin | NSViewMaxXMargin,
+       NSViewMinXMargin | NSViewMaxXMargin},
+
+      // MinXMargin becomes MaxXMargin,
+      {NSViewMinXMargin, NSViewMaxXMargin},
+
+      // MaxXMargin becomes MinXMargin.
+      {NSViewMaxXMargin, NSViewMinXMargin},
+
+      // Y margins should be left alone.
+      {NSViewMinYMargin | NSViewMinXMargin,
+       NSViewMinYMargin | NSViewMaxXMargin},
+  };
+
+  for (const auto& pair : kCases) {
+    EXPECT_EQ(pair.ltr, [NSView cr_localizedAutoresizingMask:pair.ltr]);
+  }
+
+  cocoa_l10n_util::ScopedForceRTLMac rtl;
+
+  for (const auto& pair : kCases) {
+    EXPECT_EQ(pair.rtl, [NSView cr_localizedAutoresizingMask:pair.ltr]);
+  }
+}
+
+TEST_F(NSViewAdditionsTest, LocalizedRect) {
+  NSView* contentView = test_window().contentView;
+  NSRect rect = NSMakeRect(0, 0, 10, 10);
+  EXPECT_TRUE(NSEqualRects(rect, [contentView cr_localizedRect:rect]));
+
+  cocoa_l10n_util::ScopedForceRTLMac rtl;
+
+  EXPECT_TRUE(
+      NSEqualRects(NSMakeRect(NSMaxX(contentView.bounds) - 10, 0, 10, 10),
+                   [test_window().contentView cr_localizedRect:rect]));
+}
+
+}  // namespace
diff --git a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
index 441bbec..d87f871 100644
--- a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
+++ b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
@@ -81,6 +81,10 @@
 }
 
 void InvertBubbleView::Init() {
+  const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
+  SetBorder(views::CreateEmptyBorder(
+      provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS)));
+
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   const gfx::FontList& original_font_list =
       rb.GetFontList(ui::ResourceBundle::MediumFont);
@@ -108,7 +112,7 @@
   close_->SetFontList(original_font_list);
   close_->set_listener(this);
 
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   views::ColumnSet* columns = layout->AddColumnSet(0);
   for (int i = 0; i < 4; i++) {
@@ -119,9 +123,9 @@
 
   layout->StartRow(0, 0);
   layout->AddView(title, 4, 1);
-  layout->StartRowWithPadding(0, 0, 0,
-                              ChromeLayoutProvider::Get()->GetDistanceMetric(
-                                  DISTANCE_RELATED_CONTROL_VERTICAL_SMALL));
+  layout->StartRowWithPadding(
+      0, 0, 0,
+      provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_VERTICAL_SMALL));
   layout->AddView(high_contrast_);
   layout->AddView(dark_theme_);
   layout->AddView(learn_more_);
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc
index ab3da0f..063c51bf0 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc
@@ -90,8 +90,7 @@
 class BulletedPermissionsList : public views::View {
  public:
   BulletedPermissionsList() {
-    layout_ = new views::GridLayout(this);
-    SetLayoutManager(layout_);
+    layout_ = views::GridLayout::CreateAndInstall(this);
 
     // Create 3 columns: the bullet, the bullet text, and the revoke button.
     views::ColumnSet* column_set = layout_->AddColumnSet(kBulletColumnSetId);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index 001abcf7..d97d71e 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -296,8 +296,7 @@
 
   SetLayoutManager(new views::FillLayout());
   bookmark_contents_view_ = new views::View();
-  GridLayout* layout = new GridLayout(bookmark_contents_view_);
-  bookmark_contents_view_->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(bookmark_contents_view_);
 
   // This column set is used for the labels and textfields.
   constexpr int kColumnId = 0;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
index 8e84959..b477990 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -72,6 +72,8 @@
   DCHECK(profile);
   DCHECK(bb_model_);
   DCHECK(bb_model_->client()->CanBeEditedByUser(parent));
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
   Init();
   chrome::RecordDialogCreation(chrome::DialogIdentifier::BOOKMARK_EDITOR);
 }
@@ -335,7 +337,7 @@
     new_folder_button_->SetEnabled(false);
   }
 
-  GridLayout* layout = GridLayout::CreatePanel(this);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
 
   const int labels_column_set_id = 0;
diff --git a/chrome/browser/ui/views/certificate_selector.cc b/chrome/browser/ui/views/certificate_selector.cc
index dc75b6e..34450c71 100644
--- a/chrome/browser/ui/views/certificate_selector.cc
+++ b/chrome/browser/ui/views/certificate_selector.cc
@@ -118,6 +118,9 @@
     : web_contents_(web_contents) {
   CHECK(web_contents_);
 
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
+
   // |provider_names| and |identities_| are parallel arrays.
   // The entry at index |i| is the provider name for |identities_[i]|.
   std::vector<std::string> provider_names;
@@ -197,7 +200,7 @@
 
 void CertificateSelector::InitWithText(
     std::unique_ptr<views::View> text_label) {
-  views::GridLayout* const layout = views::GridLayout::CreatePanel(this);
+  views::GridLayout* const layout = views::GridLayout::CreateAndInstall(this);
 
   const int kColumnSetId = 0;
   views::ColumnSet* const column_set = layout->AddColumnSet(kColumnSetId);
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc
index b45cd14..e168893 100644
--- a/chrome/browser/ui/views/collected_cookies_views.cc
+++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -313,13 +313,12 @@
 void CollectedCookiesViews::Init() {
   using views::GridLayout;
 
-  GridLayout* layout = new GridLayout(this);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
   if (provider->UseExtraDialogPadding()) {
     SetBorder(
         views::CreateEmptyBorder(gfx::Insets(kTabbedPaneTopPadding, 0, 0, 0)));
   }
-  SetLayoutManager(layout);
 
   const int single_column_layout_id = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(single_column_layout_id);
@@ -387,7 +386,10 @@
   using views::GridLayout;
 
   views::View* pane = new views::View();
-  GridLayout* layout = GridLayout::CreatePanel(pane);
+  GridLayout* layout = GridLayout::CreateAndInstall(pane);
+  pane->SetBorder(
+      views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric(
+          views::INSETS_DIALOG_CONTENTS)));
   int unrelated_vertical_distance =
       ChromeLayoutProvider::Get()->GetDistanceMetric(
           views::DISTANCE_UNRELATED_CONTROL_VERTICAL);
@@ -451,7 +453,10 @@
   using views::GridLayout;
 
   views::View* pane = new views::View();
-  GridLayout* layout = GridLayout::CreatePanel(pane);
+  GridLayout* layout = GridLayout::CreateAndInstall(pane);
+  pane->SetBorder(
+      views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric(
+          views::INSETS_DIALOG_CONTENTS)));
   int unrelated_vertical_distance =
       ChromeLayoutProvider::Get()->GetDistanceMetric(
           views::DISTANCE_UNRELATED_CONTROL_VERTICAL);
diff --git a/chrome/browser/ui/views/confirm_bubble_views.cc b/chrome/browser/ui/views/confirm_bubble_views.cc
index 161a1f8..dd8199a 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.cc
+++ b/chrome/browser/ui/views/confirm_bubble_views.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/confirm_bubble.h"
 #include "chrome/browser/ui/confirm_bubble_model.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "components/constrained_window/constrained_window_views.h"
 #include "ui/base/ui_features.h"
 #include "ui/views/controls/label.h"
@@ -19,7 +20,9 @@
 ConfirmBubbleViews::ConfirmBubbleViews(
     std::unique_ptr<ConfirmBubbleModel> model)
     : model_(std::move(model)), link_(NULL) {
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   // Use a fixed maximum message width, so longer messages will wrap.
   const int kMaxMessageWidth = 400;
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc
index 3bc6f4bb1..06e97b2 100644
--- a/chrome/browser/ui/views/content_setting_bubble_contents.cc
+++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -249,8 +249,7 @@
 
 void ContentSettingBubbleContents::ListItemContainer::ResetLayout() {
   using views::GridLayout;
-  GridLayout* layout = new GridLayout(this);
-  SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
   views::ColumnSet* item_list_column_set = layout->AddColumnSet(0);
   item_list_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0,
                                   GridLayout::USE_PREF, 0, 0);
@@ -344,8 +343,7 @@
 void ContentSettingBubbleContents::Init() {
   using views::GridLayout;
 
-  GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
   const int related_control_horizontal_spacing =
       provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
diff --git a/chrome/browser/ui/views/cookie_info_view.cc b/chrome/browser/ui/views/cookie_info_view.cc
index da53a246..67fdaa8 100644
--- a/chrome/browser/ui/views/cookie_info_view.cc
+++ b/chrome/browser/ui/views/cookie_info_view.cc
@@ -146,8 +146,7 @@
       l10n_util::GetStringUTF16(IDS_COOKIES_COOKIE_EXPIRES_LABEL));
   expires_value_field_ = new views::Textfield;
 
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
   const gfx::Insets& dialog_insets =
       provider->GetInsetsMetric(views::INSETS_DIALOG);
diff --git a/chrome/browser/ui/views/create_application_shortcut_view.cc b/chrome/browser/ui/views/create_application_shortcut_view.cc
index 6f80396..0ea459a 100644
--- a/chrome/browser/ui/views/create_application_shortcut_view.cc
+++ b/chrome/browser/ui/views/create_application_shortcut_view.cc
@@ -51,6 +51,8 @@
       menu_check_box_(nullptr),
       quick_launch_check_box_(nullptr),
       weak_ptr_factory_(this) {
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
   InitControls();
 
   // Get shortcut and icon information; needed for creating the shortcut.
@@ -106,7 +108,7 @@
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
 
   // Layout controls
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   static const int kHeaderColumnSetId = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(kHeaderColumnSetId);
diff --git a/chrome/browser/ui/views/crypto_module_password_dialog_view.cc b/chrome/browser/ui/views/crypto_module_password_dialog_view.cc
index 256c7ea..d302903 100644
--- a/chrome/browser/ui/views/crypto_module_password_dialog_view.cc
+++ b/chrome/browser/ui/views/crypto_module_password_dialog_view.cc
@@ -27,6 +27,8 @@
     const std::string& hostname,
     const CryptoModulePasswordCallback& callback)
     : callback_(callback) {
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
   Init(hostname, slot_name, reason);
   chrome::RecordDialogCreation(chrome::DialogIdentifier::CRYPTO_PASSWORD);
 }
@@ -125,7 +127,7 @@
 
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
 
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   views::ColumnSet* reason_column_set = layout->AddColumnSet(0);
   reason_column_set->AddColumn(
diff --git a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc
index a3273db..0639acf 100644
--- a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc
+++ b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc
@@ -40,8 +40,7 @@
           base::MakeUnique<DesktopIOSPromotionBubbleController>(profile,
                                                                 this,
                                                                 entry_point)) {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
   SetBorder(views::CreateEmptyBorder(
       0,
diff --git a/chrome/browser/ui/views/download/download_danger_prompt_views.cc b/chrome/browser/ui/views/download/download_danger_prompt_views.cc
index a9fc68d6..9f170f9 100644
--- a/chrome/browser/ui/views/download/download_danger_prompt_views.cc
+++ b/chrome/browser/ui/views/download/download_danger_prompt_views.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/download/download_stats.h"
 #include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
 #include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/constrained_window/constrained_window_views.h"
@@ -95,7 +96,10 @@
 
   contents_view_ = new views::View;
 
-  views::GridLayout* layout = views::GridLayout::CreatePanel(contents_view_);
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
+  views::GridLayout* layout =
+      views::GridLayout::CreateAndInstall(contents_view_);
 
   views::ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
diff --git a/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc b/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc
index 425ddfa..865bd18 100644
--- a/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc
+++ b/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc
@@ -65,7 +65,8 @@
       open_as_window_checkbox_(nullptr),
       title_tf_(nullptr) {
   const ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  set_margins(layout_provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS));
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   const int column_set_id = 0;
 
   views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
index 6f62003..3a00480 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -156,8 +156,7 @@
 }  // namespace
 
 BulletedView::BulletedView(views::View* view) {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   views::ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(views::GridLayout::CENTER,
                         views::GridLayout::LEADING,
@@ -290,8 +289,8 @@
   // Create the scrollable view which will contain the permissions and retained
   // files/devices. It will span the full content width.
   CustomScrollableView* scrollable = new CustomScrollableView();
-  views::GridLayout* scroll_layout = new views::GridLayout(scrollable);
-  scrollable->SetLayoutManager(scroll_layout);
+  views::GridLayout* scroll_layout =
+      views::GridLayout::CreateAndInstall(scrollable);
 
   views::ColumnSet* scrollable_column_set =
       scroll_layout->AddColumnSet(column_set_id);
@@ -455,8 +454,7 @@
   // done so that the extension icon can be shown on the right of the dialog
   // title, but on the same y-axis, and the scroll view used to contain other
   // content can have its scrollbar aligned with the right edge of the dialog.
-  views::GridLayout* layout = new views::GridLayout(container_);
-  container_->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(container_);
   container_->SetBorder(views::CreateEmptyBorder(0, content_insets.left(),
                                                  content_insets.bottom(), 0));
   AddChildView(container_);
@@ -630,9 +628,7 @@
 
 ExpandableContainerView::DetailsView::DetailsView(int horizontal_space,
                                                   bool parent_bulleted)
-    : layout_(new views::GridLayout(this)),
-      state_(0) {
-  SetLayoutManager(layout_);
+    : layout_(views::GridLayout::CreateAndInstall(this)), state_(0) {
   views::ColumnSet* column_set = layout_->AddColumnSet(0);
   const int padding = GetLeftPaddingForBulletedItems(parent_bulleted);
   column_set->AddPaddingColumn(0, padding);
@@ -675,8 +671,7 @@
       more_details_(NULL),
       arrow_toggle_(NULL),
       expanded_(false) {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   int column_set_id = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
   column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
index a2a3adc..7b2f4fe 100644
--- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
+++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
@@ -99,9 +99,12 @@
   contents_->RemoveAllChildViews(true);
   checkbox_map_.clear();
 
-  int dialog_content_width = views::Widget::GetLocalizedContentsWidth(
+  const int dialog_content_width = views::Widget::GetLocalizedContentsWidth(
       IDS_MEDIA_GALLERIES_DIALOG_CONTENT_WIDTH_CHARS);
-  views::GridLayout* layout = views::GridLayout::CreatePanel(contents_);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(contents_);
+  contents_->SetBorder(
+      views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric(
+          views::INSETS_DIALOG_CONTENTS)));
 
   int column_set_id = 0;
   views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
diff --git a/chrome/browser/ui/views/first_run_bubble.cc b/chrome/browser/ui/views/first_run_bubble.cc
index 2e8d4f24..539e4a7 100644
--- a/chrome/browser/ui/views/first_run_bubble.cc
+++ b/chrome/browser/ui/views/first_run_bubble.cc
@@ -68,7 +68,7 @@
   views::Label* subtext = new views::Label(
       l10n_util::GetStringUTF16(IDS_FR_BUBBLE_SUBTEXT), {original_font_list});
 
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   SetBorder(views::CreateEmptyBorder(kTopInset, kLeftInset, kBottomInset,
                                      kRightInset));
 
diff --git a/chrome/browser/ui/views/first_run_dialog.cc b/chrome/browser/ui/views/first_run_dialog.cc
index d90bd93..b409d21 100644
--- a/chrome/browser/ui/views/first_run_dialog.cc
+++ b/chrome/browser/ui/views/first_run_dialog.cc
@@ -80,7 +80,9 @@
     : profile_(profile),
       make_default_(NULL),
       report_crashes_(NULL) {
-  GridLayout* layout = GridLayout::CreatePanel(this);
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
 
   views::ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
diff --git a/chrome/browser/ui/views/global_error_bubble_view.cc b/chrome/browser/ui/views/global_error_bubble_view.cc
index 6b63fad..3d16cba 100644
--- a/chrome/browser/ui/views/global_error_bubble_view.cc
+++ b/chrome/browser/ui/views/global_error_bubble_view.cc
@@ -116,8 +116,7 @@
     message_labels.push_back(message_label);
   }
 
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   // First row, message labels.
   views::ColumnSet* cs = layout->AddColumnSet(0);
diff --git a/chrome/browser/ui/views/hung_renderer_view.cc b/chrome/browser/ui/views/hung_renderer_view.cc
index ea381ada..30b2727 100644
--- a/chrome/browser/ui/views/hung_renderer_view.cc
+++ b/chrome/browser/ui/views/hung_renderer_view.cc
@@ -223,6 +223,8 @@
 
 HungRendererDialogView::HungRendererDialogView()
     : info_label_(nullptr), hung_pages_table_(nullptr), initialized_(false) {
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
   chrome::RecordDialogCreation(chrome::DialogIdentifier::HUNG_RENDERER);
 }
 
@@ -415,7 +417,7 @@
 
   using views::GridLayout;
 
-  GridLayout* layout = GridLayout::CreatePanel(this);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
 
   constexpr int kColumnSetId = 0;
diff --git a/chrome/browser/ui/views/ime/ime_warning_bubble_view.cc b/chrome/browser/ui/views/ime/ime_warning_bubble_view.cc
index 01aad4c..a4f7893 100644
--- a/chrome/browser/ui/views/ime/ime_warning_bubble_view.cc
+++ b/chrome/browser/ui/views/ime/ime_warning_bubble_view.cc
@@ -159,8 +159,7 @@
   // -----------------------------------------
   //
 
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   int cs_id = 0;
 
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index 03995b6..f9b98b41 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -163,8 +163,7 @@
 }
 
 void IntentPickerBubbleView::Init() {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   // Creates a view to hold the views for each app.
   views::View* scrollable_view = new views::View();
diff --git a/chrome/browser/ui/views/login_view.cc b/chrome/browser/ui/views/login_view.cc
index 109a75b..c2a7dbd 100644
--- a/chrome/browser/ui/views/login_view.cc
+++ b/chrome/browser/ui/views/login_view.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/views/harmony/textfield_layout.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/layout/grid_layout.h"
@@ -48,9 +49,11 @@
   // to textfield_layout.h to decide.
   constexpr int kMessageWidth = 320;
   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
+  SetBorder(views::CreateEmptyBorder(
+      provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS)));
 
   // Initialize the Grid Layout Manager used for this dialog box.
-  GridLayout* layout = GridLayout::CreatePanel(this);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
   views::ColumnSet* column_set = layout->AddColumnSet(kHeaderColumnSetId);
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kStretchy,
                         GridLayout::FIXED, kMessageWidth, 0);
diff --git a/chrome/browser/ui/views/page_info/chosen_object_row.cc b/chrome/browser/ui/views/page_info/chosen_object_row.cc
index b4cbcb9a..3333d94 100644
--- a/chrome/browser/ui/views/page_info/chosen_object_row.cc
+++ b/chrome/browser/ui/views/page_info/chosen_object_row.cc
@@ -17,8 +17,7 @@
 ChosenObjectRow::ChosenObjectRow(
     std::unique_ptr<PageInfoUI::ChosenObjectInfo> info)
     : info_(std::move(info)) {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   const int column_set_id = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
   column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 0ab7f29..4908f52 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -137,8 +137,7 @@
                                       views::Link* link) {
   views::View* new_view = new views::View();
 
-  views::GridLayout* layout = new views::GridLayout(new_view);
-  new_view->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(new_view);
 
   const int column = 0;
   views::ColumnSet* column_set = layout->AddColumnSet(column);
@@ -260,8 +259,7 @@
       password_reuse_button_container_(nullptr),
       change_password_button_(nullptr),
       whitelist_password_reuse_button_(nullptr) {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   const int label_column_status = 1;
   AddColumnWithSideMargin(layout, side_margin, label_column_status);
@@ -515,8 +513,7 @@
   // below the dialog title.
   set_margins(gfx::Insets(0, 0, margins().bottom(), 0));
 
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   // Use a single ColumnSet here. Otherwise the preferred width doesn't properly
   // propagate up to the dialog width.
@@ -697,8 +694,8 @@
   }
 
   permissions_view_ = new views::View();
-  views::GridLayout* layout = new views::GridLayout(permissions_view_);
-  permissions_view_->SetLayoutManager(layout);
+  views::GridLayout* layout =
+      views::GridLayout::CreateAndInstall(permissions_view_);
 
   site_settings_view_->AddChildView(permissions_view_);
 
diff --git a/chrome/browser/ui/views/passwords/credentials_selection_view.cc b/chrome/browser/ui/views/passwords/credentials_selection_view.cc
index 5401203..8b43b953 100644
--- a/chrome/browser/ui/views/passwords/credentials_selection_view.cc
+++ b/chrome/browser/ui/views/passwords/credentials_selection_view.cc
@@ -38,7 +38,7 @@
   DCHECK(!password_forms_->empty());
 
   // Layout.
-  views::GridLayout* layout = new views::GridLayout(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   SetLayoutManager(layout);
 
   // ColumnSet.
diff --git a/chrome/browser/ui/views/passwords/manage_password_items_view.cc b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
index cb6285d1..92931eb 100644
--- a/chrome/browser/ui/views/passwords/manage_password_items_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
@@ -284,7 +284,7 @@
 void ManagePasswordItemsView::AddRows() {
   const int vertical_padding = ChromeLayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_RELATED_CONTROL_VERTICAL);
-  views::GridLayout* layout = new views::GridLayout(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   SetLayoutManager(layout);
   for (const std::unique_ptr<PasswordFormRow>& row : password_forms_rows_) {
     if (row != password_forms_rows_[0])
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
index d63a8f9..2ad4be4 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
@@ -330,9 +330,8 @@
 }
 
 void ManagePasswordsBubbleView::PendingView::CreateAndSetLayout() {
-  views::GridLayout* layout = new views::GridLayout(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
-  SetLayoutManager(layout);
 
   // Create the edit, save and never buttons.
   if (!edit_button_ &&
@@ -494,9 +493,8 @@
 ManagePasswordsBubbleView::ManageView::ManageView(
     ManagePasswordsBubbleView* parent)
     : parent_(parent) {
-  views::GridLayout* layout = new views::GridLayout(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
-  SetLayoutManager(layout);
   BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET);
   layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
 
@@ -587,9 +585,8 @@
 ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView(
     ManagePasswordsBubbleView* parent)
     : parent_(parent) {
-  views::GridLayout* layout = new views::GridLayout(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
-  SetLayoutManager(layout);
 
   views::StyledLabel* confirmation =
       new views::StyledLabel(parent_->model()->save_confirmation_text(), this);
@@ -661,9 +658,8 @@
 ManagePasswordsBubbleView::SignInPromoView::SignInPromoView(
     ManagePasswordsBubbleView* parent)
     : parent_(parent) {
-  views::GridLayout* layout = new views::GridLayout(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
-  SetLayoutManager(layout);
 
   signin_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
       this,
@@ -729,9 +725,8 @@
     ManagePasswordsBubbleView* parent)
     : parent_(parent), selection_view_(nullptr) {
   ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
-  views::GridLayout* layout = new views::GridLayout(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0));
-  SetLayoutManager(layout);
 
   // Credential row.
   if (parent->model()->ShouldShowMultipleAccountUpdateUI()) {
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
index ae740ce5..2ba676f 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
@@ -293,8 +293,8 @@
     view = std::move(exp_label);
   } else {
     // Two comboboxes, one for month and the other for year.
-    std::unique_ptr<views::GridLayout> combobox_layout =
-        base::MakeUnique<views::GridLayout>(view.get());
+    views::GridLayout* combobox_layout =
+        views::GridLayout::CreateAndInstall(view.get());
     views::ColumnSet* columns = combobox_layout->AddColumnSet(0);
     columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 1,
                        views::GridLayout::USE_PREF, 0, 0);
@@ -328,7 +328,6 @@
     combobox_layout->AddView(year_combobox.release(), 1, 1,
                              views::GridLayout::FILL, views::GridLayout::FILL,
                              0, kInputFieldHeight);
-    view->SetLayoutManager(combobox_layout.release());
   }
 
   // Set the initial validity of the custom view.
diff --git a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
index 27a11bc..c75d125 100644
--- a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
+++ b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
@@ -155,8 +155,7 @@
 }
 
 void CvcUnmaskViewController::FillContentView(views::View* content_view) {
-  std::unique_ptr<views::GridLayout> layout =
-      base::MakeUnique<views::GridLayout>(content_view);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(content_view);
   content_view->SetBorder(views::CreateEmptyBorder(
       kPaymentRequestRowVerticalInsets, kPaymentRequestRowHorizontalInsets,
       kPaymentRequestRowVerticalInsets, kPaymentRequestRowHorizontalInsets));
@@ -272,8 +271,6 @@
   error_label->SetVisible(false);
 
   layout->AddView(error_label.release());
-
-  content_view->SetLayoutManager(layout.release());
 }
 
 std::unique_ptr<views::Button> CvcUnmaskViewController::CreatePrimaryButton() {
diff --git a/chrome/browser/ui/views/payments/editor_view_controller.cc b/chrome/browser/ui/views/payments/editor_view_controller.cc
index 1c83e1d..cdb7c7d 100644
--- a/chrome/browser/ui/views/payments/editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/editor_view_controller.cc
@@ -251,8 +251,8 @@
   constexpr int kShortFieldMinimumWidth = 176;
   constexpr int kLongFieldMinimumWidth = 272;
 
-  std::unique_ptr<views::GridLayout> editor_layout =
-      base::MakeUnique<views::GridLayout>(editor_view.get());
+  views::GridLayout* editor_layout =
+      views::GridLayout::CreateAndInstall(editor_view.get());
   // Column set for short fields.
   views::ColumnSet* columns_short = editor_layout->AddColumnSet(0);
   columns_short->AddColumn(views::GridLayout::LEADING,
@@ -307,7 +307,7 @@
   for (const auto& field : GetFieldDefinitions()) {
     bool valid = false;
     views::View* focusable_field =
-        CreateInputField(editor_layout.get(), field, &valid);
+        CreateInputField(editor_layout, field, &valid);
     if (!first_field)
       first_field = focusable_field;
     if (!initial_focus_field_view_ && !valid)
@@ -331,8 +331,6 @@
           l10n_util::GetStringUTF16(IDS_PAYMENTS_REQUIRED_FIELD_MESSAGE))
           .release());
 
-  editor_view->SetLayoutManager(editor_layout.release());
-
   return editor_view;
 }
 
diff --git a/chrome/browser/ui/views/payments/order_summary_view_controller.cc b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
index 0c16806..334700a3 100644
--- a/chrome/browser/ui/views/payments/order_summary_view_controller.cc
+++ b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
@@ -57,8 +57,7 @@
           ui::NativeTheme::kColorId_SeparatorColor),
       row_insets));
 
-  views::GridLayout* layout = new views::GridLayout(row.get());
-  row->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(row.get());
 
   views::ColumnSet* columns = layout->AddColumnSet(0);
   // The first column has resize_percent = 1 so that it stretches all the way
@@ -96,7 +95,7 @@
 
   std::unique_ptr<views::View> amount_wrapper = base::MakeUnique<views::View>();
   views::GridLayout* wrapper_layout =
-      new views::GridLayout(amount_wrapper.get());
+      views::GridLayout::CreateAndInstall(amount_wrapper.get());
   views::ColumnSet* wrapper_columns = wrapper_layout->AddColumnSet(0);
   wrapper_columns->AddColumn(views::GridLayout::LEADING,
                              views::GridLayout::CENTER, 0,
@@ -109,7 +108,6 @@
   currency_text->set_id(static_cast<int>(currency_label_id));
   wrapper_layout->AddView(currency_text.release());
   wrapper_layout->AddView(amount_text.release());
-  amount_wrapper->SetLayoutManager(wrapper_layout);
 
   layout->AddView(label_text.release());
   layout->AddView(amount_wrapper.release());
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
index ca2e17c..a547962 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
@@ -344,8 +344,8 @@
   // would be under it.
   throbber_overlay_.SetBackground(views::CreateSolidBackground(SK_ColorWHITE));
 
-  std::unique_ptr<views::GridLayout> layout =
-      base::MakeUnique<views::GridLayout>(&throbber_overlay_);
+  views::GridLayout* layout =
+      views::GridLayout::CreateAndInstall(&throbber_overlay_);
   views::ColumnSet* throbber_columns = layout->AddColumnSet(0);
   throbber_columns->AddPaddingColumn(0.5, 0);
   throbber_columns->AddColumn(views::GridLayout::Alignment::CENTER,
@@ -367,7 +367,6 @@
   layout->AddView(new views::Label(
       l10n_util::GetStringUTF16(IDS_PAYMENTS_PROCESSING_MESSAGE)));
 
-  throbber_overlay_.SetLayoutManager(layout.release());
   AddChildView(&throbber_overlay_);
 }
 
diff --git a/chrome/browser/ui/views/payments/payment_request_item_list.cc b/chrome/browser/ui/views/payments/payment_request_item_list.cc
index d55367a..e476ce94 100644
--- a/chrome/browser/ui/views/payments/payment_request_item_list.cc
+++ b/chrome/browser/ui/views/payments/payment_request_item_list.cc
@@ -63,8 +63,7 @@
   std::unique_ptr<views::View> content =
       CreateContentView(&accessible_item_description_);
 
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   // Add a column for the item's content view.
   views::ColumnSet* columns = layout->AddColumnSet(0);
diff --git a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
index 07436a9..7c7b96f 100644
--- a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
@@ -218,8 +218,7 @@
   // layer) won't do proper clipping.
   view->SetPaintToLayer();
 
-  views::GridLayout* layout = new views::GridLayout(view.get());
-  view->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(view.get());
 
   // Note: each view is responsible for its own padding (insets).
   views::ColumnSet* columns = layout->AddColumnSet(0);
@@ -237,13 +236,12 @@
   // otherwise it'll be sized to the ScrollView's viewport height, preventing
   // the scroll bar from ever being shown.
   pane_ = new views::View;
-  views::GridLayout* pane_layout = new views::GridLayout(pane_);
+  views::GridLayout* pane_layout = views::GridLayout::CreateAndInstall(pane_);
   views::ColumnSet* pane_columns = pane_layout->AddColumnSet(0);
   pane_columns->AddColumn(views::GridLayout::Alignment::FILL,
                           views::GridLayout::Alignment::LEADING, 0,
                           views::GridLayout::SizeType::FIXED,
                           GetActualDialogWidth(), GetActualDialogWidth());
-  pane_->SetLayoutManager(pane_layout);
   pane_layout->StartRow(0, 0);
   // This is owned by its parent. It's the container passed to FillContentView.
   content_view_ = new views::View;
@@ -345,8 +343,8 @@
   container->SetBorder(
       views::CreateEmptyBorder(kInset, kInset, kInset, kInset));
 
-  views::GridLayout* layout = new views::GridLayout(container.get());
-  container->SetLayoutManager(layout);
+  views::GridLayout* layout =
+      views::GridLayout::CreateAndInstall(container.get());
 
   views::ColumnSet* columns = layout->AddColumnSet(0);
   columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.cc b/chrome/browser/ui/views/payments/payment_request_views_util.cc
index c79047e..de8452bf 100644
--- a/chrome/browser/ui/views/payments/payment_request_views_util.cc
+++ b/chrome/browser/ui/views/payments/payment_request_views_util.cc
@@ -184,8 +184,8 @@
     const base::string16& title,
     views::ButtonListener* listener) {
   std::unique_ptr<views::View> container = base::MakeUnique<views::View>();
-  views::GridLayout* layout = new views::GridLayout(container.get());
-  container->SetLayoutManager(layout);
+  views::GridLayout* layout =
+      views::GridLayout::CreateAndInstall(container.get());
 
   constexpr int kHeaderTopVerticalInset = 14;
   constexpr int kHeaderBottomVerticalInset = 8;
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
index 1c4e99f..e9262b9 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
@@ -150,7 +150,7 @@
       kPaymentRequestRowVerticalInsets, trailing_inset);
   std::unique_ptr<PaymentRequestRowView> row =
       base::MakeUnique<PaymentRequestRowView>(listener, clickable, row_insets);
-  views::GridLayout* layout = new views::GridLayout(row.get());
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(row.get());
   row->SetLayoutManager(layout);
 
   views::ColumnSet* columns = layout->AddColumnSet(0);
@@ -211,8 +211,8 @@
     bool bold) {
   std::unique_ptr<views::View> item_amount_line =
       base::MakeUnique<views::View>();
-  std::unique_ptr<views::GridLayout> item_amount_layout =
-      base::MakeUnique<views::GridLayout>(item_amount_line.get());
+  views::GridLayout* item_amount_layout =
+      views::GridLayout::CreateAndInstall(item_amount_line.get());
   views::ColumnSet* item_amount_columns = item_amount_layout->AddColumnSet(0);
   item_amount_columns->AddColumn(views::GridLayout::LEADING,
                                  views::GridLayout::LEADING, 0,
@@ -240,7 +240,6 @@
   item_amount_layout->AddView(currency_label.release());
   item_amount_layout->AddView(amount_label.release());
 
-  item_amount_line->SetLayoutManager(item_amount_layout.release());
   return item_amount_line;
 }
 
@@ -413,7 +412,7 @@
 }
 
 void PaymentSheetViewController::FillContentView(views::View* content_view) {
-  views::GridLayout* layout = new views::GridLayout(content_view);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(content_view);
   content_view->SetLayoutManager(layout);
   views::ColumnSet* columns = layout->AddColumnSet(0);
   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1,
@@ -563,8 +562,8 @@
 std::unique_ptr<PaymentRequestRowView>
 PaymentSheetViewController::CreatePaymentSheetSummaryRow() {
   std::unique_ptr<views::View> inline_summary = base::MakeUnique<views::View>();
-  std::unique_ptr<views::GridLayout> layout =
-      base::MakeUnique<views::GridLayout>(inline_summary.get());
+  views::GridLayout* layout =
+      views::GridLayout::CreateAndInstall(inline_summary.get());
   views::ColumnSet* columns = layout->AddColumnSet(0);
   columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 1,
                      views::GridLayout::USE_PREF, 0, 0);
@@ -636,8 +635,6 @@
           false, true)
           .release());
 
-  inline_summary->SetLayoutManager(layout.release());
-
   PaymentSheetRowBuilder builder(
       this, l10n_util::GetStringUTF16(IDS_PAYMENTS_ORDER_SUMMARY_LABEL));
   builder.Tag(PaymentSheetViewControllerTags::SHOW_ORDER_SUMMARY_BUTTON)
@@ -731,7 +728,8 @@
   if (selected_instrument) {
     std::unique_ptr<views::View> content_view = base::MakeUnique<views::View>();
 
-    views::GridLayout* layout = new views::GridLayout(content_view.get());
+    views::GridLayout* layout =
+        views::GridLayout::CreateAndInstall(content_view.get());
     content_view->SetLayoutManager(layout);
     views::ColumnSet* columns = layout->AddColumnSet(0);
     columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 1,
diff --git a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc
index 997d433..b15420e 100644
--- a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc
+++ b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc
@@ -208,7 +208,7 @@
       provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS);
   SetBorder(views::CreateEmptyBorder(dialog_insets.top(), 0,
                                      dialog_insets.bottom(), 0));
-  views::GridLayout* dialog_layout = new views::GridLayout(this);
+  views::GridLayout* dialog_layout = views::GridLayout::CreateAndInstall(this);
   SetLayoutManager(dialog_layout);
 
   // Use a column set with no padding.
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index 76f0f4b9..7920a6a2 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -124,8 +124,7 @@
 // Creates a GridLayout with a single column. This ensures that all the child
 // views added get auto-expanded to fill the full width of the bubble.
 views::GridLayout* CreateSingleColumnLayout(views::View* view, int width) {
-  views::GridLayout* layout = new views::GridLayout(view);
-  view->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(view);
 
   views::ColumnSet* columns = layout->AddColumnSet(0);
   columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
@@ -452,8 +451,8 @@
                                          TitleCard* title_card,
                                          int width) {
     views::View* titled_view = new views::View();
-    views::GridLayout* layout = new views::GridLayout(titled_view);
-    titled_view->SetLayoutManager(layout);
+    views::GridLayout* layout =
+        views::GridLayout::CreateAndInstall(titled_view);
 
     ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
     const gfx::Insets dialog_insets =
@@ -1114,8 +1113,8 @@
   // Container for the profile photo and avatar/user name.
   BackgroundColorHoverButton* current_profile_card =
       new BackgroundColorHoverButton(this, base::string16());
-  views::GridLayout* grid_layout = new views::GridLayout(current_profile_card);
-  current_profile_card->SetLayoutManager(grid_layout);
+  views::GridLayout* grid_layout =
+      views::GridLayout::CreateAndInstall(current_profile_card);
   views::ColumnSet* columns = grid_layout->AddColumnSet(0);
   // BackgroundColorHoverButton has already accounted for the left and right
   // margins.
diff --git a/chrome/browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc b/chrome/browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc
index 223f1a2..5fc2d8d 100644
--- a/chrome/browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc
+++ b/chrome/browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc
@@ -94,7 +94,7 @@
   // ----------------------------
   // | icon | padding | message |
   // ----------------------------
-  std::unique_ptr<views::GridLayout> layout(new views::GridLayout(this));
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   views::ColumnSet* columns = layout->AddColumnSet(0);
   columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0,
                      views::GridLayout::USE_PREF, 0, 0);
@@ -120,7 +120,6 @@
   layout->StartRow(0, 0);
   layout->AddView(warning_icon.release());
   layout->AddView(label.release());
-  SetLayoutManager(layout.release());
 }
 
 ProximityAuthErrorBubbleView::~ProximityAuthErrorBubbleView() {}
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc
index 93e78f2f..493763b6 100644
--- a/chrome/browser/ui/views/sad_tab_view.cc
+++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -55,8 +55,7 @@
   SetBackground(views::CreateThemedSolidBackground(
       this, ui::NativeTheme::kColorId_DialogBackground));
 
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   const int column_set_id = 0;
   views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
diff --git a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
index a85c3dca..21bfc08c 100644
--- a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
+++ b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
@@ -7,6 +7,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/harmony/chrome_typography.h"
 #include "components/constrained_window/constrained_window_views.h"
 #include "components/strings/grit/components_strings.h"
@@ -36,8 +37,11 @@
     : show_softer_warning_(
           PasswordProtectionService::ShouldShowSofterWarning()),
       done_callback_(std::move(done_callback)) {
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
+
   // TODO(jialiul): Dialog message should align with title.
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   views::ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                         views::GridLayout::FIXED, 400, 0);
diff --git a/chrome/browser/ui/views/session_crashed_bubble_view.cc b/chrome/browser/ui/views/session_crashed_bubble_view.cc
index 876d5ba..223d5c6 100644
--- a/chrome/browser/ui/views/session_crashed_bubble_view.cc
+++ b/chrome/browser/ui/views/session_crashed_bubble_view.cc
@@ -247,7 +247,7 @@
 
   // Create a view to hold the checkbox and the text.
   views::View* uma_view = new views::View();
-  GridLayout* uma_layout = new GridLayout(uma_view);
+  GridLayout* uma_layout = GridLayout::CreateAndInstall(uma_view);
   uma_view->SetLayoutManager(uma_layout);
 
   const int kReportColumnSetId = 0;
diff --git a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
index 3caf029f..1f22a9e 100644
--- a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
+++ b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -74,6 +75,8 @@
       advanced_link_(nullptr),
       learn_more_link_(nullptr) {
   DCHECK(!start_sync_callback_.is_null());
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
   chrome::RecordDialogCreation(chrome::DialogIdentifier::ONE_CLICK_SIGNIN);
 }
 
@@ -85,7 +88,7 @@
 }
 
 void OneClickSigninDialogView::Init() {
-  views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
   // Column set for descriptive text and link.
   views::ColumnSet* cs = layout->AddColumnSet(0);
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
index cab51a4..0d3d229d 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/constrained_window/constrained_window_views.h"
@@ -205,11 +206,14 @@
   // insets.
   SetBorder(views::CreateEmptyBorder(content_insets.top(), 0,
                                      content_insets.bottom(), 0));
-  views::GridLayout* dialog_layout = new views::GridLayout(this);
-  SetLayoutManager(dialog_layout);
+  views::GridLayout* dialog_layout = views::GridLayout::CreateAndInstall(this);
 
   // Use GridLayout inside the prompt bar because StyledLabel requires it.
-  views::GridLayout* prompt_layout = views::GridLayout::CreatePanel(prompt_bar);
+  views::GridLayout* prompt_layout =
+      views::GridLayout::CreateAndInstall(prompt_bar);
+  prompt_bar->SetBorder(
+      views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric(
+          views::INSETS_DIALOG_CONTENTS)));
   constexpr int kPromptBarColumnSetId = 0;
   prompt_layout->AddColumnSet(kPromptBarColumnSetId)
       ->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 100,
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index e1a0a5f81..1b338b9 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -544,8 +544,7 @@
   }
 
   views::View* view = new views::View();
-  views::GridLayout* layout = new views::GridLayout(view);
-  view->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(view);
 
   using views::GridLayout;
 
@@ -673,8 +672,7 @@
       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATING));
 
   views::View* view = new views::View();
-  views::GridLayout* layout = new views::GridLayout(view);
-  view->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(view);
 
   using views::GridLayout;
 
@@ -725,8 +723,7 @@
       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATED));
 
   views::View* view = new views::View();
-  views::GridLayout* layout = new views::GridLayout(view);
-  view->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(view);
 
   using views::GridLayout;
 
@@ -776,8 +773,7 @@
       l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE));
 
   views::View* view = new views::View();
-  views::GridLayout* layout = new views::GridLayout(view);
-  view->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(view);
 
   using views::GridLayout;
 
@@ -857,8 +853,7 @@
   }
 
   views::View* view = new views::View();
-  views::GridLayout* layout = new views::GridLayout(view);
-  view->SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(view);
 
   using views::GridLayout;
 
diff --git a/chrome/browser/ui/views/try_chrome_dialog.cc b/chrome/browser/ui/views/try_chrome_dialog.cc
index b4687b02..7b4ce00 100644
--- a/chrome/browser/ui/views/try_chrome_dialog.cc
+++ b/chrome/browser/ui/views/try_chrome_dialog.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "base/strings/string16.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/harmony/chrome_typography.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -156,7 +157,10 @@
 
   views::View* root_view = popup_->GetRootView();
   root_view->SetBackground(views::CreateSolidBackground(kBackgroundColor));
-  views::GridLayout* layout = views::GridLayout::CreatePanel(root_view);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(root_view);
+  root_view->SetBorder(
+      views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric(
+          views::INSETS_DIALOG_CONTENTS)));
   layout->set_minimum_size(gfx::Size(kToastWidth, 0));
   views::ColumnSet* columns;
 
diff --git a/chrome/browser/ui/views/uninstall_view.cc b/chrome/browser/ui/views/uninstall_view.cc
index 5569240bb..ad6b948 100644
--- a/chrome/browser/ui/views/uninstall_view.cc
+++ b/chrome/browser/ui/views/uninstall_view.cc
@@ -30,6 +30,8 @@
       browsers_combo_(NULL),
       user_selection_(*user_selection),
       quit_closure_(quit_closure) {
+  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
+      views::INSETS_DIALOG_CONTENTS));
   SetupControls();
 }
 
@@ -45,7 +47,7 @@
   using views::ColumnSet;
   using views::GridLayout;
 
-  GridLayout* layout = GridLayout::CreatePanel(this);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
 
   // Message to confirm uninstallation.
   int column_set_id = 0;
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index 2d2b383..f66590d 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -848,6 +848,13 @@
   localized_strings->SetString("browseAsGuestAllProfilesLockedError",
       l10n_util::GetStringUTF16(
           IDS_USER_MANAGER_GO_GUEST_PROFILES_LOCKED_ERROR));
+
+  base::string16 prompt_message;
+  if (signin_util::IsForceSigninEnabled()) {
+    prompt_message = l10n_util::GetStringUTF16(IDS_USER_MANAGER_PROMPT_MESSAGE);
+  }
+
+  localized_strings->SetString("userManagerPromptMessage", prompt_message);
 }
 
 void UserManagerScreenHandler::SendUserList() {
diff --git a/chrome/browser/vr/ui_scene_manager.cc b/chrome/browser/vr/ui_scene_manager.cc
index 784b907..ebdfa632 100644
--- a/chrome/browser/vr/ui_scene_manager.cc
+++ b/chrome/browser/vr/ui_scene_manager.cc
@@ -758,8 +758,13 @@
   // TODO(vollick): it would be nice if ceiling, floor and the grid were
   // UiElement subclasses and could respond to the OnSetMode signal.
   for (Rect* panel : background_panels_) {
-    panel->SetCenterColor(color_scheme().world_background);
-    panel->SetEdgeColor(color_scheme().world_background);
+    if (showing_web_vr_splash_screen_) {
+      panel->SetCenterColor(color_scheme().splash_screen_background);
+      panel->SetEdgeColor(color_scheme().splash_screen_background);
+    } else {
+      panel->SetCenterColor(color_scheme().world_background);
+      panel->SetEdgeColor(color_scheme().world_background);
+    }
   }
   ceiling_->SetCenterColor(color_scheme().ceiling);
   ceiling_->SetEdgeColor(color_scheme().world_background);
diff --git a/chrome/browser/vr/ui_scene_manager_unittest.cc b/chrome/browser/vr/ui_scene_manager_unittest.cc
index 9169d135..05c9bd9f 100644
--- a/chrome/browser/vr/ui_scene_manager_unittest.cc
+++ b/chrome/browser/vr/ui_scene_manager_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/test/scoped_mock_time_message_loop_task_runner.h"
 #include "cc/base/math_util.h"
+#include "chrome/browser/vr/color_scheme.h"
 #include "chrome/browser/vr/elements/ui_element.h"
 #include "chrome/browser/vr/elements/ui_element_debug_id.h"
 #include "chrome/browser/vr/target_property.h"
@@ -238,8 +239,8 @@
   // WebVR frame is not received.
   auto initial_elements = kBackgroundElements;
   initial_elements.insert(kSplashScreenText);
-
   VerifyElementsVisible("Initial", initial_elements);
+
   manager_->OnWebVrFrameAvailable();
   VerifyElementsVisible(
       "Autopresented", std::set<UiElementDebugId>{
@@ -267,6 +268,9 @@
   auto initial_elements = kBackgroundElements;
   initial_elements.insert(kSplashScreenText);
   VerifyElementsVisible("Initial", initial_elements);
+  EXPECT_EQ(ColorScheme::GetColorScheme(ColorScheme::kModeNormal)
+                .splash_screen_background,
+            GetBackgroundColor());
 
   // Enter WebVR with autopresentation.
   manager_->SetWebVrMode(true, false);
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc
index dab44ad..1b33498 100644
--- a/chrome/common/crash_keys.cc
+++ b/chrome/common/crash_keys.cc
@@ -208,7 +208,6 @@
 
     // TODO(asvitkine): Remove after fixing https://crbug.com/736675
     {"bad_histogram", kMediumSize},
-    {"from_location", kMediumSize},
   };
 
   // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/profiling/json_exporter_unittest.cc b/chrome/profiling/json_exporter_unittest.cc
index 48d1cf2..111266a4 100644
--- a/chrome/profiling/json_exporter_unittest.cc
+++ b/chrome/profiling/json_exporter_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/json/json_writer.h"
 #include "base/process/process.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/profiling/backtrace_storage.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -93,7 +94,8 @@
 
 }  // namespace
 
-TEST(ProfilingJsonExporterTest, Simple) {
+// Test failing. crbug.com/759176
+TEST(ProfilingJsonExporterTest, DISABLED_Simple) {
   BacktraceStorage backtrace_storage;
 
   std::vector<Address> stack1;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index e91cbfed..d73588ba 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2032,7 +2032,7 @@
         "../browser/chromeos/input_method/textinput_test_helper.cc",
         "../browser/chromeos/input_method/textinput_test_helper.h",
         "../browser/ui/ash/accelerator_commands_browsertest.cc",
-        "../browser/ui/ash/app_list/app_list_controller_ash_browsertest.cc",
+        "../browser/ui/ash/app_list/app_list_browsertest.cc",
         "../browser/ui/ash/chrome_new_window_client_browsertest.cc",
         "../browser/ui/ash/chrome_screenshot_grabber_browsertest.cc",
         "../browser/ui/ash/keyboard_controller_browsertest.cc",
@@ -4717,6 +4717,7 @@
         "../browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm",
         "../browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm",
         "../browser/ui/cocoa/nsmenuitem_additions_unittest.mm",
+        "../browser/ui/cocoa/nsview_additions_unittest.mm",
         "../browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm",
         "../browser/ui/cocoa/omnibox/omnibox_popup_matrix_unittest.mm",
         "../browser/ui/cocoa/omnibox/omnibox_popup_separator_view_unittest.mm",
diff --git a/chromecast/crash/cast_crash_keys.cc b/chromecast/crash/cast_crash_keys.cc
index b01dc265c..3223f442 100644
--- a/chromecast/crash/cast_crash_keys.cc
+++ b/chromecast/crash/cast_crash_keys.cc
@@ -73,10 +73,6 @@
 
       // Temporary for https://crbug.com/685996.
       {"user-cloud-policy-manager-connect-trace", ::crash_keys::kMediumSize},
-
-      // TODO(bcwhite): Remove after fixing https://crbug.com/736675
-      {"bad_histogram", ::crash_keys::kMediumSize},
-      {"from_location", ::crash_keys::kMediumSize},
   };
 
   return base::debug::InitCrashKeys(fixed_keys, arraysize(fixed_keys),
diff --git a/chromeos/disks/disk_mount_manager_unittest.cc b/chromeos/disks/disk_mount_manager_unittest.cc
index e60702d..baba418 100644
--- a/chromeos/disks/disk_mount_manager_unittest.cc
+++ b/chromeos/disks/disk_mount_manager_unittest.cc
@@ -175,7 +175,8 @@
   DEVICE_EVENT,  // OnDeviceEvent()
   DISK_EVENT,    // OnDiskEvent()
   FORMAT_EVENT,  // OnFormatEvent()
-  MOUNT_EVENT    // OnMountEvent()
+  MOUNT_EVENT,   // OnMountEvent()
+  RENAME_EVENT   // OnRenameEvent()
 };
 
 // Represents every event notified to |DiskMountManager::Observer|.
@@ -259,6 +260,30 @@
   }
 };
 
+// Represents an invocation of |DiskMountManager::Observer::OnRenameEvent()|.
+struct RenameEvent : public ObserverEvent {
+  DiskMountManager::RenameEvent event;
+  chromeos::RenameError error_code;
+  std::string device_path;
+
+  RenameEvent(DiskMountManager::RenameEvent event,
+              chromeos::RenameError error_code,
+              const std::string& device_path)
+      : event(event), error_code(error_code), device_path(device_path) {}
+
+  ObserverEventType type() const override { return RENAME_EVENT; }
+
+  bool operator==(const RenameEvent& other) const {
+    return event == other.event && error_code == other.error_code &&
+           device_path == other.device_path;
+  }
+
+  std::string DebugString() const {
+    return StringPrintf("OnRenameEvent(%d, %d, %s)", event, error_code,
+                        device_path.c_str());
+  }
+};
+
 // Represents an invocation of |DiskMountManager::Observer::OnMountEvent()|.
 struct MountEvent : public ObserverEvent {
   DiskMountManager::MountEvent event;
@@ -325,8 +350,7 @@
   void OnRenameEvent(DiskMountManager::RenameEvent event,
                      chromeos::RenameError error_code,
                      const std::string& device_path) override {
-    // TODO(klemenko): Add implementation when you will add unit tests for
-    // rename
+    events_.push_back(MakeUnique<RenameEvent>(event, error_code, device_path));
   }
 
   void OnMountEvent(
@@ -364,6 +388,14 @@
     return static_cast<const FormatEvent&>(*events_[index]);
   }
 
+  // Verifies if the |index|th invocation is OnRenameEvent() and returns
+  // details.
+  const RenameEvent& GetRenameEvent(size_t index) {
+    DCHECK_GT(events_.size(), index);
+    DCHECK_EQ(RENAME_EVENT, events_[index]->type());
+    return static_cast<const RenameEvent&>(*events_[index]);
+  }
+
   // Verifies if the |index|th invocation is OnMountEvent() and returns details.
   const MountEvent& GetMountEvent(size_t index) {
     DCHECK_GT(events_.size(), index);
@@ -405,6 +437,19 @@
     return num_matched;
   }
 
+  // Counts the number of |RenameEvent| recorded so far that matches with
+  // |rename_event|.
+  size_t CountRenameEvents(const RenameEvent& exptected_rename_event) {
+    size_t num_matched = 0;
+    for (const auto& event : events_) {
+      if (event->type() != RENAME_EVENT)
+        continue;
+      if (static_cast<const RenameEvent&>(*event) == exptected_rename_event)
+        num_matched++;
+    }
+    return num_matched;
+  }
+
  private:
   // Pointer to the manager object to which this |Observer| is registered.
   const DiskMountManager* manager_;
@@ -964,4 +1009,351 @@
   EXPECT_FALSE(observer_->GetMountEvent(1).disk->is_read_only());
 }
 
+// Tests that the observer gets notified on attempt to rename non existent mount
+// point.
+TEST_F(DiskMountManagerTest, Rename_NotMounted) {
+  DiskMountManager::GetInstance()->RenameMountedDevice("/mount/non_existent",
+                                                       "MYUSB");
+  ASSERT_EQ(1U, observer_->GetEventCount());
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED,
+                        chromeos::RENAME_ERROR_UNKNOWN, "/mount/non_existent"),
+            observer_->GetRenameEvent(0));
+}
+
+// Tests that the observer gets notified on attempt to rename read-only mount
+// point.
+TEST_F(DiskMountManagerTest, Rename_ReadOnly) {
+  DiskMountManager::GetInstance()->RenameMountedDevice(kReadOnlyDeviceMountPath,
+                                                       "MYUSB");
+  ASSERT_EQ(1U, observer_->GetEventCount());
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED,
+                        chromeos::RENAME_ERROR_DEVICE_NOT_ALLOWED,
+                        kReadOnlyDeviceMountPath),
+            observer_->GetRenameEvent(0));
+}
+
+// Tests that it is not possible to rename archive mount point.
+TEST_F(DiskMountManagerTest, Rename_Archive) {
+  DiskMountManager::GetInstance()->RenameMountedDevice("/archive/mount_path",
+                                                       "MYUSB");
+  ASSERT_EQ(1U, observer_->GetEventCount());
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED,
+                        chromeos::RENAME_ERROR_UNKNOWN, "/archive/source_path"),
+            observer_->GetRenameEvent(0));
+}
+
+// Tests that rename fails if the device cannot be unmounted.
+TEST_F(DiskMountManagerTest, Rename_FailToUnmount) {
+  // Before renaming mounted device, the device should be unmounted.
+  // In this test unmount will fail, and there should be no attempt to
+  // rename the device.
+
+  fake_cros_disks_client_->MakeUnmountFail();
+  // Start test.
+  DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath,
+                                                       "MYUSB");
+
+  // Cros disks will respond asynchronoulsy, so let's drain the message loop.
+  base::RunLoop().RunUntilIdle();
+
+  // Observer should be notified that unmount attempt fails and rename task
+  // failed to start.
+  ASSERT_EQ(2U, observer_->GetEventCount());
+  const MountEvent& mount_event = observer_->GetMountEvent(0);
+  EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event);
+  EXPECT_EQ(chromeos::MOUNT_ERROR_INTERNAL, mount_event.error_code);
+  EXPECT_EQ(kDevice1MountPath, mount_event.mount_point.mount_path);
+
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED,
+                        chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath),
+            observer_->GetRenameEvent(1));
+  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
+  EXPECT_EQ(kDevice1MountPath,
+            fake_cros_disks_client_->last_unmount_device_path());
+  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
+            fake_cros_disks_client_->last_unmount_options());
+  EXPECT_EQ(0, fake_cros_disks_client_->rename_call_count());
+
+  // The device mount should still be here.
+  EXPECT_TRUE(HasMountPoint(kDevice1MountPath));
+}
+
+// Tests that observer is notified when cros disks fails to start rename
+// process.
+TEST_F(DiskMountManagerTest, Rename_RenameFailsToStart) {
+  // Before renaming mounted device, the device should be unmounted.
+  // In this test, unmount will succeed, but call to Rename method will
+  // fail.
+
+  fake_cros_disks_client_->MakeRenameFail();
+  // Start the test.
+  DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath,
+                                                       "MYUSB");
+
+  // Cros disks will respond asynchronoulsy, so let's drain the message loop.
+  base::RunLoop().RunUntilIdle();
+
+  // Observer should be notified that the device was unmounted and rename task
+  // failed to start.
+  ASSERT_EQ(2U, observer_->GetEventCount());
+  const MountEvent& mount_event = observer_->GetMountEvent(0);
+  EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event);
+  EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, mount_event.error_code);
+  EXPECT_EQ(kDevice1MountPath, mount_event.mount_point.mount_path);
+
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED,
+                        chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath),
+            observer_->GetRenameEvent(1));
+
+  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
+  EXPECT_EQ(kDevice1MountPath,
+            fake_cros_disks_client_->last_unmount_device_path());
+  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
+            fake_cros_disks_client_->last_unmount_options());
+  EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count());
+  EXPECT_EQ(kDevice1SourcePath,
+            fake_cros_disks_client_->last_rename_device_path());
+  EXPECT_EQ("MYUSB", fake_cros_disks_client_->last_rename_volume_name());
+
+  // The device mount should be gone.
+  EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
+}
+
+// Tests the case where there are two rename requests for the same device.
+TEST_F(DiskMountManagerTest, Rename_ConcurrentRenameCalls) {
+  // Only the first rename request should be processed (the second unmount
+  // request fails because the device is already unmounted at that point).
+  // CrosDisksClient will report that the rename process for the first request
+  // is successfully started.
+
+  fake_cros_disks_client_->set_unmount_listener(
+      base::Bind(&FakeCrosDisksClient::MakeUnmountFail,
+                 base::Unretained(fake_cros_disks_client_)));
+  // Start the test.
+  DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath,
+                                                       "MYUSB1");
+  DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath,
+                                                       "MYUSB2");
+
+  // Cros disks will respond asynchronoulsy, so let's drain the message loop.
+  base::RunLoop().RunUntilIdle();
+
+  // The observer should get a RENAME_STARTED event for one rename request and a
+  // RENAME_COMPLETED with an error code for the other rename request. The
+  // renaming will be started only for the first request.
+  // There should be only one UNMOUNTING event. The result of the second one
+  // should not be reported as the mount point will go away after the first
+  // request.
+  //
+  // Note that in this test the rename completion signal will not be simulated,
+  // so the observer should not get RENAME_COMPLETED signal.
+
+  ASSERT_EQ(3U, observer_->GetEventCount());
+  const MountEvent& mount_event = observer_->GetMountEvent(0);
+  EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event);
+  EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, mount_event.error_code);
+  EXPECT_EQ(kDevice1MountPath, mount_event.mount_point.mount_path);
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED,
+                        chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath),
+            observer_->GetRenameEvent(1));
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_STARTED,
+                        chromeos::RENAME_ERROR_NONE, kDevice1SourcePath),
+            observer_->GetRenameEvent(2));
+
+  EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count());
+  EXPECT_EQ(kDevice1MountPath,
+            fake_cros_disks_client_->last_unmount_device_path());
+  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
+            fake_cros_disks_client_->last_unmount_options());
+  EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count());
+  EXPECT_EQ(kDevice1SourcePath,
+            fake_cros_disks_client_->last_rename_device_path());
+  EXPECT_EQ("MYUSB1", fake_cros_disks_client_->last_rename_volume_name());
+
+  // The device mount should be gone.
+  EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
+}
+
+// Tests the case when the rename process actually starts and fails.
+TEST_F(DiskMountManagerTest, Rename_RenameFails) {
+  // Both unmount and rename device calls are successful in this test.
+
+  // Start the test.
+  DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath,
+                                                       "MYUSB");
+
+  // Wait for Unmount and Rename calls to end.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
+  EXPECT_EQ(kDevice1MountPath,
+            fake_cros_disks_client_->last_unmount_device_path());
+  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
+            fake_cros_disks_client_->last_unmount_options());
+  EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count());
+  EXPECT_EQ(kDevice1SourcePath,
+            fake_cros_disks_client_->last_rename_device_path());
+  EXPECT_EQ("MYUSB", fake_cros_disks_client_->last_rename_volume_name());
+
+  // The device should be unmounted by now.
+  EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
+
+  // Send failing RENAME_COMPLETED signal.
+  // The failure is marked by ! in fromt of the path (but this should change
+  // soon).
+  fake_cros_disks_client_->SendRenameCompletedEvent(
+      chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath);
+
+  // The observer should get notified that the device was unmounted and that
+  // renaming has started.
+  // After the renaming starts, the test will simulate failing
+  // RENAME_COMPLETED signal, so the observer should also be notified the
+  // renaming has failed (RENAME_COMPLETED event).
+  ASSERT_EQ(3U, observer_->GetEventCount());
+  VerifyMountEvent(observer_->GetMountEvent(0), DiskMountManager::UNMOUNTING,
+                   chromeos::MOUNT_ERROR_NONE, kDevice1MountPath);
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_STARTED,
+                        chromeos::RENAME_ERROR_NONE, kDevice1SourcePath),
+            observer_->GetRenameEvent(1));
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED,
+                        chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath),
+            observer_->GetRenameEvent(2));
+}
+
+// Tests the case when renaming completes successfully.
+TEST_F(DiskMountManagerTest, Rename_RenameSuccess) {
+  DiskMountManager* manager = DiskMountManager::GetInstance();
+  const DiskMountManager::DiskMap& disks = manager->disks();
+  // Set up cros disks client mocks.
+  // Both unmount and rename device calls are successful in this test.
+
+  // Start the test.
+  DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath,
+                                                       "MYUSB1");
+
+  // Wait for Unmount and Rename calls to end.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
+  EXPECT_EQ(kDevice1MountPath,
+            fake_cros_disks_client_->last_unmount_device_path());
+  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
+            fake_cros_disks_client_->last_unmount_options());
+  EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count());
+  EXPECT_EQ(kDevice1SourcePath,
+            fake_cros_disks_client_->last_rename_device_path());
+  EXPECT_EQ("MYUSB1", fake_cros_disks_client_->last_rename_volume_name());
+
+  // The device should be unmounted by now.
+  EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
+
+  // Simulate cros_disks reporting success.
+  fake_cros_disks_client_->SendRenameCompletedEvent(chromeos::RENAME_ERROR_NONE,
+                                                    kDevice1SourcePath);
+
+  // The observer should receive UNMOUNTING, RENAME_STARTED and RENAME_COMPLETED
+  // events (all of them without an error set).
+  ASSERT_EQ(3U, observer_->GetEventCount());
+  VerifyMountEvent(observer_->GetMountEvent(0), DiskMountManager::UNMOUNTING,
+                   chromeos::MOUNT_ERROR_NONE, kDevice1MountPath);
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_STARTED,
+                        chromeos::RENAME_ERROR_NONE, kDevice1SourcePath),
+            observer_->GetRenameEvent(1));
+  EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED,
+                        chromeos::RENAME_ERROR_NONE, kDevice1SourcePath),
+            observer_->GetRenameEvent(2));
+
+  // Disk should have new value for device label name
+  EXPECT_EQ("MYUSB1", disks.find(kDevice1SourcePath)->second->device_label());
+}
+
+// Tests that it's possible to rename the device twice in a row (this may not be
+// true if the list of pending renames is not properly cleared).
+TEST_F(DiskMountManagerTest, Rename_ConsecutiveRenameCalls) {
+  DiskMountManager* manager = DiskMountManager::GetInstance();
+  const DiskMountManager::DiskMap& disks = manager->disks();
+  // All unmount and rename device calls are successful in this test.
+  // Each of the should be made twice (once for each renaming task).
+
+  // Start the test.
+  DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath,
+                                                       "MYUSB");
+
+  // Wait for Unmount and Rename calls to end.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count());
+  EXPECT_EQ(kDevice1MountPath,
+            fake_cros_disks_client_->last_unmount_device_path());
+  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
+            fake_cros_disks_client_->last_unmount_options());
+  EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count());
+  EXPECT_EQ(kDevice1SourcePath,
+            fake_cros_disks_client_->last_rename_device_path());
+  EXPECT_EQ("MYUSB", fake_cros_disks_client_->last_rename_volume_name());
+  EXPECT_EQ("", disks.find(kDevice1SourcePath)->second->base_mount_path());
+
+  // The device should be unmounted by now.
+  EXPECT_FALSE(HasMountPoint(kDevice1MountPath));
+
+  // Simulate cros_disks reporting success.
+  fake_cros_disks_client_->SendRenameCompletedEvent(chromeos::RENAME_ERROR_NONE,
+                                                    kDevice1SourcePath);
+
+  // Simulate the device remounting.
+  fake_cros_disks_client_->SendMountCompletedEvent(
+      chromeos::MOUNT_ERROR_NONE, kDevice1SourcePath,
+      chromeos::MOUNT_TYPE_DEVICE, kDevice1MountPath);
+
+  EXPECT_TRUE(HasMountPoint(kDevice1MountPath));
+
+  auto previousMountPath = disks.find(kDevice1SourcePath)->second->mount_path();
+  // Try renaming again.
+  DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath,
+                                                       "MYUSB2");
+
+  // Wait for Unmount and Rename calls to end.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count());
+  EXPECT_EQ(kDevice1MountPath,
+            fake_cros_disks_client_->last_unmount_device_path());
+  EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE,
+            fake_cros_disks_client_->last_unmount_options());
+  EXPECT_EQ(2, fake_cros_disks_client_->rename_call_count());
+  EXPECT_EQ(kDevice1SourcePath,
+            fake_cros_disks_client_->last_rename_device_path());
+  EXPECT_EQ("MYUSB2", fake_cros_disks_client_->last_rename_volume_name());
+  // Base mount path should be set to previous mount path.
+  EXPECT_EQ(previousMountPath,
+            disks.find(kDevice1SourcePath)->second->base_mount_path());
+
+  // Simulate cros_disks reporting success.
+  fake_cros_disks_client_->SendRenameCompletedEvent(chromeos::RENAME_ERROR_NONE,
+                                                    kDevice1SourcePath);
+
+  // The observer should receive UNMOUNTING, RENAME_STARTED and RENAME_COMPLETED
+  // events (all of them without an error set) twice (once for each renaming
+  // task).
+  // Also, there should be a MOUNTING event when the device remounting is
+  // simulated.
+  EXPECT_EQ(7U, observer_->GetEventCount());
+
+  EXPECT_EQ(2U, observer_->CountRenameEvents(RenameEvent(
+                    DiskMountManager::RENAME_COMPLETED,
+                    chromeos::RENAME_ERROR_NONE, kDevice1SourcePath)));
+
+  EXPECT_EQ(2U, observer_->CountRenameEvents(RenameEvent(
+                    DiskMountManager::RENAME_STARTED,
+                    chromeos::RENAME_ERROR_NONE, kDevice1SourcePath)));
+
+  EXPECT_EQ(2U, observer_->CountMountEvents(DiskMountManager::UNMOUNTING,
+                                            chromeos::MOUNT_ERROR_NONE,
+                                            kDevice1MountPath));
+
+  EXPECT_EQ(1U, observer_->CountMountEvents(DiskMountManager::MOUNTING,
+                                            chromeos::MOUNT_ERROR_NONE,
+                                            kDevice1MountPath));
+}
+
 }  // namespace
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 2634e8b..5708943 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -7,8 +7,6 @@
 
 static_library("arc") {
   sources = [
-    "arc_util.cc",
-    "arc_util.h",
     "audio/arc_audio_bridge.cc",
     "audio/arc_audio_bridge.h",
     "bluetooth/arc_bluetooth_bridge.cc",
@@ -113,6 +111,8 @@
     "arc_session_runner.h",
     "arc_stop_reason.cc",
     "arc_stop_reason.h",
+    "arc_util.cc",
+    "arc_util.h",
     "instance_holder.h",
   ]
 
@@ -123,6 +123,7 @@
     "//components/signin/core/account_id",
     "//components/user_manager",
     "//mojo/edk/system",
+    "//ui/aura",
   ]
 
   public_deps = [
diff --git a/components/arc/arc_session_runner.cc b/components/arc/arc_session_runner.cc
index 5401b22..ecdeff29 100644
--- a/components/arc/arc_session_runner.cc
+++ b/components/arc/arc_session_runner.cc
@@ -8,6 +8,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/task_runner.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "components/arc/arc_util.h"
 
 namespace arc {
 
@@ -236,6 +237,11 @@
 }
 
 void ArcSessionRunner::EmitLoginPromptVisibleCalled() {
+  if (ShouldArcOnlyStartAfterLogin()) {
+    // Skip starting ARC for now. We'll have another chance to start the full
+    // instance after the user logs in.
+    return;
+  }
   // Since 'login-prompt-visible' Upstart signal starts all Upstart jobs the
   // container may depend on such as cras, EmitLoginPromptVisibleCalled() is the
   // safe place to start the container for login screen.
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index 0ebc98c..83436fe 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -33,6 +33,7 @@
 constexpr char kAlwaysStart[] = "always-start";
 constexpr char kAlwaysStartWithNoPlayStore[] =
     "always-start-with-no-play-store";
+constexpr char kOnlyStartAfterLogin[] = "only-start-after-login";
 
 void SetArcCpuRestrictionCallback(
     login_manager::ContainerCpuRestrictionState state,
@@ -106,6 +107,15 @@
       play_store_available ? kAlwaysStart : kAlwaysStartWithNoPlayStore);
 }
 
+bool ShouldArcOnlyStartAfterLogin() {
+  const auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
+    return false;
+  const std::string value =
+      command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode);
+  return value == kOnlyStartAfterLogin;
+}
+
 bool IsArcKioskAvailable() {
   const auto* command_line = base::CommandLine::ForCurrentProcess();
 
diff --git a/components/arc/arc_util.h b/components/arc/arc_util.h
index 3214545..7558ac3 100644
--- a/components/arc/arc_util.h
+++ b/components/arc/arc_util.h
@@ -51,6 +51,9 @@
 // Store UI is added.
 void SetArcAlwaysStartForTesting(bool play_store_available);
 
+// Returns true if ARC should only start after the user has logged in.
+bool ShouldArcOnlyStartAfterLogin();
+
 // Returns true if ARC is installed and running ARC kiosk apps on the current
 // device is officially supported.
 // It doesn't follow that ARC is available for user sessions and
diff --git a/components/cronet/android/test/javatests/AndroidManifest.xml b/components/cronet/android/test/javatests/AndroidManifest.xml
index e5e166655..8da4a5b7 100644
--- a/components/cronet/android/test/javatests/AndroidManifest.xml
+++ b/components/cronet/android/test/javatests/AndroidManifest.xml
@@ -22,5 +22,10 @@
     <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner"
         android:targetPackage="org.chromium.net"
         android:label="Tests for org.chromium.net"
-        chromium-junit4="true" />
+        chromium-junit4="true">
+        <!-- Meta data used for BaseChromiumAndroidJUnitRunner to scan only
+             one package path for potential tests -->
+        <meta-data android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner.TestListPackage"
+            android:value="org.chromium" />
+    </instrumentation>
 </manifest>
diff --git a/components/metrics/metrics_log.cc b/components/metrics/metrics_log.cc
index 412179394..02ac223e 100644
--- a/components/metrics/metrics_log.cc
+++ b/components/metrics/metrics_log.cc
@@ -175,7 +175,7 @@
   os->set_name(base::SysInfo::OperatingSystemName());
   os->set_version(base::SysInfo::OperatingSystemVersion());
 #if defined(OS_ANDROID)
-  os->set_fingerprint(
+  os->set_build_fingerprint(
       base::android::BuildInfo::GetInstance()->android_build_fp());
 #endif
 }
diff --git a/components/metrics/metrics_log_unittest.cc b/components/metrics/metrics_log_unittest.cc
index ca009f5..e3eb422 100644
--- a/components/metrics/metrics_log_unittest.cc
+++ b/components/metrics/metrics_log_unittest.cc
@@ -154,7 +154,7 @@
   system_profile->mutable_os()->set_version(
       base::SysInfo::OperatingSystemVersion());
 #if defined(OS_ANDROID)
-  system_profile->mutable_os()->set_fingerprint(
+  system_profile->mutable_os()->set_build_fingerprint(
       base::android::BuildInfo::GetInstance()->android_build_fp());
 #endif
 
diff --git a/components/metrics/proto/system_profile.proto b/components/metrics/proto/system_profile.proto
index 5d1dd73..96203965 100644
--- a/components/metrics/proto/system_profile.proto
+++ b/components/metrics/proto/system_profile.proto
@@ -89,7 +89,7 @@
     optional string version = 2;
 
     // The fingerprint of the build.  This field is used only on Android.
-    optional string fingerprint = 3;
+    optional string build_fingerprint = 3;
 
     // Whether the version of iOS appears to be "jailbroken". This field is
     // used only on iOS. Chrome for iOS detects whether device contains a
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index bded6831..18469953 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -1300,7 +1300,7 @@
   const std::vector<int>& pages = print_pages_params_->pages;
 
   if (!print_preview_context_.CreatePreviewDocument(
-          prep_frame_view_.release(), pages, print_params.printed_doc_type)) {
+          std::move(prep_frame_view_), pages, print_params.printed_doc_type)) {
     return false;
   }
 
@@ -2194,14 +2194,14 @@
 }
 
 bool PrintRenderFrameHelper::PrintPreviewContext::CreatePreviewDocument(
-    PrepareFrameAndViewForPrint* prepared_frame,
+    std::unique_ptr<PrepareFrameAndViewForPrint> prepared_frame,
     const std::vector<int>& pages,
     SkiaDocumentType doc_type) {
   DCHECK_EQ(INITIALIZED, state_);
   state_ = RENDERING;
 
   // Need to make sure old object gets destroyed first.
-  prep_frame_view_.reset(prepared_frame);
+  prep_frame_view_ = std::move(prepared_frame);
   prep_frame_view_->StartPrinting();
 
   total_page_count_ = prep_frame_view_->GetExpectedPageCount();
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
index 1922098b..6a52cd8b 100644
--- a/components/printing/renderer/print_render_frame_helper.h
+++ b/components/printing/renderer/print_render_frame_helper.h
@@ -434,10 +434,10 @@
     void OnPrintPreview();
 
     // Create the print preview document. |pages| is empty to print all pages.
-    // Takes ownership of |prepared_frame|.
-    bool CreatePreviewDocument(PrepareFrameAndViewForPrint* prepared_frame,
-                               const std::vector<int>& pages,
-                               SkiaDocumentType doc_type);
+    bool CreatePreviewDocument(
+        std::unique_ptr<PrepareFrameAndViewForPrint> prepared_frame,
+        const std::vector<int>& pages,
+        SkiaDocumentType doc_type);
 
     // Called after a page gets rendered. |page_time| is how long the
     // rendering took.
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index 9dd03c38..7d1d968 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -169,8 +169,6 @@
 
   gpu::GpuPreferences gpu_preferences_;
 
-  gpu::GpuDriverBugWorkarounds gpu_workarounds_;
-
   // Information about the GPU, such as device and vendor ID.
   gpu::GPUInfo gpu_info_;
 
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 1711f5f..ebf10e6 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -188,6 +188,13 @@
 #include "media/device_monitors/device_monitor_mac.h"
 #endif
 
+#if defined(OS_FUCHSIA)
+#include <magenta/process.h>
+#include <magenta/syscalls.h>
+
+#include "base/fuchsia/default_job.h"
+#endif  // defined(OS_FUCHSIA)
+
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
 #include "content/browser/renderer_host/render_sandbox_host_linux.h"
 #include "content/browser/zygote_host/zygote_host_impl_linux.h"
@@ -458,6 +465,18 @@
     base::TimeDelta::FromSeconds(60);
 #endif  // !defined(OS_FUCHSIA)
 
+#if defined(OS_FUCHSIA)
+// Create and register the job which will contain all child processes
+// of the browser process as well as their descendents.
+void InitDefaultJob() {
+  base::ScopedMxHandle handle;
+  mx_status_t result = mx_job_create(mx_job_default(), 0, handle.receive());
+  CHECK_EQ(MX_OK, result) << "mx_job_create(job): "
+                          << mx_status_get_string(result);
+  base::SetDefaultJob(std::move(handle));
+}
+#endif  // defined(OS_FUCHSIA)
+
 }  // namespace
 
 #if defined(USE_X11) && !defined(OS_CHROMEOS)
@@ -608,6 +627,10 @@
   crypto::EnsureNSPRInit();
 #endif
 
+#if defined(OS_FUCHSIA)
+  InitDefaultJob();
+#endif
+
   if (parsed_command_line_.HasSwitch(switches::kRendererProcessLimit)) {
     std::string limit_string = parsed_command_line_.GetSwitchValueASCII(
         switches::kRendererProcessLimit);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 97345bc..8d1cb4e 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -519,6 +519,7 @@
       did_first_visually_non_empty_paint_(false),
       capturer_count_(0),
       should_normally_be_visible_(true),
+      should_normally_be_occluded_(false),
       did_first_set_visible_(false),
       is_being_destroyed_(false),
       is_notifying_observers_(false),
@@ -1358,7 +1359,7 @@
   }
 
   // Ensure that all views are un-occluded before capture begins.
-  WasUnOccluded();
+  DoWasUnOccluded();
 }
 
 void WebContentsImpl::DecrementCapturerCount() {
@@ -1374,11 +1375,14 @@
     const gfx::Size old_size = preferred_size_for_capture_;
     preferred_size_for_capture_ = gfx::Size();
     OnPreferredSizeChanged(old_size);
-  }
 
-  if (IsHidden()) {
-    DVLOG(1) << "Executing delayed WasHidden().";
-    WasHidden();
+    if (IsHidden()) {
+      DVLOG(1) << "Executing delayed WasHidden().";
+      WasHidden();
+    }
+
+    if (should_normally_be_occluded_)
+      WasOccluded();
   }
 }
 
@@ -1569,14 +1573,23 @@
 }
 
 void WebContentsImpl::WasOccluded() {
-  if (capturer_count_ > 0)
-    return;
+  if (capturer_count_ == 0) {
+    for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree())
+      view->WasOccluded();
+  }
 
-  for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree())
-    view->WasOccluded();
+  should_normally_be_occluded_ = true;
 }
 
 void WebContentsImpl::WasUnOccluded() {
+  if (capturer_count_ == 0)
+    DoWasUnOccluded();
+
+  should_normally_be_occluded_ = false;
+}
+
+void WebContentsImpl::DoWasUnOccluded() {
+  // TODO(fdoray): Only call WasUnOccluded on frames in the active viewport.
   for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree())
     view->WasUnOccluded();
 }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index e2bc7f3..a594d87 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -232,12 +232,6 @@
 
   bool should_normally_be_visible() { return should_normally_be_visible_; }
 
-  // Indicate if the window has been occluded, and pass this to the views, only
-  // if there is no active capture going on (otherwise it is dropped on the
-  // floor).
-  void WasOccluded();
-  void WasUnOccluded();
-
   // Broadcasts the mode change to all frames.
   void SetAccessibilityMode(ui::AXMode mode);
 
@@ -374,6 +368,8 @@
   void WasShown() override;
   void WasHidden() override;
   bool IsVisible() const override;
+  void WasOccluded() override;
+  void WasUnOccluded() override;
   bool NeedToFireBeforeUnload() override;
   void DispatchBeforeUnload() override;
   void AttachToOuterWebContentsFrame(
@@ -1043,6 +1039,9 @@
   // all the unique RenderWidgetHostViews.
   std::set<RenderWidgetHostView*> GetRenderWidgetHostViewsInTree();
 
+  // Calls WasUnOccluded() on all RenderWidgetHostViews in the frame tree.
+  void DoWasUnOccluded();
+
   // Called with the result of a DownloadImage() request.
   void OnDidDownloadImage(const ImageDownloadCallback& callback,
                           int id,
@@ -1455,6 +1454,9 @@
   // Tracks whether RWHV should be visible once capturer_count_ becomes zero.
   bool should_normally_be_visible_;
 
+  // Tracks whether RWHV should be occluded once |capturer_count_| becomes zero.
+  bool should_normally_be_occluded_;
+
   // Tracks whether this WebContents was ever set to be visible. Used to
   // facilitate WebContents being loaded in the background by setting
   // |should_normally_be_visible_|. Ensures WasShown() will trigger when first
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 85f5209..e871c9c 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2858,20 +2858,34 @@
   contents()->WasOccluded();
   EXPECT_TRUE(view->is_occluded());
 
-  // Add a capturer. This should cause the view to be un-occluded.
+  // Adding a capturer on an occluded WebContents should cause the view to be
+  // unoccluded. Removing the capturer should cause the view to be occluded
+  // again.
   contents()->IncrementCapturerCount(gfx::Size());
   EXPECT_FALSE(view->is_occluded());
 
-  // Try to occlude the view. This will fail to propagate because of the
-  // active capturer.
+  contents()->DecrementCapturerCount();
+  EXPECT_TRUE(view->is_occluded());
+
+  // Adding a capturer on an unoccluded WebContents should not change the
+  // occlusion state of the view. Calling WasOccluded() on an unoccluded
+  // WebContents() that has a capturer should not change the occlusion state of
+  // the view. Removing the capturer should cause the view to become occluded.
+  contents()->WasUnOccluded();
+  EXPECT_FALSE(view->is_occluded());
+  contents()->IncrementCapturerCount(gfx::Size());
+  EXPECT_FALSE(view->is_occluded());
+
   contents()->WasOccluded();
   EXPECT_FALSE(view->is_occluded());
 
-  // Remove the capturer and try again.
   contents()->DecrementCapturerCount();
-  EXPECT_FALSE(view->is_occluded());
-  contents()->WasOccluded();
   EXPECT_TRUE(view->is_occluded());
+
+  // Calling WasUnoccluded() on a WebContents with no capturers should cause the
+  // view to become unoccluded.
+  contents()->WasUnOccluded();
+  EXPECT_FALSE(view->is_occluded());
 }
 
 // Tests that GetLastActiveTime starts with a real, non-zero time and updates
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 576bd5e..ddaf66425 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -442,7 +442,8 @@
   // Get the last time that the WebContents was made hidden.
   virtual base::TimeTicks GetLastHiddenTime() const = 0;
 
-  // Invoked when the WebContents becomes shown/hidden.
+  // Invoked when the WebContents becomes shown/hidden. A hidden WebContents
+  // isn't painted on the screen.
   virtual void WasShown() = 0;
   virtual void WasHidden() = 0;
 
@@ -451,6 +452,12 @@
   // always returns false when the page is still loading.
   virtual bool IsVisible() const = 0;
 
+  // Invoked when the WebContents becomes occluded/unoccluded. An occluded
+  // WebContents isn't painted on the screen, except in a window switching
+  // feature (e.g. Alt-Tab).
+  virtual void WasOccluded() = 0;
+  virtual void WasUnOccluded() = 0;
+
   // Returns true if the before unload and unload listeners need to be
   // fired. The value of this changes over time. For example, if true and the
   // before unload listener is executed and allows the user to exit, then this
diff --git a/content/shell/browser/shell_views.cc b/content/shell/browser/shell_views.cc
index bd20d90..c513c703 100644
--- a/content/shell/browser/shell_views.cc
+++ b/content/shell/browser/shell_views.cc
@@ -115,8 +115,7 @@
   void InitShellWindow() {
     SetBackground(views::CreateStandardPanelBackground());
 
-    views::GridLayout* layout = new views::GridLayout(this);
-    SetLayoutManager(layout);
+    views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
     views::ColumnSet* column_set = layout->AddColumnSet(0);
     column_set->AddPaddingColumn(0, 2);
@@ -129,8 +128,8 @@
     // Add toolbar buttons and URL text field
     {
       layout->StartRow(0, 0);
-      views::GridLayout* toolbar_layout = new views::GridLayout(toolbar_view_);
-      toolbar_view_->SetLayoutManager(toolbar_layout);
+      views::GridLayout* toolbar_layout =
+          views::GridLayout::CreateAndInstall(toolbar_view_);
 
       views::ColumnSet* toolbar_column_set =
           toolbar_layout->AddColumnSet(0);
diff --git a/content/test/content_test_suite.cc b/content/test/content_test_suite.cc
index ab0f2b5..a4563e8 100644
--- a/content/test/content_test_suite.cc
+++ b/content/test/content_test_suite.cc
@@ -15,8 +15,10 @@
 #include "content/public/test/test_content_client_initializer.h"
 #include "gpu/config/gpu_info_collector.h"
 #include "gpu/config/gpu_util.h"
+#include "gpu/ipc/in_process_command_buffer.h"
 #include "media/base/media.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/init/gl_factory.h"
 #include "ui/gl/test/gl_surface_test_support.h"
 
 #if defined(OS_WIN)
@@ -83,14 +85,19 @@
   media::InitializeMediaLibrary();
   // When running in a child process for Mac sandbox tests, the sandbox exists
   // to initialize GL, so don't do it here.
-  bool is_child_process = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kTestChildProcess);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  bool is_child_process = command_line->HasSwitch(switches::kTestChildProcess);
   if (!is_child_process) {
     gpu::GPUInfo gpu_info;
     gpu::CollectBasicGraphicsInfo(&gpu_info);
-    gpu::ApplyGpuDriverBugWorkarounds(gpu_info,
-                                      base::CommandLine::ForCurrentProcess());
-    gl::GLSurfaceTestSupport::InitializeOneOff();
+    gpu::GpuFeatureInfo gpu_feature_info =
+        gpu::GetGpuFeatureInfo(gpu_info, *command_line);
+    gpu::InProcessCommandBuffer::InitializeDefaultServiceForTesting(
+        gpu_feature_info);
+    gl::GLSurfaceTestSupport::InitializeNoExtensionsOneOff();
+    gl::init::SetDisabledExtensionsPlatform(
+        gpu_feature_info.disabled_extensions);
+    gl::init::InitializeExtensionSettingsOneOffPlatform();
   }
   testing::TestEventListeners& listeners =
       testing::UnitTest::GetInstance()->listeners();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index 8923aaf..3d8d53dc 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -2236,7 +2236,7 @@
   context_creation_attribs_.bind_generates_resource = true;
 
   gl::init::InitializeGLOneOffImplementation(gl::kGLImplementationEGLGLES2,
-                                             false, false, false);
+                                             false, false, false, true);
   surface_ = gl::init::CreateOffscreenGLSurface(
       context_creation_attribs_.offscreen_framebuffer_size);
   context_ = gl::init::CreateGLContext(
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc
index f7d8f48..be6ff9e 100644
--- a/gpu/command_buffer/tests/fuzzer_main.cc
+++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -122,12 +122,12 @@
     command_line->AppendSwitchASCII(switches::kUseANGLE,
                                     gl::kANGLEImplementationNullName);
     CHECK(gl::init::InitializeGLOneOffImplementation(
-        gl::kGLImplementationEGLGLES2, false, false, false));
+        gl::kGLImplementationEGLGLES2, false, false, false, true));
 #elif defined(GPU_FUZZER_USE_SWIFTSHADER)
     command_line->AppendSwitchASCII(switches::kUseGL,
                                     gl::kGLImplementationSwiftShaderName);
     CHECK(gl::init::InitializeGLOneOffImplementation(
-        gl::kGLImplementationSwiftShaderGL, false, false, false));
+        gl::kGLImplementationSwiftShaderGL, false, false, false, true));
 #endif
 
 #if !defined(GPU_FUZZER_USE_STUB)
diff --git a/gpu/ipc/gpu_in_process_thread_service.cc b/gpu/ipc/gpu_in_process_thread_service.cc
index d1aedd6..ba6097f 100644
--- a/gpu/ipc/gpu_in_process_thread_service.cc
+++ b/gpu/ipc/gpu_in_process_thread_service.cc
@@ -13,8 +13,11 @@
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     gpu::SyncPointManager* sync_point_manager,
     gpu::gles2::MailboxManager* mailbox_manager,
-    scoped_refptr<gl::GLShareGroup> share_group)
-    : gpu::InProcessCommandBuffer::Service(mailbox_manager, share_group),
+    scoped_refptr<gl::GLShareGroup> share_group,
+    const GpuFeatureInfo& gpu_feature_info)
+    : gpu::InProcessCommandBuffer::Service(mailbox_manager,
+                                           share_group,
+                                           gpu_feature_info),
       task_runner_(task_runner),
       sync_point_manager_(sync_point_manager) {}
 
diff --git a/gpu/ipc/gpu_in_process_thread_service.h b/gpu/ipc/gpu_in_process_thread_service.h
index 6dbed869..464e713 100644
--- a/gpu/ipc/gpu_in_process_thread_service.h
+++ b/gpu/ipc/gpu_in_process_thread_service.h
@@ -24,7 +24,8 @@
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       gpu::SyncPointManager* sync_point_manager,
       gpu::gles2::MailboxManager* mailbox_manager,
-      scoped_refptr<gl::GLShareGroup> share_group);
+      scoped_refptr<gl::GLShareGroup> share_group,
+      const GpuFeatureInfo& gpu_feature_info);
 
   // gpu::InProcessCommandBuffer::Service implementation.
   void ScheduleTask(const base::Closure& task) override;
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index b91fd7c..1177f09 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -86,10 +86,16 @@
 
   ~GpuInProcessThreadHolder() override { Stop(); }
 
+  void SetGpuFeatureInfo(const GpuFeatureInfo& gpu_feature_info) {
+    DCHECK(!gpu_thread_service_.get());
+    gpu_feature_info_ = gpu_feature_info;
+  }
+
   const scoped_refptr<InProcessCommandBuffer::Service>& GetGpuThreadService() {
     if (!gpu_thread_service_) {
       gpu_thread_service_ = new GpuInProcessThreadService(
-          task_runner(), sync_point_manager_.get(), nullptr, nullptr);
+          task_runner(), sync_point_manager_.get(), nullptr, nullptr,
+          gpu_feature_info_);
     }
     return gpu_thread_service_;
   }
@@ -97,6 +103,7 @@
  private:
   std::unique_ptr<SyncPointManager> sync_point_manager_;
   scoped_refptr<InProcessCommandBuffer::Service> gpu_thread_service_;
+  GpuFeatureInfo gpu_feature_info_;
 };
 
 base::LazyInstance<GpuInProcessThreadHolder>::DestructorAtExit
@@ -128,13 +135,20 @@
 
 }  // anonyous namespace
 
+// TODO(zmo): This constructor is used only by DeferredGpuCommandService for
+// Android WebView. We will need to wire up the computed GpuFeatureInfo to
+// here instead of computing from commandline switch..
 InProcessCommandBuffer::Service::Service(const GpuPreferences& gpu_preferences)
     : Service(gpu_preferences, nullptr, nullptr) {}
 
 InProcessCommandBuffer::Service::Service(
     gpu::gles2::MailboxManager* mailbox_manager,
-    scoped_refptr<gl::GLShareGroup> share_group)
-    : Service(GpuPreferences(), mailbox_manager, share_group) {}
+    scoped_refptr<gl::GLShareGroup> share_group,
+    const GpuFeatureInfo& gpu_feature_info)
+    : Service(GpuPreferences(),
+              mailbox_manager,
+              share_group,
+              gpu_feature_info) {}
 
 InProcessCommandBuffer::Service::Service(
     const GpuPreferences& gpu_preferences,
@@ -152,6 +166,25 @@
   }
 }
 
+InProcessCommandBuffer::Service::Service(
+    const GpuPreferences& gpu_preferences,
+    gpu::gles2::MailboxManager* mailbox_manager,
+    scoped_refptr<gl::GLShareGroup> share_group,
+    const GpuFeatureInfo& gpu_feature_info)
+    : gpu_preferences_(gpu_preferences),
+      gpu_feature_info_(gpu_feature_info),
+      gpu_driver_bug_workarounds_(
+          gpu_feature_info.enabled_gpu_driver_bug_workarounds),
+      mailbox_manager_(mailbox_manager),
+      share_group_(share_group),
+      shader_translator_cache_(gpu_preferences_) {
+  if (!mailbox_manager_) {
+    // TODO(piman): have embedders own the mailbox manager.
+    owned_mailbox_manager_ = gles2::MailboxManager::Create(gpu_preferences_);
+    mailbox_manager_ = owned_mailbox_manager_.get();
+  }
+}
+
 InProcessCommandBuffer::Service::~Service() {}
 
 const gpu::GpuPreferences& InProcessCommandBuffer::Service::gpu_preferences() {
@@ -215,6 +248,19 @@
   Destroy();
 }
 
+// static
+void InProcessCommandBuffer::InitializeDefaultServiceForTesting(
+    const GpuFeatureInfo& gpu_feature_info) {
+  // Call base::ThreadTaskRunnerHandle::IsSet() to ensure that it is
+  // instantiated before we create the GPU thread, otherwise shutdown order will
+  // delete the ThreadTaskRunnerHandle before the GPU thread's message loop,
+  // and when the message loop is shutdown, it will recreate
+  // ThreadTaskRunnerHandle, which will re-add a new task to the, AtExitManager,
+  // which causes a deadlock because it's already locked.
+  base::ThreadTaskRunnerHandle::IsSet();
+  g_default_service.Get().SetGpuFeatureInfo(gpu_feature_info);
+}
+
 bool InProcessCommandBuffer::MakeCurrent() {
   CheckSequencedThread();
   command_buffer_lock_.AssertAcquired();
@@ -357,6 +403,9 @@
           gl_share_group_.get(), surface_.get(),
           GenerateGLContextAttribs(
               params.attribs, decoder_->GetContextGroup()->gpu_preferences()));
+      if (context_.get()) {
+        service_->gpu_feature_info().ApplyToGLContext(context_.get());
+      }
       gl_share_group_->SetSharedContext(surface_.get(), context_.get());
     }
 
@@ -376,6 +425,9 @@
         gl_share_group_.get(), surface_.get(),
         GenerateGLContextAttribs(
             params.attribs, decoder_->GetContextGroup()->gpu_preferences()));
+    if (context_.get()) {
+      service_->gpu_feature_info().ApplyToGLContext(context_.get());
+    }
   }
 
   if (!context_.get()) {
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index 110e8d4..eb04c78 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -31,6 +31,7 @@
 #include "gpu/command_buffer/service/image_manager.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "gpu/config/gpu_feature_info.h"
 #include "gpu/gpu_export.h"
 #include "gpu/ipc/service/image_transport_surface_delegate.h"
 #include "ui/gfx/gpu_memory_buffer.h"
@@ -186,12 +187,20 @@
   void UpdateVSyncParametersOnOriginThread(base::TimeTicks timebase,
                                            base::TimeDelta interval);
 
+  // Mostly the GpuFeatureInfo from GpuInit will be used to create a gpu thread
+  // service. In certain tests GpuInit is not part of the execution path, so
+  // the test suite need to compute it and pass it to the default service.
+  // See "gpu/ipc/in_process_command_buffer.cc".
+  static void InitializeDefaultServiceForTesting(
+      const GpuFeatureInfo& gpu_feature_info);
+
   // The serializer interface to the GPU service (i.e. thread).
   class Service {
    public:
     explicit Service(const gpu::GpuPreferences& gpu_preferences);
     Service(gles2::MailboxManager* mailbox_manager,
-            scoped_refptr<gl::GLShareGroup> share_group);
+            scoped_refptr<gl::GLShareGroup> share_group,
+            const GpuFeatureInfo& gpu_feature_info);
 
     virtual ~Service();
 
@@ -210,6 +219,7 @@
     virtual bool BlockThreadOnWaitSyncToken() const = 0;
 
     const GpuPreferences& gpu_preferences();
+    const GpuFeatureInfo& gpu_feature_info() { return gpu_feature_info_; }
     const GpuDriverBugWorkarounds& gpu_driver_bug_workarounds();
     scoped_refptr<gl::GLShareGroup> share_group();
     gles2::MailboxManager* mailbox_manager() { return mailbox_manager_; }
@@ -228,9 +238,14 @@
    protected:
     Service(const gpu::GpuPreferences& gpu_preferences,
             gles2::MailboxManager* mailbox_manager,
+            scoped_refptr<gl::GLShareGroup> share_group,
+            const GpuFeatureInfo& gpu_feature_info);
+    Service(const gpu::GpuPreferences& gpu_preferences,
+            gles2::MailboxManager* mailbox_manager,
             scoped_refptr<gl::GLShareGroup> share_group);
 
     const GpuPreferences gpu_preferences_;
+    const GpuFeatureInfo gpu_feature_info_;
     const GpuDriverBugWorkarounds gpu_driver_bug_workarounds_;
     std::unique_ptr<gles2::MailboxManager> owned_mailbox_manager_;
     gles2::MailboxManager* mailbox_manager_ = nullptr;
diff --git a/ios/chrome/browser/content_suggestions/BUILD.gn b/ios/chrome/browser/content_suggestions/BUILD.gn
index 88ce984..b8af6c00 100644
--- a/ios/chrome/browser/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/content_suggestions/BUILD.gn
@@ -17,8 +17,6 @@
     "content_suggestions_header_view_controller.mm",
     "content_suggestions_mediator.h",
     "content_suggestions_mediator.mm",
-    "content_suggestions_metrics_recorder.h",
-    "content_suggestions_metrics_recorder.mm",
     "content_suggestions_service_bridge_observer.h",
     "content_suggestions_service_bridge_observer.mm",
     "mediator_util.h",
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.mm b/ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.mm
index 206a0f98..c83b22c0 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.mm
@@ -44,6 +44,7 @@
                               action:^{
                                 ContentSuggestionsItem* strongItem = weakItem;
                                 if (strongItem) {
+                                  // TODO(crbug.com/691979): Add metrics.
                                   [weakCommandHandler
                                       openNewTabWithSuggestionsItem:strongItem
                                                           incognito:NO];
@@ -57,6 +58,7 @@
                               action:^{
                                 ContentSuggestionsItem* strongItem = weakItem;
                                 if (strongItem) {
+                                  // TODO(crbug.com/691979): Add metrics.
                                   [weakCommandHandler
                                       openNewTabWithSuggestionsItem:strongItem
                                                           incognito:YES];
@@ -72,6 +74,7 @@
                   action:^{
                     ContentSuggestionsItem* strongItem = weakItem;
                     if (strongItem) {
+                      // TODO(crbug.com/691979): Add metrics.
                       [weakCommandHandler addItemToReadingList:strongItem];
                     }
                   }
@@ -84,6 +87,7 @@
                               action:^{
                                 ContentSuggestionsItem* strongItem = weakItem;
                                 if (strongItem) {
+                                  // TODO(crbug.com/691979): Add metrics.
                                   [weakCommandHandler
                                       dismissSuggestion:strongItem
                                             atIndexPath:indexPath];
@@ -93,6 +97,7 @@
 
   [alertCoordinator addItemWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL)
                               action:^{
+                                // TODO(crbug.com/691979): Add metrics.
                               }
                                style:UIAlertActionStyleCancel];
   return alertCoordinator;
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
index 2073976..6637b52 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
@@ -20,7 +20,6 @@
 #import "ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.h"
 #import "ios/chrome/browser/content_suggestions/content_suggestions_header_view_controller.h"
 #import "ios/chrome/browser/content_suggestions/content_suggestions_mediator.h"
-#import "ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h"
 #import "ios/chrome/browser/content_suggestions/ntp_home_metrics.h"
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h"
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
@@ -94,7 +93,6 @@
 @property(nonatomic, strong) GoogleLandingMediator* googleLandingMediator;
 @property(nonatomic, strong)
     ContentSuggestionsHeaderSynchronizer* headerCollectionInteractionHandler;
-@property(nonatomic, strong) ContentSuggestionsMetricsRecorder* metricsRecorder;
 
 // Redefined as readwrite.
 @property(nonatomic, strong, readwrite)
@@ -119,7 +117,6 @@
 @synthesize webStateList = _webStateList;
 @synthesize dispatcher = _dispatcher;
 @synthesize delegate = _delegate;
-@synthesize metricsRecorder = _metricsRecorder;
 
 - (void)start {
   if (self.visible || !self.browserState) {
@@ -174,8 +171,6 @@
              mostVisitedSite:std::move(mostVisitedFactory)];
   self.contentSuggestionsMediator.commandHandler = self;
   self.contentSuggestionsMediator.headerProvider = self.headerController;
-  self.metricsRecorder = [[ContentSuggestionsMetricsRecorder alloc] init];
-  self.metricsRecorder.delegate = self.contentSuggestionsMediator;
 
   self.suggestionsViewController = [[ContentSuggestionsViewController alloc]
       initWithStyle:CollectionViewControllerStyleDefault];
@@ -185,7 +180,6 @@
   self.suggestionsViewController.suggestionsDelegate = self;
   self.suggestionsViewController.audience = self;
   self.suggestionsViewController.overscrollDelegate = self;
-  self.suggestionsViewController.metricsRecorder = self.metricsRecorder;
 
   [self.suggestionsViewController addChildViewController:self.headerController];
   [self.headerController
@@ -234,21 +228,12 @@
   [self.dispatcher showReadingList];
 }
 
-- (void)openPageForItemAtIndexPath:(NSIndexPath*)indexPath {
-  CollectionViewItem* item = [self.suggestionsViewController.collectionViewModel
-      itemAtIndexPath:indexPath];
+- (void)openPageForItem:(CollectionViewItem*)item {
+  // TODO(crbug.com/691979): Add metrics.
+
   ContentSuggestionsItem* suggestionItem =
       base::mac::ObjCCastStrict<ContentSuggestionsItem>(item);
 
-  [self.metricsRecorder
-         onSuggestionOpened:suggestionItem
-                atIndexPath:indexPath
-         sectionsShownAbove:[self.suggestionsViewController
-                                numberOfSectionsAbove:indexPath.section]
-      suggestionsShownAbove:[self.suggestionsViewController
-                                numberOfSuggestionsAbove:indexPath.section]
-                 withAction:WindowOpenDisposition::CURRENT_TAB];
-
   // Use a referrer with a specific URL to mark this entry as coming from
   // ContentSuggestions.
   web::Referrer referrer;
@@ -281,14 +266,6 @@
                         readLaterAction:(BOOL)readLaterAction {
   ContentSuggestionsItem* suggestionsItem =
       base::mac::ObjCCastStrict<ContentSuggestionsItem>(item);
-
-  [self.metricsRecorder
-      onMenuOpenedForSuggestion:suggestionsItem
-                    atIndexPath:indexPath
-          suggestionsShownAbove:[self.suggestionsViewController
-                                    numberOfSuggestionsAbove:indexPath
-                                                                 .section]];
-
   self.alertCoordinator = [ContentSuggestionsAlertFactory
       alertCoordinatorForSuggestionItem:suggestionsItem
                        onViewController:self.suggestionsViewController
@@ -355,40 +332,10 @@
                             incognito:(BOOL)incognito {
   new_tab_page_uma::RecordAction(self.browserState,
                                  new_tab_page_uma::ACTION_OPENED_SUGGESTION);
-
-  NSIndexPath* indexPath = [self.suggestionsViewController.collectionViewModel
-      indexPathForItem:item];
-  if (indexPath) {
-    WindowOpenDisposition disposition =
-        incognito ? WindowOpenDisposition::OFF_THE_RECORD
-                  : WindowOpenDisposition::NEW_BACKGROUND_TAB;
-    [self.metricsRecorder
-           onSuggestionOpened:item
-                  atIndexPath:indexPath
-           sectionsShownAbove:[self.suggestionsViewController
-                                  numberOfSectionsAbove:indexPath.section]
-        suggestionsShownAbove:[self.suggestionsViewController
-                                  numberOfSuggestionsAbove:indexPath.section]
-                   withAction:disposition];
-  }
-
   [self openNewTabWithURL:item.URL incognito:incognito];
 }
 
 - (void)addItemToReadingList:(ContentSuggestionsItem*)item {
-  NSIndexPath* indexPath = [self.suggestionsViewController.collectionViewModel
-      indexPathForItem:item];
-  if (indexPath) {
-    [self.metricsRecorder
-           onSuggestionOpened:item
-                  atIndexPath:indexPath
-           sectionsShownAbove:[self.suggestionsViewController
-                                  numberOfSectionsAbove:indexPath.section]
-        suggestionsShownAbove:[self.suggestionsViewController
-                                  numberOfSuggestionsAbove:indexPath.section]
-                   withAction:WindowOpenDisposition::SAVE_TO_DISK];
-  }
-
   self.contentSuggestionsMediator.readingListNeedsReload = YES;
   ReadingListAddCommand* command =
       [[ReadingListAddCommand alloc] initWithURL:item.URL title:item.title];
@@ -404,6 +351,7 @@
         indexPathForItem:item];
   }
 
+  // TODO(crbug.com/691979): Add metrics.
   [self.contentSuggestionsMediator dismissSuggestion:item.suggestionIdentifier];
   [self.suggestionsViewController dismissEntryAtIndexPath:itemIndexPath];
 }
@@ -586,6 +534,8 @@
 
 // Opens the |URL| in a new tab |incognito| or not.
 - (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito {
+  // TODO(crbug.com/691979): Add metrics.
+
   // Open the tab in background if it is non-incognito only.
   [self.URLLoader webPageOrderedOpen:URL
                             referrer:web::Referrer()
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.h b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.h
index 35abe31..a9e61abf 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.h
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.h
@@ -10,7 +10,6 @@
 #include <memory>
 
 #import "ios/chrome/browser/content_suggestions/content_suggestions_mediator.h"
-#import "ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_source.h"
 
 namespace favicon {
@@ -36,9 +35,7 @@
 // Mediator for ContentSuggestions. Makes the interface between a
 // ntp_snippets::ContentSuggestionsService and the Objective-C services using
 // its data.
-@interface ContentSuggestionsMediator
-    : NSObject<ContentSuggestionsDataSource,
-               ContentSuggestionsMetricsRecorderDelegate>
+@interface ContentSuggestionsMediator : NSObject<ContentSuggestionsDataSource>
 
 // Initialize the mediator with the |contentService| to mediate.
 - (nullable instancetype)
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
index 8e212bb..404846cf 100644
--- a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
@@ -128,7 +128,6 @@
         base::MakeUnique<ContentSuggestionsServiceBridge>(self, contentService);
     _contentService = contentService;
     _sectionInformationByCategory = [[NSMutableDictionary alloc] init];
-
     _faviconMediator = [[ContentSuggestionsFaviconMediator alloc]
         initWithContentService:contentService
               largeIconService:largeIconService
@@ -465,14 +464,6 @@
   }
 }
 
-#pragma mark - ContentSuggestionsMetricsRecorderDelegate
-
-- (ContentSuggestionsCategoryWrapper*)categoryWrapperForSectionInfo:
-    (ContentSuggestionsSectionInformation*)sectionInfo {
-  return [[self.sectionInformationByCategory allKeysForObject:sectionInfo]
-      firstObject];
-}
-
 #pragma mark - Private
 
 // Converts the |suggestions| from |category| to CSCollectionViewItem and adds
@@ -518,6 +509,13 @@
       wrapperWithCategory:category]] = sectionInfo;
 }
 
+// Returns a CategoryWrapper acting as a key for this section info.
+- (ContentSuggestionsCategoryWrapper*)categoryWrapperForSectionInfo:
+    (ContentSuggestionsSectionInformation*)sectionInfo {
+  return [[self.sectionInformationByCategory allKeysForObject:sectionInfo]
+      firstObject];
+}
+
 // If the |statusCode| is a success and |suggestions| is not empty, runs the
 // |callback| with the |suggestions| converted to Objective-C.
 - (void)didFetchMoreSuggestions:
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h b/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h
deleted file mode 100644
index 62ec5015..0000000
--- a/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDER_H_
-#define IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDER_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h"
-
-@class ContentSuggestionsCategoryWrapper;
-@class ContentSuggestionsItem;
-@class ContentSuggestionsSectionInformation;
-
-// Delegate for the metrics recorder.
-@protocol ContentSuggestionsMetricsRecorderDelegate
-
-// Returns a CategoryWrapper corresponding to this |sectionInfo|.
-- (nullable ContentSuggestionsCategoryWrapper*)categoryWrapperForSectionInfo:
-    (nonnull ContentSuggestionsSectionInformation*)sectionInfo;
-
-@end
-
-// Records different metrics for ContentSuggestions
-@interface ContentSuggestionsMetricsRecorder
-    : NSObject<ContentSuggestionsMetricsRecording>
-
-@property(nonatomic, weak, nullable)
-    id<ContentSuggestionsMetricsRecorderDelegate>
-        delegate;
-
-// Records the opening of an |item| suggestion at the |indexPath|. Also needs
-// the number of |sectionsShownAbove| this section and the number of
-// |suggestionsAbove| its section. The item is opened with |action|.
-- (void)onSuggestionOpened:(nonnull ContentSuggestionsItem*)item
-               atIndexPath:(nonnull NSIndexPath*)indexPath
-        sectionsShownAbove:(NSInteger)sectionsShownAbove
-     suggestionsShownAbove:(NSInteger)suggestionsAbove
-                withAction:(WindowOpenDisposition)action;
-
-// Records the opening of a context menu for an |item| at the |indexPath|. Needs
-// the number of |suggestionsAbove| the section of the item.
-- (void)onMenuOpenedForSuggestion:(nonnull ContentSuggestionsItem*)item
-                      atIndexPath:(nonnull NSIndexPath*)indexPath
-            suggestionsShownAbove:(NSInteger)suggestionsAbove;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDER_H_
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.mm b/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.mm
deleted file mode 100644
index c595864..0000000
--- a/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.mm
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h"
-
-#include "base/mac/foundation_util.h"
-#include "components/ntp_snippets/content_suggestions_metrics.h"
-#import "ios/chrome/browser/content_suggestions/content_suggestions_category_wrapper.h"
-#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h"
-#import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h"
-#import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h"
-#import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation ContentSuggestionsMetricsRecorder
-
-@synthesize delegate = _delegate;
-
-#pragma mark - Public
-
-- (void)onSuggestionOpened:(ContentSuggestionsItem*)item
-               atIndexPath:(NSIndexPath*)indexPath
-        sectionsShownAbove:(NSInteger)sectionsShownAbove
-     suggestionsShownAbove:(NSInteger)suggestionsAbove
-                withAction:(WindowOpenDisposition)action {
-  ContentSuggestionsSectionInformation* sectionInfo =
-      item.suggestionIdentifier.sectionInfo;
-  ContentSuggestionsCategoryWrapper* categoryWrapper =
-      [self.delegate categoryWrapperForSectionInfo:sectionInfo];
-
-  ntp_snippets::metrics::OnSuggestionOpened(
-      suggestionsAbove + indexPath.item, [categoryWrapper category],
-      sectionsShownAbove, indexPath.item, item.publishDate, item.score, action,
-      /*is_prefetched=*/false, /*is_offline=*/false);
-}
-
-- (void)onMenuOpenedForSuggestion:(ContentSuggestionsItem*)item
-                      atIndexPath:(NSIndexPath*)indexPath
-            suggestionsShownAbove:(NSInteger)suggestionsAbove {
-  ContentSuggestionsSectionInformation* sectionInfo =
-      item.suggestionIdentifier.sectionInfo;
-  ContentSuggestionsCategoryWrapper* categoryWrapper =
-      [self.delegate categoryWrapperForSectionInfo:sectionInfo];
-
-  ntp_snippets::metrics::OnSuggestionMenuOpened(
-      suggestionsAbove + indexPath.item, [categoryWrapper category],
-      indexPath.item, item.publishDate, item.score);
-}
-
-#pragma mark - ContentSuggestionsMetricsRecording
-
-- (void)onSuggestionShown:(CollectionViewItem*)item
-              atIndexPath:(NSIndexPath*)indexPath
-    suggestionsShownAbove:(NSInteger)suggestionsAbove {
-  ContentSuggestionsItem* suggestion =
-      base::mac::ObjCCastStrict<ContentSuggestionsItem>(item);
-  ContentSuggestionsSectionInformation* sectionInfo =
-      suggestion.suggestionIdentifier.sectionInfo;
-  ContentSuggestionsCategoryWrapper* categoryWrapper =
-      [self.delegate categoryWrapperForSectionInfo:sectionInfo];
-
-  ntp_snippets::metrics::OnSuggestionShown(
-      suggestionsAbove + indexPath.item, [categoryWrapper category],
-      indexPath.item, suggestion.publishDate, suggestion.score,
-      suggestion.fetchDate, /*is_prefetched=*/false, /*is_offline=*/false);
-}
-
-- (void)onMoreButtonTappedAtPosition:(NSInteger)position
-                           inSection:(ContentSuggestionsSectionInformation*)
-                                         sectionInfo {
-  ContentSuggestionsCategoryWrapper* categoryWrapper =
-      [self.delegate categoryWrapperForSectionInfo:sectionInfo];
-
-  ntp_snippets::metrics::OnMoreButtonClicked([categoryWrapper category],
-                                             position);
-}
-
-- (void)onSuggestionDismissed:(CollectionViewItem<SuggestedContent>*)item
-                  atIndexPath:(NSIndexPath*)indexPath
-        suggestionsShownAbove:(NSInteger)suggestionsAbove {
-  ContentSuggestionsSectionInformation* sectionInfo =
-      item.suggestionIdentifier.sectionInfo;
-  ContentSuggestionsCategoryWrapper* categoryWrapper =
-      [self.delegate categoryWrapperForSectionInfo:sectionInfo];
-
-  ntp_snippets::metrics::OnSuggestionDismissed(
-      suggestionsAbove + indexPath.item, [categoryWrapper category],
-      indexPath.item, /*visited=*/false);
-}
-
-@end
diff --git a/ios/chrome/browser/content_suggestions/mediator_util.mm b/ios/chrome/browser/content_suggestions/mediator_util.mm
index 2072de8..81306c03 100644
--- a/ios/chrome/browser/content_suggestions/mediator_util.mm
+++ b/ios/chrome/browser/content_suggestions/mediator_util.mm
@@ -62,7 +62,6 @@
       initWithType:0
              title:base::SysUTF16ToNSString(contentSuggestion.title())
                url:contentSuggestion.url()];
-  suggestion.metricsRecorded = NO;
 
   suggestion.publisher =
       base::SysUTF16ToNSString(contentSuggestion.publisher_name());
@@ -73,9 +72,6 @@
       contentSuggestion.id().id_within_category();
   suggestion.suggestionIdentifier.sectionInfo = sectionInfo;
 
-  suggestion.score = contentSuggestion.score();
-  suggestion.fetchDate = contentSuggestion.fetch_date();
-
   if (category.IsKnownCategory(ntp_snippets::KnownCategories::READING_LIST)) {
     suggestion.faviconURL =
         contentSuggestion.reading_list_suggestion_extra()->favicon_page_url;
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
index d769bc2..25a2ec4 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
@@ -182,8 +182,6 @@
       base::Bind(&ParseJson), GetFetchEndpoint(GetChannel()), api_key,
       service->user_classifier());
 
-  // This pref is also used for logging. If it is changed, change it in the
-  // other places.
   std::string pref_name = prefs::kSearchSuggestEnabled;
   auto provider = base::MakeUnique<RemoteSuggestionsProviderImpl>(
       service, prefs, GetApplicationContext()->GetApplicationLocale(),
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 2f8318ba..f19a502 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -19,7 +19,6 @@
     "content_suggestions_header_view_controller_delegate.h",
     "content_suggestions_layout.h",
     "content_suggestions_layout.mm",
-    "content_suggestions_metrics_recording.h",
     "content_suggestions_view_controller.h",
     "content_suggestions_view_controller.mm",
     "content_suggestions_view_controller_audience.h",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h
index f69d7428..e0168ec 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h
@@ -55,11 +55,6 @@
 // Command handler for the accessibility custom actions.
 @property(nonatomic, weak) id<ContentSuggestionsGestureCommands> commandHandler;
 
-// Score of the suggestions.
-@property(nonatomic, assign) float score;
-// Date when the suggestion has been fetched.
-@property(nonatomic, assign) base::Time fetchDate;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_ITEM_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm
index a19dc8a..567c2a23 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm
@@ -49,9 +49,6 @@
 @synthesize firstTimeWithImage = _firstTimeWithImage;
 @synthesize readLaterAction = _readLaterAction;
 @synthesize commandHandler = _commandHandler;
-@synthesize score = _score;
-@synthesize fetchDate = _fetchDate;
-@synthesize metricsRecorded = _metricsRecorded;
 
 - (instancetype)initWithType:(NSInteger)type
                        title:(NSString*)title
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm
index 32000bf..55c25af 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm
@@ -29,7 +29,6 @@
 @implementation ContentSuggestionsLearnMoreItem
 
 @synthesize suggestionIdentifier;
-@synthesize metricsRecorded;
 
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.mm
index b644250..9f04f3d7 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.mm
@@ -25,7 +25,6 @@
 @synthesize URL = _URL;
 @synthesize source = _source;
 @synthesize commandHandler = _commandHandler;
-@synthesize metricsRecorded = _metricsRecorded;
 
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
index e95c525..fea3722a 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
@@ -18,7 +18,6 @@
 @synthesize text = _text;
 @synthesize detailText = _detailText;
 @synthesize suggestionIdentifier = _suggestionIdentifier;
-@synthesize metricsRecorded = _metricsRecorded;
 
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm
index 2d4038f..3408bc7 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm
@@ -35,7 +35,6 @@
 @synthesize text = _text;
 @synthesize icon = _icon;
 @synthesize suggestionIdentifier = _suggestionIdentifier;
-@synthesize metricsRecorded = _metricsRecorded;
 
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h b/ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h
index c1f775b..118dab41 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h
@@ -15,10 +15,7 @@
 @protocol SuggestedContent
 
 // Identifier for this content.
-@property(nonatomic, strong, nullable)
-    ContentSuggestionIdentifier* suggestionIdentifier;
-// Whether the metrics for this suggestion have been recorded.
-@property(nonatomic, assign) BOOL metricsRecorded;
+@property(nonatomic, strong) ContentSuggestionIdentifier* suggestionIdentifier;
 
 // The height needed by a cell configured by this item, for a |width|.
 - (CGFloat)cellHeightForWidth:(CGFloat)width;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
index 46a5341..ac4517e0 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
@@ -77,10 +77,6 @@
 // header containing the fake omnibox and the logo.
 - (BOOL)isHeaderSection:(NSInteger)section;
 
-// Returns whether |section| is one of the section containing ContentSuggestions
-// items.
-- (BOOL)isContentSuggestionsSection:(NSInteger)section;
-
 // Updates the number of Most Visited tiles shown for the |size| on the model
 // only. The collection needs to be updated separately.
 - (void)updateMostVisitedForSize:(CGSize)size;
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 ee9c3a15d..0752881 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
@@ -20,7 +20,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_sink.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_source.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h"
@@ -126,7 +125,7 @@
 }
 
 // Returns whether this |sectionIdentifier| comes from ContentSuggestions.
-BOOL IsFromContentSuggestionsService(NSInteger sectionIdentifier) {
+BOOL IsFromContentSuggestions(NSInteger sectionIdentifier) {
   return sectionIdentifier == SectionIdentifierArticles ||
          sectionIdentifier == SectionIdentifierReadingList;
 }
@@ -407,7 +406,7 @@
         [model itemsInSectionWithIdentifier:sectionIdentifier].count > 0) {
       return @[];
     }
-  } else if (IsFromContentSuggestionsService(sectionIdentifier)) {
+  } else if (IsFromContentSuggestions(sectionIdentifier)) {
     // If the section is a ContentSuggestions section, add the "Learn more"
     // items if they are not already present.
     if ([model hasSectionForSectionIdentifier:SectionIdentifierLearnMore] &&
@@ -531,12 +530,6 @@
              sectionIdentifierForSection:section] == SectionIdentifierPromo;
 }
 
-- (BOOL)isContentSuggestionsSection:(NSInteger)section {
-  return IsFromContentSuggestionsService(
-      [self.collectionViewController.collectionViewModel
-          sectionIdentifierForSection:section]);
-}
-
 - (void)updateMostVisitedForSize:(CGSize)size {
   self.collectionWidth = size.width;
 
@@ -621,7 +614,7 @@
       sectionInfo.title) {
     BOOL addHeader = YES;
 
-    if (IsFromContentSuggestionsService(sectionIdentifier)) {
+    if (IsFromContentSuggestions(sectionIdentifier)) {
       addHeader = NO;
 
       if ([self.sectionIdentifiersFromContentSuggestions
@@ -690,19 +683,6 @@
                                  cell:(ContentSuggestionsFooterCell*)cell {
   SectionIdentifier sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
 
-  CSCollectionViewModel* model =
-      self.collectionViewController.collectionViewModel;
-  if (![model hasSectionForSectionIdentifier:sectionIdentifier])
-    return;
-
-  // The more button is the footer of the section. So its position is the number
-  // of items in the section.
-  [self.collectionViewController.metricsRecorder
-      onMoreButtonTappedAtPosition:
-          [model numberOfItemsInSection:
-                     [model sectionForSectionIdentifier:sectionIdentifier]]
-                         inSection:sectionInfo];
-
   item.loading = YES;
   [item configureCell:cell];
 
@@ -710,7 +690,8 @@
       [NSMutableArray array];
 
   NSArray<CSCollectionViewItem*>* knownSuggestions =
-      [model itemsInSectionWithIdentifier:sectionIdentifier];
+      [self.collectionViewController.collectionViewModel
+          itemsInSectionWithIdentifier:sectionIdentifier];
   for (CSCollectionViewItem* suggestion in knownSuggestions) {
     if (suggestion.type != ItemTypeEmpty) {
       [knownSuggestionIdentifiers addObject:suggestion.suggestionIdentifier];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
index 3e257b2..f7f1210 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
@@ -13,8 +13,8 @@
 
 // Opens the Reading List.
 - (void)openReadingList;
-// Opens the page associated with the item at |indexPath|.
-- (void)openPageForItemAtIndexPath:(nonnull NSIndexPath*)indexPath;
+// Opens the page associated with this |item|.
+- (void)openPageForItem:(nonnull CollectionViewItem*)item;
 // Opens the Most Visited associated with this |item| at the |mostVisitedItem|.
 - (void)openMostVisitedItem:(nonnull CollectionViewItem*)item
                     atIndex:(NSInteger)mostVisitedIndex;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h
deleted file mode 100644
index cd4e9f4..0000000
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDING_H_
-#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDING_H_
-
-#import <UIKit/UIKIt.h>
-
-#include "ui/base/window_open_disposition.h"
-
-@class CollectionViewItem;
-@class ContentSuggestionsSectionInformation;
-@protocol SuggestedContent;
-
-// Protocol for recording metrics related to ContentSuggestions.
-@protocol ContentSuggestionsMetricsRecording
-
-// Records the appearance of an |item| suggestion at |indexPath|. Needs the
-// number of |suggestionsAbove| the item's section.
-- (void)onSuggestionShown:(CollectionViewItem*)item
-              atIndexPath:(NSIndexPath*)indexPath
-    suggestionsShownAbove:(NSInteger)suggestionsAbove;
-
-// Records a tap on a more button in the section associated with |sectionInfo|.
-// Needs the button |position| in the section.
-- (void)onMoreButtonTappedAtPosition:(NSInteger)position
-                           inSection:(ContentSuggestionsSectionInformation*)
-                                         sectionInfo;
-
-// Records the dismissal of a suggestion |item| at |indexPath|.Needs the number
-// of |suggestionsAbove| the item's section.
-- (void)onSuggestionDismissed:(CollectionViewItem<SuggestedContent>*)item
-                  atIndexPath:(NSIndexPath*)indexPath
-        suggestionsShownAbove:(NSInteger)suggestionsAbove;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDING_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
index a9a1f5d..1c9b286 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
@@ -14,7 +14,6 @@
 @protocol ContentSuggestionsCommands;
 @protocol ContentSuggestionsDataSource;
 @protocol ContentSuggestionsHeaderSynchronizing;
-@protocol ContentSuggestionsMetricsRecording;
 @protocol ContentSuggestionsViewControllerAudience;
 @protocol ContentSuggestionsViewControllerDelegate;
 @protocol OverscrollActionsControllerDelegate;
@@ -45,8 +44,6 @@
 // Delegate for the overscroll actions.
 @property(nonatomic, weak) id<OverscrollActionsControllerDelegate>
     overscrollDelegate;
-@property(nonatomic, weak) id<ContentSuggestionsMetricsRecording>
-    metricsRecorder;
 
 - (void)setDataSource:(id<ContentSuggestionsDataSource>)dataSource;
 
@@ -59,11 +56,6 @@
 - (void)addSuggestions:
             (NSArray<CollectionViewItem<SuggestedContent>*>*)suggestions
          toSectionInfo:(ContentSuggestionsSectionInformation*)sectionInfo;
-// Returns the number of suggestions displayed above this |section|.
-- (NSInteger)numberOfSuggestionsAbove:(NSInteger)section;
-// Returns the number of sections containing suggestions displayed above this
-// |section|.
-- (NSInteger)numberOfSectionsAbove:(NSInteger)section;
 
 // Returns the accessibility identifier of the collection.
 + (NSString*)collectionAccessibilityIdentifier;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 84217d5..783afb3 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -18,7 +18,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizing.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
@@ -63,7 +62,6 @@
 @synthesize overscrollActionsController = _overscrollActionsController;
 @synthesize overscrollDelegate = _overscrollDelegate;
 @synthesize scrolledToTop = _scrolledToTop;
-@synthesize metricsRecorder = _metricsRecorder;
 @dynamic collectionViewModel;
 
 #pragma mark - Lifecycle
@@ -97,11 +95,6 @@
     return;
   }
 
-  [self.metricsRecorder
-      onSuggestionDismissed:[self.collectionViewModel itemAtIndexPath:indexPath]
-                atIndexPath:indexPath
-      suggestionsShownAbove:[self numberOfSuggestionsAbove:indexPath.section]];
-
   [self.collectionView performBatchUpdates:^{
     [self collectionView:self.collectionView
         willDeleteItemsAtIndexPaths:@[ indexPath ]];
@@ -169,27 +162,6 @@
       }];
 }
 
-- (NSInteger)numberOfSuggestionsAbove:(NSInteger)section {
-  NSInteger suggestionsAbove = 0;
-  for (NSInteger sectionAbove = 0; sectionAbove < section; sectionAbove++) {
-    if ([self.collectionUpdater isContentSuggestionsSection:sectionAbove]) {
-      suggestionsAbove +=
-          [self.collectionViewModel numberOfItemsInSection:sectionAbove];
-    }
-  }
-  return suggestionsAbove;
-}
-
-- (NSInteger)numberOfSectionsAbove:(NSInteger)section {
-  NSInteger sectionsAbove = 0;
-  for (NSInteger sectionAbove = 0; sectionAbove < section; sectionAbove++) {
-    if ([self.collectionUpdater isContentSuggestionsSection:sectionAbove]) {
-      sectionsAbove++;
-    }
-  }
-  return sectionsAbove;
-}
-
 + (NSString*)collectionAccessibilityIdentifier {
   return @"ContentSuggestionsCollectionIdentifier";
 }
@@ -284,10 +256,10 @@
   switch ([self.collectionUpdater contentSuggestionTypeForItem:item]) {
     case ContentSuggestionTypeReadingList:
       base::RecordAction(base::UserMetricsAction("MobileReadingListOpen"));
-      [self.suggestionCommandHandler openPageForItemAtIndexPath:indexPath];
+      [self.suggestionCommandHandler openPageForItem:item];
       break;
     case ContentSuggestionTypeArticle:
-      [self.suggestionCommandHandler openPageForItemAtIndexPath:indexPath];
+      [self.suggestionCommandHandler openPageForItem:item];
       break;
     case ContentSuggestionTypeMostVisited:
       [self.suggestionCommandHandler openMostVisitedItem:item
@@ -306,23 +278,6 @@
   }
 }
 
-- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
-                 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
-  CSCollectionViewItem* item =
-      [self.collectionViewModel itemAtIndexPath:indexPath];
-
-  if (!item.metricsRecorded) {
-    [self.metricsRecorder
-            onSuggestionShown:item
-                  atIndexPath:indexPath
-        suggestionsShownAbove:[self
-                                  numberOfSuggestionsAbove:indexPath.section]];
-    item.metricsRecorded = YES;
-  }
-
-  return [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
-}
-
 #pragma mark - UICollectionViewDelegateFlowLayout
 
 - (CGSize)collectionView:(UICollectionView*)collectionView
diff --git a/ios/chrome/browser/ui/payments/payment_request_egtest_base.mm b/ios/chrome/browser/ui/payments/payment_request_egtest_base.mm
index 2ea450da..2c8315a 100644
--- a/ios/chrome/browser/ui/payments/payment_request_egtest_base.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_egtest_base.mm
@@ -7,12 +7,14 @@
 #include <algorithm>
 #include <memory>
 
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/payments/core/features.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/payments/ios_payment_request_cache_factory.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
@@ -47,6 +49,16 @@
 
 #pragma mark - XCTestCase
 
++ (void)setUp {
+  [super setUp];
+  if (!base::FeatureList::IsEnabled(payments::features::kWebPayments)) {
+    // payments::features::kWebPayments feature is not enabled,
+    // You have to pass --enable-features=WebPayments command line argument in
+    // order to run this test.
+    DCHECK(false);
+  }
+}
+
 - (void)tearDown {
   for (const auto& profile : _profiles) {
     [self personalDataManager]->RemoveByGUID(profile.guid());
diff --git a/ios/chrome/browser/ui/settings/compose_email_handler_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/compose_email_handler_collection_view_controller_unittest.mm
index 80e1ef0f..4105ea7 100644
--- a/ios/chrome/browser/ui/settings/compose_email_handler_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/compose_email_handler_collection_view_controller_unittest.mm
@@ -19,10 +19,6 @@
 
 #pragma mark - MailtoURLRewriter private interface for testing.
 
-@interface MailtoURLRewriter ()
-- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps;
-@end
-
 #pragma mark - ComposeEmailHandlerCollectionViewControllerTest
 
 class ComposeEmailHandlerCollectionViewControllerTest
@@ -34,7 +30,7 @@
   // CollectionViewController.
   CollectionViewController* InstantiateController() override {
     rewriter_ = [[LegacyMailtoURLRewriter alloc] init];
-    [rewriter_ addMailtoApps:handlers_];
+    [rewriter_ setDefaultHandlers:handlers_];
     if (defaultHandlerID_)
       [rewriter_ setDefaultHandlerID:defaultHandlerID_];
     return [[ComposeEmailHandlerCollectionViewController alloc]
diff --git a/ios/chrome/browser/ui/settings/content_settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/content_settings_collection_view_controller.mm
index 13d4de7..a8a6eef 100644
--- a/ios/chrome/browser/ui/settings/content_settings_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/content_settings_collection_view_controller.mm
@@ -108,7 +108,7 @@
     [_disablePopupsSetting setObserver:self];
 
     _mailtoURLRewriter =
-        [[LegacyMailtoURLRewriter alloc] initWithStandardHandlers];
+        [LegacyMailtoURLRewriter mailtoURLRewriterWithStandardHandlers];
     [_mailtoURLRewriter setObserver:self];
 
     [self loadModel];
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 5d97c1de7..3af6b5d 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -25,6 +25,8 @@
     "navigation_manager_util.mm",
     "network_activity_indicator_tab_helper.h",
     "network_activity_indicator_tab_helper.mm",
+    "nullable_mailto_url_rewriter.h",
+    "nullable_mailto_url_rewriter.mm",
     "page_placeholder_tab_helper.h",
     "page_placeholder_tab_helper.mm",
     "repost_form_tab_helper.h",
@@ -68,6 +70,7 @@
     "mailto_handler_unittest.mm",
     "navigation_manager_util_unittest.mm",
     "network_activity_indicator_tab_helper_unittest.mm",
+    "nullable_mailto_url_rewriter_unittest.mm",
     "page_placeholder_tab_helper_unittest.mm",
     "repost_form_tab_helper_unittest.mm",
     "sad_tab_tab_helper_unittest.mm",
diff --git a/ios/chrome/browser/web/external_app_launcher.mm b/ios/chrome/browser/web/external_app_launcher.mm
index 77b4b4ed..ecc8c4f 100644
--- a/ios/chrome/browser/web/external_app_launcher.mm
+++ b/ios/chrome/browser/web/external_app_launcher.mm
@@ -176,7 +176,7 @@
   if (base::FeatureList::IsEnabled(kMailtoUrlRewriting) &&
       gURL.SchemeIs(url::kMailToScheme)) {
     MailtoURLRewriter* rewriter =
-        [[LegacyMailtoURLRewriter alloc] initWithStandardHandlers];
+        [LegacyMailtoURLRewriter mailtoURLRewriterWithStandardHandlers];
     NSString* launchURL = [rewriter rewriteMailtoURL:gURL];
     if (launchURL)
       URL = [NSURL URLWithString:launchURL];
diff --git a/ios/chrome/browser/web/legacy_mailto_url_rewriter.mm b/ios/chrome/browser/web/legacy_mailto_url_rewriter.mm
index 9ba570c..b26e6c5 100644
--- a/ios/chrome/browser/web/legacy_mailto_url_rewriter.mm
+++ b/ios/chrome/browser/web/legacy_mailto_url_rewriter.mm
@@ -15,14 +15,6 @@
 #error "This file requires ARC support."
 #endif
 
-namespace {
-// The key for NSUserDefaults to store the Mail client selected to handle
-// mailto: URL scheme. If this key is not set, user has not made an explicit
-// choice for default mailto: handler and system-provided Mail client app will
-// be used.
-NSString* const kMailtoDefaultHandlerKey = @"MailtoHandlerDefault";
-}  // namespace
-
 @interface LegacyMailtoURLRewriter ()
 
 // Dictionary keyed by the App Store ID of the Mail client and the value is
@@ -30,13 +22,6 @@
 @property(nonatomic, strong)
     NSMutableDictionary<NSString*, MailtoHandler*>* handlers;
 
-// Private method for testing to clear the default state.
-+ (void)resetDefaultHandlerIDForTesting;
-
-// Private method to add one or more |handlerApp| objects to the list of known
-// Mail client apps.
-- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps;
-
 // Custom logic to handle the migration from Google Native App Launcher options
 // to this simplified mailto: URL only system. This must be called after
 // -addMailtoApp: has been called to add all the known Mail client apps.
@@ -54,6 +39,22 @@
 @implementation LegacyMailtoURLRewriter
 @synthesize handlers = _handlers;
 
++ (NSString*)userDefaultsKey {
+  // The key for NSUserDefaults to store the Mail client selected to handle
+  // mailto: URL scheme. If this key is not set, user has not made an explicit
+  // choice for default mailto: handler and system-provided Mail client app will
+  // be used.
+  return @"MailtoHandlerDefault";
+}
+
++ (instancetype)mailtoURLRewriterWithStandardHandlers {
+  id result = [[LegacyMailtoURLRewriter alloc] init];
+  [result setDefaultHandlers:@[
+    [[MailtoHandlerSystemMail alloc] init], [[MailtoHandlerGmail alloc] init]
+  ]];
+  return result;
+}
+
 - (instancetype)init {
   self = [super init];
   if (self) {
@@ -62,16 +63,6 @@
   return self;
 }
 
-- (instancetype)initWithStandardHandlers {
-  self = [self init];
-  if (self) {
-    [self addMailtoApps:@[
-      [[MailtoHandlerSystemMail alloc] init], [[MailtoHandlerGmail alloc] init]
-    ]];
-  }
-  return self;
-}
-
 - (NSArray<MailtoHandler*>*)defaultHandlers {
   return [[_handlers allValues]
       sortedArrayUsingComparator:^NSComparisonResult(
@@ -80,20 +71,33 @@
       }];
 }
 
+- (void)setDefaultHandlers:(NSArray<MailtoHandler*>*)handlerApps {
+  for (MailtoHandler* app in handlerApps) {
+    [_handlers setObject:app forKey:[app appStoreID]];
+  }
+  [self migrateLegacyOptions];
+  [self autoDefaultToGmailIfInstalled];
+}
+
 - (NSString*)defaultHandlerID {
   NSString* value = [[NSUserDefaults standardUserDefaults]
-      stringForKey:kMailtoDefaultHandlerKey];
+      stringForKey:[[self class] userDefaultsKey]];
+  // This implementation of MailtoURLRewriter always returns a non-nil mailto:
+  // URL handler ID.
   if ([_handlers[value] isAvailable])
     return value;
   return [[self class] systemMailApp];
 }
 
 - (void)setDefaultHandlerID:(NSString*)appStoreID {
+  // This implementation of MailtoURLRewriter does not allow the unsetting of
+  // a mailto: URL handler.
   DCHECK([appStoreID length]);
   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
-  if ([appStoreID isEqual:[defaults objectForKey:kMailtoDefaultHandlerKey]])
+  NSString* defaultsKey = [[self class] userDefaultsKey];
+  if ([appStoreID isEqual:[defaults objectForKey:defaultsKey]])
     return;
-  [defaults setObject:appStoreID forKey:kMailtoDefaultHandlerKey];
+  [defaults setObject:appStoreID forKey:defaultsKey];
   [self.observer rewriterDidChange:self];
 }
 
@@ -116,19 +120,6 @@
 
 #pragma mark - Private
 
-+ (void)resetDefaultHandlerIDForTesting {
-  [[NSUserDefaults standardUserDefaults]
-      removeObjectForKey:kMailtoDefaultHandlerKey];
-}
-
-- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps {
-  for (MailtoHandler* app in handlerApps) {
-    [_handlers setObject:app forKey:[app appStoreID]];
-  }
-  [self migrateLegacyOptions];
-  [self autoDefaultToGmailIfInstalled];
-}
-
 //
 // Implements the migration logic for users of previous versions of Google
 // Chrome which supports Google Native App Launcher. The goal is to preserve
@@ -157,6 +148,7 @@
 //
 - (void)migrateLegacyOptions {
   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  NSString* defaultsKey = [[self class] userDefaultsKey];
 
   // User previously had a selection made for opening mailto: links with Gmail,
   // upgrade will set Gmail app to be the default mailto: handler. If user had
@@ -178,17 +170,16 @@
         // Gmail app.
         MailtoHandler* gmailHandler = _handlers[kGmailAppStoreID];
         if ([gmailHandler isAvailable])
-          [defaults setObject:kGmailAppStoreID forKey:kMailtoDefaultHandlerKey];
+          [defaults setObject:kGmailAppStoreID forKey:defaultsKey];
         else
-          [defaults removeObjectForKey:kMailtoDefaultHandlerKey];
+          [defaults removeObjectForKey:defaultsKey];
         break;
       }
       case 1:
         // If legacy user was not using Gmail to handle mailto: links
         // (kAutoOpenLinksNo), consider this an explicit user choice and
         // migrate to use system-provided Mail app.
-        [defaults setObject:[[self class] systemMailApp]
-                     forKey:kMailtoDefaultHandlerKey];
+        [defaults setObject:[[self class] systemMailApp] forKey:defaultsKey];
         break;
       default:
         NOTREACHED();
@@ -200,15 +191,16 @@
 
 - (void)autoDefaultToGmailIfInstalled {
   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  NSString* defaultsKey = [[self class] userDefaultsKey];
   // If a default handler for mailto: has already been set, user had made an
   // explicit choice and no further changes should be done.
-  if ([defaults objectForKey:kMailtoDefaultHandlerKey])
+  if ([defaults objectForKey:defaultsKey])
     return;
 
   NSString* const kGmailAppStoreID = @"422689480";
   MailtoHandler* gmailHandler = _handlers[kGmailAppStoreID];
   if ([gmailHandler isAvailable])
-    [defaults setObject:kGmailAppStoreID forKey:kMailtoDefaultHandlerKey];
+    [defaults setObject:kGmailAppStoreID forKey:defaultsKey];
 }
 
 @end
diff --git a/ios/chrome/browser/web/legacy_mailto_url_rewriter_unittest.mm b/ios/chrome/browser/web/legacy_mailto_url_rewriter_unittest.mm
index b7ab354..f25a571d 100644
--- a/ios/chrome/browser/web/legacy_mailto_url_rewriter_unittest.mm
+++ b/ios/chrome/browser/web/legacy_mailto_url_rewriter_unittest.mm
@@ -27,26 +27,20 @@
 
 }  // namespace
 
-#pragma mark - LegacyMailtoURLRewriter private interfaces for testing.
-
-@interface LegacyMailtoURLRewriter ()
-+ (void)resetDefaultHandlerIDForTesting;
-- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps;
-@end
-
 #pragma mark - Unit Test Cases
 
 class LegacyMailtoURLRewriterTest : public PlatformTest {
- protected:
-  void SetUp() override {
-    [LegacyMailtoURLRewriter resetDefaultHandlerIDForTesting];
+ public:
+  LegacyMailtoURLRewriterTest() {
+    [[NSUserDefaults standardUserDefaults]
+        removeObjectForKey:[LegacyMailtoURLRewriter userDefaultsKey]];
   }
 };
 
 // Tests that a standard instance has the expected values.
 TEST_F(LegacyMailtoURLRewriterTest, TestStandardInstance) {
   LegacyMailtoURLRewriter* rewriter =
-      [[LegacyMailtoURLRewriter alloc] initWithStandardHandlers];
+      [LegacyMailtoURLRewriter mailtoURLRewriterWithStandardHandlers];
   EXPECT_TRUE(rewriter);
   EXPECT_GT([[rewriter defaultHandlerName] length], 0U);
   // ID for system Mail client app must not be an empty string.
@@ -73,7 +67,7 @@
   MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
   MailtoHandler* fakeGmailHandler =
       [[FakeMailtoHandlerGmailNotInstalled alloc] init];
-  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
   // Sets the default handler to Gmail (which is not installed). This simulates
   // the situation when Gmail was installed and set as the default handler.
   // Then Gmail app is deleted from the device.
@@ -87,12 +81,12 @@
 TEST_F(LegacyMailtoURLRewriterTest, TestUserPreferencePersistence) {
   // Sets up a first LegacyMailtoURLRewriter with at least 2 MailtoHandler
   // objects. A faked Gmail handler that is installed must be used or
-  // -addMailtoApp: will just skip it.
+  // -setDefaultHandlers: will just skip it.
   LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init];
   MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
   MailtoHandler* fakeGmailHandler =
       [[FakeMailtoHandlerGmailInstalled alloc] init];
-  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
 
   // Verifies that there must be 2 registered handlers. Then find a
   // MailtoHandler that is not the current default and set that as the new
@@ -113,7 +107,7 @@
   // Create a new LegacyMailtoURLRewriter object and verify that the current
   // default is the |otherHandlerID| set in the previous step.
   LegacyMailtoURLRewriter* rewriter2 = [[LegacyMailtoURLRewriter alloc] init];
-  [rewriter2 addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter2 setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
   EXPECT_NSEQ(otherHandlerID, [rewriter2 defaultHandlerID]);
 }
 
@@ -128,7 +122,7 @@
   MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
   MailtoHandler* fakeGmailHandler =
       [[FakeMailtoHandlerGmailInstalled alloc] init];
-  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
   EXPECT_NSEQ([fakeGmailHandler appStoreID], [rewriter defaultHandlerID]);
   [rewriter setObserver:observer];
 
@@ -166,7 +160,7 @@
   // Sets up a LegacyMailtoURLRewriter for testing.
   LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init];
   MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
-  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
 
   // Verify that LegacyMailtoURLRewriter will use the system Mail app.
   EXPECT_NSEQ([LegacyMailtoURLRewriter systemMailApp],
@@ -185,7 +179,7 @@
   // Sets up a LegacyMailtoURLRewriter for testing.
   LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init];
   MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
-  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
 
   // Verify that LegacyMailtoURLRewriter will use Gmail app.
   EXPECT_NSEQ(kGmailAppStoreID, [rewriter defaultHandlerID]);
@@ -205,7 +199,7 @@
   // Sets up a LegacyMailtoURLRewriter for testing.
   LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init];
   MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
-  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
 
   // Verify that LegacyMailtoURLRewriter will use the system Mail app. As part
   // of the "upgrade", the legacy key should be removed as well.
@@ -227,7 +221,7 @@
   // Sets up a LegacyMailtoURLRewriter for testing.
   LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init];
   MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
-  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
 
   // Verify that LegacyMailtoURLRewriter will use Gmail app. As part of the
   // upgrade, the legacy key should be removed as well.
@@ -248,7 +242,7 @@
   // Sets up a LegacyMailtoURLRewriter for testing.
   LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init];
   MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
-  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
 
   // Verify that LegacyMailtoURLRewriter will use Gmail app.
   EXPECT_NSEQ(kGmailAppStoreID, [rewriter defaultHandlerID]);
diff --git a/ios/chrome/browser/web/mailto_url_rewriter.h b/ios/chrome/browser/web/mailto_url_rewriter.h
index e58c4788..16e1b94e56 100644
--- a/ios/chrome/browser/web/mailto_url_rewriter.h
+++ b/ios/chrome/browser/web/mailto_url_rewriter.h
@@ -24,30 +24,33 @@
 @interface MailtoURLRewriter : NSObject
 
 // The unique ID of the Mail client app that handles mailto: URL scheme.
+// This has a value of nil if default has not been set.
 @property(nonatomic, copy) NSString* defaultHandlerID;
 
+// Array of all the currently supported Mail client apps that claim to handle
+// mailto: URL scheme through their own custom defined URL schemes.
+@property(nonatomic, strong) NSArray<MailtoHandler*>* defaultHandlers;
+
 // Observer object that will be called when |defaultHandlerID| is changed.
 @property(nonatomic, weak) id<MailtoURLRewriterObserver> observer;
 
+// Returns the NSString* key to store state in NSUserDefaults.
++ (NSString*)userDefaultsKey;
+
 // Returns the ID as a string for the system-provided Mail client app.
 + (NSString*)systemMailApp;
 
-// An initializer returning an instance that has the standard set of
-// MailtoHandlers initialized. Unit tests can use -init and then set up the
-// different handlers.
-- (instancetype)initWithStandardHandlers;
+// Convenience method to return a new instance of this class initialized with
+// a standard set of MailtoHandlers.
++ (instancetype)mailtoURLRewriterWithStandardHandlers;
 
-// Returns a sorted array of all the currently supported Mail client apps that
-// claim to handle mailto: URL scheme through their own custom defined URL
-// schemes.
-- (NSArray<MailtoHandler*>*)defaultHandlers;
-
-// Returns the name of the application that handles mailto: URLs.
+// Returns the name of the application that handles mailto: URLs. Returns nil
+// if a default has not been set.
 - (NSString*)defaultHandlerName;
 
 // Rewrites |URL| into a new URL that can be "opened" to launch the Mail
-// client app. May return nil if |URL| is not a mailto: URL or there are no
-// Mail client app available.
+// client app. May return nil if |URL| is not a mailto: URL, a mail client
+// app has not been selected, or there are no Mail client app available.
 - (NSString*)rewriteMailtoURL:(const GURL&)URL;
 
 @end
diff --git a/ios/chrome/browser/web/mailto_url_rewriter.mm b/ios/chrome/browser/web/mailto_url_rewriter.mm
index 3fa669a..060ba2ea 100644
--- a/ios/chrome/browser/web/mailto_url_rewriter.mm
+++ b/ios/chrome/browser/web/mailto_url_rewriter.mm
@@ -17,6 +17,7 @@
 
 @implementation MailtoURLRewriter
 @synthesize observer = _observer;
+@dynamic defaultHandlers;
 
 - (NSString*)defaultHandlerID {
   NOTREACHED();
@@ -27,15 +28,23 @@
   NOTREACHED();
 }
 
++ (NSString*)userDefaultsKey {
+  return nil;
+}
+
 + (NSString*)systemMailApp {
   // This is the App Store ID for Apple Mail app.
   // See https://itunes.apple.com/us/app/mail/id1108187098?mt=8
   return @"1108187098";
 }
 
-- (instancetype)initWithStandardHandlers {
++ (instancetype)mailtoURLRewriterWithStandardHandlers {
   NOTREACHED();
-  return self;
+  return nil;
+}
+
+- (void)setDefaultHandlers:(NSArray<MailtoHandler*>*)defaultHandlers {
+  NOTREACHED();
 }
 
 - (NSArray<MailtoHandler*>*)defaultHandlers {
diff --git a/ios/chrome/browser/web/nullable_mailto_url_rewriter.h b/ios/chrome/browser/web/nullable_mailto_url_rewriter.h
new file mode 100644
index 0000000..9f15241b
--- /dev/null
+++ b/ios/chrome/browser/web/nullable_mailto_url_rewriter.h
@@ -0,0 +1,18 @@
+// 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_CHROME_BROWSER_WEB_NULLABLE_MAILTO_URL_REWRITER_H_
+#define IOS_CHROME_BROWSER_WEB_NULLABLE_MAILTO_URL_REWRITER_H_
+
+#import "ios/chrome/browser/web/mailto_url_rewriter.h"
+
+// An object that manages the available Mail client apps. The currently selected
+// Mail client to handle mailto: URL is stored in a key in NSUserDefaults. If a
+// default has not been set in NSUserDefaults, nil may be returned from some of
+// the public APIs of MailtoURLRewriter.
+@interface NullableMailtoURLRewriter : MailtoURLRewriter
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_WEB_NULLABLE_MAILTO_URL_REWRITER_H_
diff --git a/ios/chrome/browser/web/nullable_mailto_url_rewriter.mm b/ios/chrome/browser/web/nullable_mailto_url_rewriter.mm
new file mode 100644
index 0000000..dc4a18d3
--- /dev/null
+++ b/ios/chrome/browser/web/nullable_mailto_url_rewriter.mm
@@ -0,0 +1,113 @@
+// 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/chrome/browser/web/nullable_mailto_url_rewriter.h"
+
+#import <UIKit/UIKit.h>
+
+#import "base/logging.h"
+#import "ios/chrome/browser/web/mailto_handler.h"
+#import "ios/chrome/browser/web/mailto_handler_gmail.h"
+#import "ios/chrome/browser/web/mailto_handler_system_mail.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface NullableMailtoURLRewriter ()
+
+// Dictionary keyed by the unique ID of the Mail client. The value is
+// the MailtoHandler object that can rewrite a mailto: URL.
+@property(nonatomic, strong)
+    NSMutableDictionary<NSString*, MailtoHandler*>* handlers;
+
+@end
+
+@implementation NullableMailtoURLRewriter
+@synthesize handlers = _handlers;
+
++ (NSString*)userDefaultsKey {
+  // This key in NSUserDefaults stores the default handler ID stored.
+  return @"UserChosenDefaultMailApp";
+}
+
++ (instancetype)mailtoURLRewriterWithStandardHandlers {
+  id result = [[NullableMailtoURLRewriter alloc] init];
+  [result setDefaultHandlers:@[
+    [[MailtoHandlerSystemMail alloc] init], [[MailtoHandlerGmail alloc] init]
+  ]];
+  return result;
+}
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _handlers = [NSMutableDictionary dictionary];
+  }
+  return self;
+}
+
+- (NSArray<MailtoHandler*>*)defaultHandlers {
+  return [[_handlers allValues]
+      sortedArrayUsingComparator:^NSComparisonResult(
+          MailtoHandler* _Nonnull obj1, MailtoHandler* _Nonnull obj2) {
+        return [[obj1 appName] compare:[obj2 appName]];
+      }];
+}
+
+- (void)setDefaultHandlers:(NSArray<MailtoHandler*>*)defaultHandlers {
+  for (MailtoHandler* app in defaultHandlers) {
+    [_handlers setObject:app forKey:[app appStoreID]];
+  }
+}
+
+- (NSString*)defaultHandlerID {
+  NSString* value = [[NSUserDefaults standardUserDefaults]
+      stringForKey:[[self class] userDefaultsKey]];
+  if (value) {
+    if ([_handlers[value] isAvailable])
+      return value;
+    return [[self class] systemMailApp];
+  }
+  // User has not made a choice.
+  NSMutableArray* availableHandlers = [NSMutableArray array];
+  for (MailtoHandler* handler in [_handlers allValues]) {
+    if ([handler isAvailable])
+      [availableHandlers addObject:handler];
+  }
+  if ([availableHandlers count] == 1)
+    return [[availableHandlers firstObject] appStoreID];
+  return nil;
+}
+
+- (void)setDefaultHandlerID:(NSString*)appStoreID {
+  DCHECK([appStoreID length]);
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  NSString* defaultsKey = [[self class] userDefaultsKey];
+  if ([appStoreID isEqual:[defaults objectForKey:defaultsKey]])
+    return;
+  [defaults setObject:appStoreID forKey:defaultsKey];
+  [self.observer rewriterDidChange:self];
+}
+
+- (NSString*)defaultHandlerName {
+  NSString* handlerID = [self defaultHandlerID];
+  if (!handlerID)
+    return nil;
+  MailtoHandler* handler = _handlers[handlerID];
+  return [handler appName];
+}
+
+- (NSString*)rewriteMailtoURL:(const GURL&)gURL {
+  NSString* value = [self defaultHandlerID];
+  if ([value length]) {
+    MailtoHandler* handler = _handlers[value];
+    if ([handler isAvailable]) {
+      return [handler rewriteMailtoURL:gURL];
+    }
+  }
+  return nil;
+}
+
+@end
diff --git a/ios/chrome/browser/web/nullable_mailto_url_rewriter_unittest.mm b/ios/chrome/browser/web/nullable_mailto_url_rewriter_unittest.mm
new file mode 100644
index 0000000..80a1464
--- /dev/null
+++ b/ios/chrome/browser/web/nullable_mailto_url_rewriter_unittest.mm
@@ -0,0 +1,113 @@
+// 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/chrome/browser/web/nullable_mailto_url_rewriter.h"
+
+#import "ios/chrome/browser/web/fake_mailto_handler_helpers.h"
+#import "ios/chrome/browser/web/mailto_handler_system_mail.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+class NullableMailtoURLRewriterTest : public PlatformTest {
+ public:
+  NullableMailtoURLRewriterTest() {
+    [[NSUserDefaults standardUserDefaults]
+        removeObjectForKey:[NullableMailtoURLRewriter userDefaultsKey]];
+  }
+};
+
+// Tests that a new instance has expected properties and behaviors.
+TEST_F(NullableMailtoURLRewriterTest, TestStandardInstance) {
+  NullableMailtoURLRewriter* rewriter =
+      [NullableMailtoURLRewriter mailtoURLRewriterWithStandardHandlers];
+  EXPECT_TRUE(rewriter);
+
+  NSArray<MailtoHandler*>* handlers = [rewriter defaultHandlers];
+  EXPECT_GE([handlers count], 1U);
+  for (MailtoHandler* handler in handlers) {
+    ASSERT_TRUE(handler);
+    NSString* appStoreID = [handler appStoreID];
+    NSString* expectedDefaultAppID =
+        [handler isAvailable] ? appStoreID : [MailtoURLRewriter systemMailApp];
+    [rewriter setDefaultHandlerID:appStoreID];
+    EXPECT_NSEQ(expectedDefaultAppID, [rewriter defaultHandlerID]);
+  }
+}
+
+// If Gmail is not installed, rewriter defaults to system Mail app.
+TEST_F(NullableMailtoURLRewriterTest, TestNoGmailInstalled) {
+  NullableMailtoURLRewriter* rewriter =
+      [[NullableMailtoURLRewriter alloc] init];
+  [rewriter setDefaultHandlers:@[
+    [[MailtoHandlerSystemMail alloc] init],
+    [[FakeMailtoHandlerGmailNotInstalled alloc] init]
+  ]];
+  EXPECT_NSEQ([MailtoURLRewriter systemMailApp], [rewriter defaultHandlerID]);
+}
+
+// If Gmail is installed but user has not made a choice, there is no default
+// mail app.
+TEST_F(NullableMailtoURLRewriterTest, TestWithGmailChoiceNotMade) {
+  NullableMailtoURLRewriter* rewriter =
+      [[NullableMailtoURLRewriter alloc] init];
+  [rewriter setDefaultHandlers:@[
+    [[MailtoHandlerSystemMail alloc] init],
+    [[FakeMailtoHandlerGmailInstalled alloc] init]
+  ]];
+  EXPECT_FALSE([rewriter defaultHandlerID]);
+}
+
+// If Gmail was installed and user has made a choice, then Gmail is uninstalled.
+// The default returns to system Mail app.
+TEST_F(NullableMailtoURLRewriterTest, TestWithGmailUninstalled) {
+  NullableMailtoURLRewriter* rewriter =
+      [[NullableMailtoURLRewriter alloc] init];
+  MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
+  MailtoHandler* fakeGmailHandler =
+      [[FakeMailtoHandlerGmailInstalled alloc] init];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
+  [rewriter setDefaultHandlerID:[fakeGmailHandler appStoreID]];
+  EXPECT_NSEQ([fakeGmailHandler appStoreID], [rewriter defaultHandlerID]);
+
+  rewriter = [[NullableMailtoURLRewriter alloc] init];
+  fakeGmailHandler = [[FakeMailtoHandlerGmailNotInstalled alloc] init];
+  [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]];
+  EXPECT_NSEQ([MailtoURLRewriter systemMailApp], [rewriter defaultHandlerID]);
+}
+
+// If Gmail is installed but system Mail app has been chosen by user as the
+// default mail handler app. Then Gmail is uninstalled. User's choice of system
+// Mail app remains unchanged and will persist through a re-installation of
+// Gmail.
+TEST_F(NullableMailtoURLRewriterTest,
+       TestSystemMailAppChosenSurviveGmailUninstall) {
+  // Initial state of system Mail app explicitly chosen.
+  NullableMailtoURLRewriter* rewriter =
+      [[NullableMailtoURLRewriter alloc] init];
+  MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
+  [rewriter setDefaultHandlers:@[
+    systemMailHandler, [[FakeMailtoHandlerGmailInstalled alloc] init]
+  ]];
+  [rewriter setDefaultHandlerID:[systemMailHandler appStoreID]];
+  EXPECT_NSEQ([systemMailHandler appStoreID], [rewriter defaultHandlerID]);
+
+  // Gmail is installed.
+  rewriter = [[NullableMailtoURLRewriter alloc] init];
+  [rewriter setDefaultHandlers:@[
+    systemMailHandler, [[FakeMailtoHandlerGmailNotInstalled alloc] init]
+  ]];
+  EXPECT_NSEQ([systemMailHandler appStoreID], [rewriter defaultHandlerID]);
+
+  // Gmail is installed again.
+  rewriter = [[NullableMailtoURLRewriter alloc] init];
+  [rewriter setDefaultHandlers:@[
+    systemMailHandler, [[FakeMailtoHandlerGmailInstalled alloc] init]
+  ]];
+  EXPECT_NSEQ([systemMailHandler appStoreID], [rewriter defaultHandlerID]);
+}
diff --git a/ios/chrome/browser/web/sad_tab_tab_helper.h b/ios/chrome/browser/web/sad_tab_tab_helper.h
index 1f7cffc..93a994eb 100644
--- a/ios/chrome/browser/web/sad_tab_tab_helper.h
+++ b/ios/chrome/browser/web/sad_tab_tab_helper.h
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/web_state/web_state_observer.h"
-#import "ios/web/public/web_state/web_state_user_data.h"
+#import <Foundation/Foundation.h>
 
 #include "base/macros.h"
 #include "base/timer/elapsed_timer.h"
+#import "ios/web/public/web_state/web_state_observer.h"
+#import "ios/web/public/web_state/web_state_user_data.h"
 
 // SadTabTabHelper listens to RenderProcessGone events and presents a
 // SadTabView view appropriately.
@@ -61,6 +62,10 @@
   void DidFinishNavigation(web::NavigationContext* navigation_context) override;
   void WebStateDestroyed() override;
 
+  // The default window of time a failure of the same URL needs to occur
+  // to be considered a repeat failure.
+  static const double kDefaultRepeatFailureInterval;
+
   // Stores the last URL that caused a renderer crash,
   // used to detect repeated crashes.
   GURL last_failed_url_;
@@ -69,21 +74,21 @@
   // used to determine time window for repeated crashes.
   std::unique_ptr<base::ElapsedTimer> last_failed_timer_;
 
-  // Stores the interval window during which a second RenderProcessGone failure
-  // will be considered a repeat failure.
-  double repeat_failure_interval_;
+  // Stores the interval window in seconds during which a second
+  // RenderProcessGone failure will be considered a repeat failure.
+  double repeat_failure_interval_ = kDefaultRepeatFailureInterval;
 
   // Whether or not WebState is currently being displayed.
-  bool is_visible_;
+  bool is_visible_ = false;
 
   // true if the WebState needs to be reloaded after web state becomes visible.
-  bool requires_reload_on_becoming_visible_;
+  bool requires_reload_on_becoming_visible_ = false;
 
   // true if the WebState needs to be reloaded after the app becomes active.
-  bool requires_reload_on_becoming_active_;
+  bool requires_reload_on_becoming_active_ = false;
 
   // Observer for UIApplicationDidBecomeActiveNotification.
-  __strong id<NSObject> application_did_become_active_observer_;
+  __strong id<NSObject> application_did_become_active_observer_ = nil;
 
   DISALLOW_COPY_AND_ASSIGN(SadTabTabHelper);
 };
diff --git a/ios/chrome/browser/web/sad_tab_tab_helper.mm b/ios/chrome/browser/web/sad_tab_tab_helper.mm
index eff6eae..d597585c 100644
--- a/ios/chrome/browser/web/sad_tab_tab_helper.mm
+++ b/ios/chrome/browser/web/sad_tab_tab_helper.mm
@@ -4,8 +4,6 @@
 
 #import "ios/chrome/browser/web/sad_tab_tab_helper.h"
 
-#import <Foundation/Foundation.h>
-
 #include "base/memory/ptr_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
@@ -21,17 +19,15 @@
 
 DEFINE_WEB_STATE_USER_DATA_KEY(SadTabTabHelper);
 
-namespace {
-// The default window of time a failure of the same URL needs to occur
-// to be considered a repeat failure.
-NSTimeInterval const kDefaultRepeatFailureInterval = 60.0f;
+const double SadTabTabHelper::kDefaultRepeatFailureInterval = 60.0f;
 
+namespace {
 // Returns true if the application is in UIApplicationStateActive state.
 bool IsApplicationStateActive() {
   return UIApplication.sharedApplication.applicationState ==
          UIApplicationStateActive;
 }
-}
+}  // namespace
 
 SadTabTabHelper::SadTabTabHelper(web::WebState* web_state)
     : SadTabTabHelper(web_state, kDefaultRepeatFailureInterval) {}
@@ -39,10 +35,7 @@
 SadTabTabHelper::SadTabTabHelper(web::WebState* web_state,
                                  double repeat_failure_interval)
     : web::WebStateObserver(web_state),
-      repeat_failure_interval_(repeat_failure_interval),
-      is_visible_(false),
-      requires_reload_on_becoming_visible_(false),
-      requires_reload_on_becoming_active_(false) {
+      repeat_failure_interval_(repeat_failure_interval) {
   AddApplicationDidBecomeActiveObserver();
 }
 
diff --git a/ios/clean/chrome/browser/ui/commands/BUILD.gn b/ios/clean/chrome/browser/ui/commands/BUILD.gn
index 2d6888e..a7fbd4b 100644
--- a/ios/clean/chrome/browser/ui/commands/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/commands/BUILD.gn
@@ -5,6 +5,7 @@
 source_set("commands") {
   sources = [
     "context_menu_commands.h",
+    "dialog_commands.h",
     "find_in_page_search_commands.h",
     "find_in_page_visibility_commands.h",
     "navigation_commands.h",
diff --git a/ios/clean/chrome/browser/ui/commands/dialog_commands.h b/ios/clean/chrome/browser/ui/commands/dialog_commands.h
new file mode 100644
index 0000000..f3f0052
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/commands/dialog_commands.h
@@ -0,0 +1,30 @@
+// 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_COMMANDS_DIALOG_COMMANDS_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_DIALOG_COMMANDS_H_
+
+#import <Foundation/Foundation.h>
+
+@class DialogConfigurationIdentifier;
+
+// Convenience typedef to improve formatting.
+using DialogTextFieldValues =
+    NSDictionary<DialogConfigurationIdentifier*, NSString*>;
+
+// Command protocol for dismissing DialogConsumers.
+@protocol DialogDismissalCommands
+
+// Called to dismiss the dialog.  |buttonID| is the identifier of the
+// DialogButtonConfiguration corresponding with the dialog button that was
+// tapped, if any.  |textFieldValues| contains the user input text.  The keys
+// are the identifiers of the DialogTextFieldConfigurations passed to the
+// consumer, and the values are the text in their corresponding text fields.
+- (void)
+dismissDialogWithButtonID:(nonnull DialogConfigurationIdentifier*)buttonID
+          textFieldValues:(nonnull DialogTextFieldValues*)textFieldValues;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_DIALOG_COMMANDS_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/BUILD.gn b/ios/clean/chrome/browser/ui/dialogs/BUILD.gn
new file mode 100644
index 0000000..f5f87d2c
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/BUILD.gn
@@ -0,0 +1,83 @@
+# 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("dialog_coordinator_support") {
+  sources = [
+    "dialog_coordinator+subclassing.h",
+    "dialog_coordinator.h",
+    "dialog_coordinator.mm",
+    "dialog_mediator+subclassing.h",
+    "dialog_mediator.h",
+    "dialog_mediator.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":dialogs_ui",
+    "//base",
+    "//ios/chrome/browser/ui/browser_list",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/coordinators",
+    "//ios/clean/chrome/browser/ui/commands",
+    "//ios/clean/chrome/browser/ui/overlays",
+  ]
+}
+
+source_set("dialogs_ui") {
+  sources = [
+    "dialog_button_configuration.h",
+    "dialog_button_configuration.mm",
+    "dialog_button_style.h",
+    "dialog_configuration_identifier.h",
+    "dialog_configuration_identifier.mm",
+    "dialog_consumer.h",
+    "dialog_text_field_configuration.h",
+    "dialog_text_field_configuration.mm",
+    "dialog_view_controller.h",
+    "dialog_view_controller.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "//base",
+    "//components/strings",
+    "//ios/chrome/app/strings",
+    "//ios/clean/chrome/browser/ui/commands",
+    "//ui/base",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "dialog_button_configuration_unittest.mm",
+    "dialog_configuration_identifier_unittest.mm",
+    "dialog_coordinator_unittest.mm",
+    "dialog_mediator_unittest.mm",
+    "dialog_text_field_configuration_unittest.mm",
+    "dialog_view_controller_unittest.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":dialog_coordinator_support",
+    ":dialogs_ui",
+    "//base",
+    "//base/test:test_support",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ui/browser_list",
+    "//ios/chrome/browser/ui/coordinators",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/chrome/test/base",
+    "//ios/clean/chrome/browser/ui/dialogs/test_helpers",
+    "//ios/clean/chrome/browser/ui/overlays/test_helpers",
+    "//ios/web",
+    "//ios/web/public/test",
+    "//ios/web/public/test/fakes",
+    "//testing/gtest",
+  ]
+}
diff --git a/ios/clean/chrome/browser/ui/dialogs/OWNERS b/ios/clean/chrome/browser/ui/dialogs/OWNERS
new file mode 100644
index 0000000..48efb49e
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/OWNERS
@@ -0,0 +1 @@
+kkhorimoto@chromium.org
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h
new file mode 100644
index 0000000..25ede32
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h
@@ -0,0 +1,34 @@
+// 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_DIALOGS_DIALOG_BUTTON_CONFIGURATION_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_CONFIGURATION_H_
+
+#import <UIKit/UIKit.h>
+
+@class DialogConfigurationIdentifier;
+enum class DialogButtonStyle : char;
+
+// An object encapsulating the data necessary to set up a dialog button.
+@interface DialogButtonConfiguration : NSObject
+
+// Factory method for item creation.  |text| must be non-empty.
++ (nonnull instancetype)configWithText:(nonnull NSString*)text
+                                 style:(DialogButtonStyle)style;
+
+// DialogTextFieldConfigurations should be created through the factory method.
+- (nonnull instancetype)init NS_UNAVAILABLE;
+
+// The default text to display in the text field, if any.
+@property(nonatomic, readonly, copy, nonnull) NSString* text;
+
+// The placehodler text to display in the text field, if any.
+@property(nonatomic, readonly) DialogButtonStyle style;
+
+// Unique identifier for this DialogButtonConfiguration.
+@property(nonatomic, readonly, strong, nonnull)
+    DialogConfigurationIdentifier* identifier;
+
+@end
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_CONFIGURATION_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.mm
new file mode 100644
index 0000000..6d5fbc8a
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.mm
@@ -0,0 +1,44 @@
+// 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/dialogs/dialog_button_configuration.h"
+
+#include "base/logging.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface DialogButtonConfiguration ()
+
+// Initializer used by the factory method.
+- (instancetype)initWithText:(NSString*)text
+                       style:(DialogButtonStyle)style NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation DialogButtonConfiguration
+
+@synthesize text = _text;
+@synthesize style = _style;
+@synthesize identifier = _identifier;
+
+- (instancetype)initWithText:(NSString*)text style:(DialogButtonStyle)style {
+  DCHECK(text.length);
+  if ((self = [super init])) {
+    _text = [text copy];
+    _style = style;
+    _identifier = [[DialogConfigurationIdentifier alloc] init];
+  }
+  return self;
+}
+
+#pragma mark - Public
+
++ (instancetype)configWithText:(NSString*)text style:(DialogButtonStyle)style {
+  return [[DialogButtonConfiguration alloc] initWithText:text style:style];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration_unittest.mm
new file mode 100644
index 0000000..114acf3e
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration_unittest.mm
@@ -0,0 +1,39 @@
+// 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/dialogs/dialog_button_configuration.h"
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Const values from which to create configurations.
+NSString* const kButtonText = @"button text";
+const DialogButtonStyle kButtonStyle = DialogButtonStyle::DESTRUCTIVE;
+}
+
+// Tests that the values passed to the factory method are reflected in the
+// returned value.
+TEST(DialogButtonConfigurationTest, FactoryMethod) {
+  DialogButtonConfiguration* config =
+      [DialogButtonConfiguration configWithText:kButtonText style:kButtonStyle];
+  EXPECT_NSEQ(kButtonText, config.text);
+  EXPECT_EQ(kButtonStyle, config.style);
+}
+
+// Tests that two DialogButtonConfigurations created with the same values have
+// unequal identifiers.
+TEST(DialogButtonConfigurationTest, Identifiers) {
+  DialogButtonConfiguration* config1 =
+      [DialogButtonConfiguration configWithText:kButtonText style:kButtonStyle];
+  DialogButtonConfiguration* config2 =
+      [DialogButtonConfiguration configWithText:kButtonText style:kButtonStyle];
+  EXPECT_FALSE([config1.identifier isEqual:config2.identifier]);
+}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h b/ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h
new file mode 100644
index 0000000..4bac25c
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h
@@ -0,0 +1,19 @@
+// 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_DIALOGS_DIALOG_BUTTON_STYLE_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_STYLE_H_
+
+// Enum type to describe the style of buttons to use.
+enum class DialogButtonStyle : char {
+  DEFAULT,  // Uses default button styling.
+
+  CANCEL,  // Uses styling to indicates that the button cancels the
+           // current action, and leaves things unchanged.
+
+  DESTRUCTIVE  // Uses styling that indicates that the button might change or
+               // delete data.
+};
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_STYLE_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h
new file mode 100644
index 0000000..d626b74
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h
@@ -0,0 +1,16 @@
+// 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_DIALOGS_DIALOG_CONFIGURATION_IDENTIFIER_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONFIGURATION_IDENTIFIER_H_
+
+#import <Foundation/Foundation.h>
+
+// Identifier objects used to communicate interaction events that occur from UI
+// elements corresponding to dialog configuration objects passed to
+// DialogConsumers.
+@interface DialogConfigurationIdentifier : NSObject<NSCopying>
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONFIGURATION_IDENTIFIER_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.mm
new file mode 100644
index 0000000..5488b1b
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.mm
@@ -0,0 +1,59 @@
+// 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/dialogs/dialog_configuration_identifier.h"
+
+#include "base/atomic_sequence_num.h"
+#import "base/mac/foundation_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+base::AtomicSequenceNumber g_dialog_configuration_identifier;
+}  // namespace
+
+@interface DialogConfigurationIdentifier () {
+  // The unique identifier for this object.
+  int _uniqueID;
+}
+
+// Designated initializer that takes a unique ID.
+- (instancetype)initWithUniqueID:(int)uniqueID;
+
+@end
+
+@implementation DialogConfigurationIdentifier
+
+- (instancetype)init {
+  return [self initWithUniqueID:g_dialog_configuration_identifier.GetNext()];
+}
+
+- (instancetype)initWithUniqueID:(int)uniqueID {
+  if ((self = [super init])) {
+    _uniqueID = uniqueID;
+  }
+  return self;
+}
+
+#pragma mark - NSObject
+
+- (BOOL)isEqual:(id)object {
+  DialogConfigurationIdentifier* identifier =
+      base::mac::ObjCCast<DialogConfigurationIdentifier>(object);
+  return identifier && identifier->_uniqueID == _uniqueID;
+}
+
+- (NSUInteger)hash {
+  return static_cast<NSUInteger>(_uniqueID);
+}
+
+#pragma mark - NSCopying
+
+- (id)copyWithZone:(NSZone*)zone {
+  return [[[self class] allocWithZone:zone] initWithUniqueID:_uniqueID];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier_unittest.mm
new file mode 100644
index 0000000..c14726a
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier_unittest.mm
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Tests that two DialogConfigurationIdentifiers created sequentially are not
+// equal to each other.
+TEST(DialogConfigurationIdentifierTest, NotEqual) {
+  DialogConfigurationIdentifier* ID1 =
+      [[DialogConfigurationIdentifier alloc] init];
+  DialogConfigurationIdentifier* ID2 =
+      [[DialogConfigurationIdentifier alloc] init];
+  EXPECT_FALSE([ID1 isEqual:ID2]);
+}
+
+// Tests that copying a DialogConfigurationIdentifier creates one that is equal
+// to the original.
+TEST(DialogConfigurationIdentifierTest, EqualCopies) {
+  DialogConfigurationIdentifier* identifier =
+      [[DialogConfigurationIdentifier alloc] init];
+  DialogConfigurationIdentifier* copy = [identifier copy];
+  EXPECT_NSEQ(identifier, copy);
+}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h b/ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h
new file mode 100644
index 0000000..699204f
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h
@@ -0,0 +1,35 @@
+// 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_DIALOGS_DIALOG_CONSUMER_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONSUMER_H_
+
+#import <Foundation/Foundation.h>
+
+@class DialogButtonConfiguration;
+@class DialogTextFieldConfiguration;
+
+// A DialogConsumer uses data provided by this protocol to configure and run a
+// dialog.  A DialogConsumer can be configured multiple times before
+// presentation, but once the consumer is presented, the appearance of the
+// dialog will be immutable.
+@protocol DialogConsumer<NSObject>
+
+// Sets the title of the dialog.
+- (void)setDialogTitle:(nullable NSString*)title;
+
+// Sets the message of the dialog.
+- (void)setDialogMessage:(nullable NSString*)message;
+
+// Sets the button items to display in the dialog.
+- (void)setDialogButtonConfigurations:
+    (nullable NSArray<DialogButtonConfiguration*>*)buttonConfigs;
+
+// Sets the text field items to display in the dialog.
+- (void)setDialogTextFieldConfigurations:
+    (nullable NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONSUMER_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h
new file mode 100644
index 0000000..6f564e08
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h
@@ -0,0 +1,27 @@
+// 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_DIALOGS_DIALOG_COORDINATOR_SUBCLASSING_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_SUBCLASSING_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h"
+
+@class DialogMediator;
+
+// Interface used to expose DialogCoordinator configuration to subclasses.
+@interface DialogCoordinator (DialogCoordinatorSubclassing)
+
+// The type of dialog UI that should be started for this coordinator.  Defaults
+// to UIAlertControllerStyleAlert.
+@property(nonatomic, readonly) UIAlertControllerStyle alertStyle;
+
+// The mediator that will be used to configure the DialogConsumer.  The value is
+// expected to return non-nil before DialogCoordinator's |-start| is called.
+@property(nonatomic, readonly) DialogMediator* mediator;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_SUBCLASSING_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h
new file mode 100644
index 0000000..1bc653e
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h
@@ -0,0 +1,18 @@
+// 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_DIALOGS_DIALOG_COORDINATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/clean/chrome/browser/ui/overlays/overlay_coordinator.h"
+
+// A coordinator that displays dialog UI.  This class is meant to be subclassed,
+// and subclasses are expected to implement the interface in the
+// DialogCoordinator+Subclassing category.
+@interface DialogCoordinator : OverlayCoordinator
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.mm
new file mode 100644
index 0000000..8f6f996
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.mm
@@ -0,0 +1,69 @@
+// 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/dialogs/dialog_coordinator.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
+#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface DialogCoordinator ()
+
+// The dispatcher for DialogDismissalCommands.
+@property(nonatomic, readonly) id<DialogDismissalCommands> dismissalDispatcher;
+// The view controller used to display this dialog.
+@property(nonatomic, strong) DialogViewController* viewController;
+
+@end
+
+@implementation DialogCoordinator
+@synthesize viewController = _viewController;
+
+#pragma mark - Accessors
+
+- (id<DialogDismissalCommands>)dismissalDispatcher {
+  return static_cast<id<DialogDismissalCommands>>(self.browser->dispatcher());
+}
+
+#pragma mark - BrowserCoordinator
+
+- (void)start {
+  DCHECK(self.mediator);
+  self.viewController =
+      [[DialogViewController alloc] initWithStyle:self.alertStyle
+                                       dispatcher:self.dismissalDispatcher];
+  [self.mediator updateConsumer:self.viewController];
+  [self.browser->dispatcher()
+      startDispatchingToTarget:self.mediator
+                   forProtocol:@protocol(DialogDismissalCommands)];
+  [super start];
+}
+
+- (void)stop {
+  [self.browser->dispatcher() stopDispatchingToTarget:self.mediator];
+  [super stop];
+}
+
+@end
+
+@implementation DialogCoordinator (DialogCoordinatorSubclassing)
+
+- (UIAlertControllerStyle)alertStyle {
+  return UIAlertControllerStyleAlert;
+}
+
+- (DialogMediator*)mediator {
+  // Implemented by subclasses.
+  NOTREACHED();
+  return nil;
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator_unittest.mm
new file mode 100644
index 0000000..b040c6f
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator_unittest.mm
@@ -0,0 +1,104 @@
+// 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/dialogs/dialog_coordinator.h"
+
+#import "base/mac/foundation_util.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/ui/browser_list/browser.h"
+#import "ios/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h"
+#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h"
+#import "ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h"
+#include "ios/web/public/test/fakes/test_browser_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test version of DialogCoordinator that populates its consumer with the dummy
+// values from TestDialogMediator.
+@interface TestDialogCoordinator : DialogCoordinator
+@property(nonatomic, readonly, strong) TestDialogMediator* testMediator;
+@property(nonatomic, readwrite) UIAlertControllerStyle alertStyle;
+@end
+
+@implementation TestDialogCoordinator
+@synthesize testMediator = _testMediator;
+@synthesize alertStyle = _alertStyle;
+
+- (instancetype)init {
+  if ((self = [super init])) {
+    _testMediator = [[TestDialogMediator alloc] init];
+    _alertStyle = UIAlertControllerStyleAlert;
+  }
+  return self;
+}
+
+@end
+
+@implementation TestDialogCoordinator (DialogCoordinatorSubclassing)
+
+- (DialogMediator*)mediator {
+  return self.testMediator;
+}
+
+@end
+
+// A test fixture for DialogCoordinators.
+class DialogCoordinatorTest : public PlatformTest {
+ public:
+  DialogCoordinatorTest()
+      : PlatformTest(),
+        coordinator_([[TestDialogCoordinator alloc] init]),
+        browser_(ios::ChromeBrowserState::FromBrowserState(&browser_state_)) {
+    // DialogConsumers require at least one button.
+    coordinator_.testMediator.buttonConfigs = @[ [DialogButtonConfiguration
+        configWithText:@"OK"
+                 style:DialogButtonStyle::DEFAULT] ];
+    // Add the coordinator to the queue.
+    queue_.SetBrowser(&browser_);
+    queue_.AddOverlay(coordinator_);
+  }
+
+  ~DialogCoordinatorTest() override { queue_.CancelOverlays(); }
+
+  void StartCoordinator() { queue_.StartNextOverlay(); }
+
+  DialogViewController* alert_controller() {
+    return base::mac::ObjCCastStrict<DialogViewController>(
+        coordinator_.viewController);
+  }
+
+ protected:
+  __strong TestDialogCoordinator* coordinator_;
+  web::TestBrowserState browser_state_;
+  Browser browser_;
+  TestOverlayQueue queue_;
+};
+
+// Tests that the alert style is used by default.
+TEST_F(DialogCoordinatorTest, DefaultStyle) {
+  StartCoordinator();
+  ASSERT_TRUE(alert_controller());
+  EXPECT_EQ(UIAlertControllerStyleAlert, alert_controller().preferredStyle);
+}
+
+// Tests that the action sheet style is used if specified.
+TEST_F(DialogCoordinatorTest, ActionSheetStyle) {
+  coordinator_.alertStyle = UIAlertControllerStyleActionSheet;
+  StartCoordinator();
+  ASSERT_TRUE(alert_controller());
+  EXPECT_EQ(UIAlertControllerStyleActionSheet,
+            alert_controller().preferredStyle);
+}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h
new file mode 100644
index 0000000..3c5cde8b
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h
@@ -0,0 +1,32 @@
+// 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_DIALOGS_DIALOG_MEDIATOR_SUBCLASSING_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_SUBCLASSING_H_
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h"
+
+#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h"
+
+@class DialogButtonConfiguration;
+@class DialogTextFieldConfiguration;
+
+// DialogMediator functionality exposed to subclasses.
+@interface DialogMediator (DialogMediatorSubclassing)
+
+// The title to provide to the consumer.
+- (NSString*)dialogTitle;
+
+// The message to provide to the consumer.
+- (NSString*)dialogMessage;
+
+// The DialogButtonConfigurations to provide to the consumer.
+- (NSArray<DialogButtonConfiguration*>*)buttonConfigs;
+
+// The DialogTextFieldConfigurations to provide to the consumer.
+- (NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_SUBCLASSING_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h
new file mode 100644
index 0000000..9eb25421
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h
@@ -0,0 +1,24 @@
+// 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_DIALOGS_DIALOG_MEDIATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h"
+
+@protocol DialogConsumer;
+
+// Class responsible for setting up a DialogConsumer.  This class is meant to be
+// subclassed, and subclasses are expected to implement the interface in the
+// DialogMediator+Subclassing category.
+@interface DialogMediator : NSObject<DialogDismissalCommands>
+
+// Supplies UI information to |consumer|.
+- (void)updateConsumer:(id<DialogConsumer>)consumer;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.mm
new file mode 100644
index 0000000..aa8585c2
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.mm
@@ -0,0 +1,62 @@
+// 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/dialogs/dialog_mediator.h"
+
+#include "base/logging.h"
+#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation DialogMediator
+
+#pragma mark - Public
+
+- (void)updateConsumer:(id<DialogConsumer>)consumer {
+  [consumer setDialogTitle:[self dialogTitle]];
+  [consumer setDialogMessage:[self dialogMessage]];
+  [consumer setDialogButtonConfigurations:[self buttonConfigs]];
+  [consumer setDialogTextFieldConfigurations:[self textFieldConfigs]];
+}
+
+#pragma mark - DialogDismissalCommands
+
+- (void)dismissDialogWithButtonID:(DialogConfigurationIdentifier*)buttonID
+                  textFieldValues:(DialogTextFieldValues*)textFieldValues {
+  // Implemented by subclasses.
+  NOTREACHED();
+}
+
+@end
+
+@implementation DialogMediator (Subclassing)
+
+- (NSString*)dialogTitle {
+  // Default is no title.
+  return nil;
+}
+
+- (NSString*)dialogMessage {
+  // Default is no message.
+  return nil;
+}
+
+- (NSArray<DialogButtonConfiguration*>*)buttonConfigs {
+  // Implemented by subclasses.  A dialog must have a least one button for
+  // dismissal.
+  NOTREACHED();
+  return nil;
+}
+
+- (NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs {
+  // Default is no text fields.
+  return nil;
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_mediator_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator_unittest.mm
new file mode 100644
index 0000000..c0ea538
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator_unittest.mm
@@ -0,0 +1,74 @@
+// 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/dialogs/dialog_mediator.h"
+
+#import "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h"
+#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h"
+#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h"
+#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h"
+#import "ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h"
+#include "ios/web/public/test/fakes/test_browser_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// A test fixture for DialogMediators.
+class DialogMediatorTest : public PlatformTest {
+ public:
+  DialogMediatorTest()
+      : PlatformTest(),
+        title_(@"title"),
+        message_(@"message"),
+        button_config_([DialogButtonConfiguration
+            configWithText:@"button"
+                     style:DialogButtonStyle::DESTRUCTIVE]),
+        text_field_config_([DialogTextFieldConfiguration
+            configWithDefaultText:@"defaultText"
+                  placeholderText:@"placeholderText"
+                           secure:YES]),
+        mediator_([[TestDialogMediator alloc] init]) {
+    mediator_.title = title_;
+    mediator_.message = message_;
+    mediator_.buttonConfigs = @[ button_config_ ];
+    mediator_.textFieldConfigs = @[ text_field_config_ ];
+  }
+
+ protected:
+  __strong NSString* title_;
+  __strong NSString* message_;
+  __strong DialogButtonConfiguration* button_config_;
+  __strong DialogTextFieldConfiguration* text_field_config_;
+  __strong TestDialogMediator* mediator_;
+};
+
+// Tests that the DialogViewController is populated properly from the
+// TestDialogMediator.
+TEST_F(DialogMediatorTest, ConsumerPopulation) {
+  // Create the consumer and update it with |mediator_|.
+  TestDialogViewController* consumer = [[TestDialogViewController alloc]
+      initWithStyle:UIAlertControllerStyleAlert];
+  [mediator_ updateConsumer:consumer];
+  // Add the view to a container view to ensure that the alert UI is set up.
+  UIView* container =
+      [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
+  consumer.view.frame = container.bounds;
+  [container addSubview:consumer.view];
+  // Verify that the test configuration was properly translated to the alert UI.
+  dialogs_test_util::TestAlertSetup(consumer, title_, message_,
+                                    mediator_.buttonConfigs,
+                                    mediator_.textFieldConfigs);
+}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h
new file mode 100644
index 0000000..d36ff20
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h
@@ -0,0 +1,39 @@
+// 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_DIALOGS_DIALOG_TEXT_FIELD_CONFIGURATION_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_TEXT_FIELD_CONFIGURATION_H_
+
+#import <Foundation/Foundation.h>
+
+@class DialogConfigurationIdentifier;
+
+// An object encapsulating the data necessary to set up a dialog's text field.
+@interface DialogTextFieldConfiguration : NSObject
+
+// Factory method for item creation.
++ (nullable instancetype)configWithDefaultText:(nullable NSString*)defaultText
+                               placeholderText:
+                                   (nullable NSString*)placeholderText
+                                        secure:(BOOL)secure;
+
+// DialogTextFieldConfigurations should be created through the factory method.
+- (nullable instancetype)init NS_UNAVAILABLE;
+
+// The default text to display in the text field, if any.
+@property(nonatomic, readonly, copy, nullable) NSString* defaultText;
+
+// The placehodler text to display in the text field, if any.
+@property(nonatomic, readonly, copy, nullable) NSString* placeholderText;
+
+// Whether the text field should be secure (e.g. for password).
+@property(nonatomic, readonly, getter=isSecure) BOOL secure;
+
+// Unique identifier for this DialogTextFieldConfiguration.
+@property(nonatomic, readonly, strong, nonnull)
+    DialogConfigurationIdentifier* identifier;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_TEXT_FIELD_CONFIGURATION_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.mm
new file mode 100644
index 0000000..fc15d6c
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.mm
@@ -0,0 +1,52 @@
+// 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/dialogs/dialog_text_field_configuration.h"
+
+#include "base/logging.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface DialogTextFieldConfiguration ()
+
+// Initializer used by the factory method.
+- (instancetype)initWithDefaultText:(NSString*)defaultText
+                    placeholderText:(NSString*)placeholderText
+                             secure:(BOOL)secure NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation DialogTextFieldConfiguration
+@synthesize defaultText = _defaultText;
+@synthesize placeholderText = _placeholderText;
+@synthesize secure = _secure;
+@synthesize identifier = _identifier;
+
+- (instancetype)initWithDefaultText:(NSString*)defaultText
+                    placeholderText:(NSString*)placeholderText
+                             secure:(BOOL)secure {
+  if ((self = [super init])) {
+    _defaultText = [defaultText copy];
+    _placeholderText = [placeholderText copy];
+    _secure = secure;
+    _identifier = [[DialogConfigurationIdentifier alloc] init];
+  }
+  return self;
+}
+
+#pragma mark - Public
+
++ (instancetype)configWithDefaultText:(NSString*)defaultText
+                      placeholderText:(NSString*)placeholderText
+                               secure:(BOOL)secure {
+  return
+      [[DialogTextFieldConfiguration alloc] initWithDefaultText:defaultText
+                                                placeholderText:placeholderText
+                                                         secure:secure];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration_unittest.mm
new file mode 100644
index 0000000..a39f90e
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration_unittest.mm
@@ -0,0 +1,46 @@
+// 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/dialogs/dialog_text_field_configuration.h"
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Const values from which to create configurations.
+NSString* const kDefaultText = @"default text";
+NSString* const kPlaceholderText = @"placeholder text";
+const BOOL kSecure = YES;
+}
+
+// Tests that the values passed to the factory method are reflected in the
+// returned value.
+TEST(DialogTextFieldConfigurationTest, FactoryMethod) {
+  DialogTextFieldConfiguration* config =
+      [DialogTextFieldConfiguration configWithDefaultText:kDefaultText
+                                          placeholderText:kPlaceholderText
+                                                   secure:kSecure];
+  EXPECT_NSEQ(kDefaultText, config.defaultText);
+  EXPECT_NSEQ(kPlaceholderText, config.placeholderText);
+  EXPECT_EQ(kSecure, config.secure);
+}
+
+// Tests that two DialogTextFieldConfigurations created with the same values
+// have unequal identifiers.
+TEST(DialogTextFieldConfigurationTest, Identifiers) {
+  DialogTextFieldConfiguration* config1 =
+      [DialogTextFieldConfiguration configWithDefaultText:kDefaultText
+                                          placeholderText:kPlaceholderText
+                                                   secure:kSecure];
+  DialogTextFieldConfiguration* config2 =
+      [DialogTextFieldConfiguration configWithDefaultText:kDefaultText
+                                          placeholderText:kPlaceholderText
+                                                   secure:kSecure];
+  EXPECT_FALSE([config1.identifier isEqual:config2.identifier]);
+}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h
new file mode 100644
index 0000000..a64c678
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h
@@ -0,0 +1,35 @@
+// 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_DIALOGS_DIALOG_VIEW_CONTROLLER_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h"
+
+enum class DialogButtonStyle : char;
+
+@protocol DialogDismissalCommands;
+
+// Class used to display dialogs.  It is configured through the DialogConsumer
+// protocol, and is not meant to be subclassed.
+@interface DialogViewController : UIAlertController<DialogConsumer>
+
+// Initializer for a dialog with |style| that uses |dispatcher| to manage its
+// dismissal.
+// NOTE: The only way to specify a UIAlertController's preferred style is via
+// |+alertControllerWithTitle:message:preferredStyle:|, so this cannot be marked
+// as the designated initializer since the compiler will warn about not calling
+// super's designated initializer.
+- (instancetype)initWithStyle:(UIAlertControllerStyle)style
+                   dispatcher:(id<DialogDismissalCommands>)dispatcher;
+
+// Returns the UIAlertActionStyle used for a UIAlertAction created from a
+// DialogButtonConfiguration with |style|.
++ (UIAlertActionStyle)alertStyleForDialogButtonStyle:(DialogButtonStyle)style;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_VIEW_CONTROLLER_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.mm
new file mode 100644
index 0000000..d8915a8
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.mm
@@ -0,0 +1,165 @@
+// 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/dialogs/dialog_view_controller.h"
+
+#include "base/logging.h"
+#include "components/strings/grit/components_strings.h"
+#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Typedef the block parameter for UIAlertAction for readability.
+typedef void (^AlertActionHandler)(UIAlertAction*);
+}
+
+@interface DialogViewController ()
+
+// The dispatcher used for dismissal;
+@property(nonatomic, readonly, strong) id<DialogDismissalCommands>
+    dismissalDispatcher;
+
+// Objects provided through the DialogConsumer protocol.
+@property(nonatomic, readonly, copy)
+    NSArray<DialogButtonConfiguration*>* buttonConfigs;
+@property(nonatomic, readonly, copy)
+    NSArray<DialogTextFieldConfiguration*>* textFieldConfigs;
+
+// The strings corresponding with the text fields.
+@property(nonatomic, readonly)
+    NSMutableDictionary<DialogConfigurationIdentifier*, NSString*>*
+        textFieldValues;
+
+// Creates an AlertActionHandler that sends a DialogDismissalCommand with |tag|.
+- (AlertActionHandler)actionForButtonConfiguration:
+    (DialogButtonConfiguration*)buttonConfig;
+// Adds buttons for each item in |buttonItems|.
+- (void)addButtons;
+// Adds text fields for each item in |textFieldItems|.
+- (void)addTextFields;
+
+@end
+
+@implementation DialogViewController
+
+@synthesize dismissalDispatcher = _dismissalDispatcher;
+@synthesize buttonConfigs = _buttonConfigs;
+@synthesize textFieldConfigs = _textFieldConfigs;
+@synthesize textFieldValues = _textFieldValues;
+
+- (instancetype)initWithStyle:(UIAlertControllerStyle)style
+                   dispatcher:(id<DialogDismissalCommands>)dispatcher {
+  DCHECK(dispatcher);
+  self = [[self class] alertControllerWithTitle:nil
+                                        message:nil
+                                 preferredStyle:style];
+  if (self) {
+    _dismissalDispatcher = dispatcher;
+  }
+  return self;
+}
+
+#pragma mark - Accessors
+
+- (NSMutableDictionary<DialogConfigurationIdentifier*, NSString*>*)
+    textFieldValues {
+  // Early return if text field items haven't been supplied or the text fields
+  // have not been instantiated.
+  NSUInteger textFieldCount = self.textFieldConfigs.count;
+  if (!textFieldCount || textFieldCount != self.textFields.count)
+    return nil;
+  // Lazily create the array and update its contents.
+  if (!_textFieldValues) {
+    _textFieldValues =
+        [[NSMutableDictionary<DialogConfigurationIdentifier*, NSString*> alloc]
+            init];
+  }
+  for (NSUInteger fieldIndex = 0; fieldIndex < textFieldCount; ++fieldIndex) {
+    _textFieldValues[self.textFieldConfigs[fieldIndex].identifier] =
+        self.textFields[fieldIndex].text;
+  }
+  return _textFieldValues;
+}
+
+#pragma mark - Public
+
++ (UIAlertActionStyle)alertStyleForDialogButtonStyle:(DialogButtonStyle)style {
+  switch (style) {
+    case DialogButtonStyle::DEFAULT:
+      return UIAlertActionStyleDefault;
+    case DialogButtonStyle::CANCEL:
+      return UIAlertActionStyleCancel;
+    case DialogButtonStyle::DESTRUCTIVE:
+      return UIAlertActionStyleDestructive;
+  }
+}
+
+#pragma mark - DialogConsumer
+
+- (void)setDialogTitle:(nullable NSString*)title {
+  self.title = title;
+}
+
+- (void)setDialogMessage:(nullable NSString*)message {
+  self.message = message;
+}
+
+- (void)setDialogButtonConfigurations:
+    (nullable NSArray<DialogButtonConfiguration*>*)buttonConfigs {
+  _buttonConfigs = buttonConfigs;
+}
+
+- (void)setDialogTextFieldConfigurations:
+    (nullable NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs {
+  _textFieldConfigs = textFieldConfigs;
+}
+
+#pragma mark - UIViewcontroller
+
+- (void)viewDidLoad {
+  DCHECK_GT(self.buttonConfigs.count, 0U);
+  [self addButtons];
+  [self addTextFields];
+}
+
+#pragma mark -
+
+- (AlertActionHandler)actionForButtonConfiguration:
+    (DialogButtonConfiguration*)buttonConfig {
+  return ^(UIAlertAction*) {
+    [self.dismissalDispatcher dismissDialogWithButtonID:buttonConfig.identifier
+                                        textFieldValues:self.textFieldValues];
+  };
+}
+
+- (void)addButtons {
+  for (DialogButtonConfiguration* config in self.buttonConfigs) {
+    AlertActionHandler handler = [self actionForButtonConfiguration:config];
+    UIAlertActionStyle style =
+        [[self class] alertStyleForDialogButtonStyle:config.style];
+    [self addAction:[UIAlertAction actionWithTitle:config.text
+                                             style:style
+                                           handler:handler]];
+  }
+}
+
+- (void)addTextFields {
+  for (DialogTextFieldConfiguration* config in self.textFieldConfigs) {
+    [self addTextFieldWithConfigurationHandler:^(UITextField* textField) {
+      textField.text = config.defaultText;
+      textField.placeholder = config.placeholderText;
+      textField.secureTextEntry = config.secure;
+    }];
+  }
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller_unittest.mm
new file mode 100644
index 0000000..d9a2968f
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller_unittest.mm
@@ -0,0 +1,43 @@
+// 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/dialogs/dialog_view_controller.h"
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h"
+#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Tests that the dialog is properly set up via the DialogConsumer API.
+TEST(DialogViewControllerTest, VerifySetup) {
+  NSString* const kTitle = @"Title";
+  NSString* const kMessage = @"message";
+  NSArray* const kButtonConfigs = @[ [DialogButtonConfiguration
+      configWithText:@"OK"
+               style:DialogButtonStyle::DEFAULT] ];
+  NSArray* const kTextFieldConfigs =
+      @[ [DialogTextFieldConfiguration configWithDefaultText:@"default"
+                                             placeholderText:@"placeholder"
+                                                      secure:YES] ];
+  TestDialogViewController* dialog = [[TestDialogViewController alloc]
+      initWithStyle:UIAlertControllerStyleAlert];
+  [dialog setDialogTitle:kTitle];
+  [dialog setDialogMessage:kMessage];
+  [dialog setDialogButtonConfigurations:kButtonConfigs];
+  [dialog setDialogTextFieldConfigurations:kTextFieldConfigs];
+  // Add the view to a container so that the alert UI is set up.
+  UIView* containerView =
+      [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
+  dialog.view.frame = containerView.bounds;
+  [containerView addSubview:dialog.view];
+  // Verify setup.
+  dialogs_test_util::TestAlertSetup(dialog, kTitle, kMessage, kButtonConfigs,
+                                    kTextFieldConfigs);
+}
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/BUILD.gn b/ios/clean/chrome/browser/ui/dialogs/test_helpers/BUILD.gn
new file mode 100644
index 0000000..54b99216
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/BUILD.gn
@@ -0,0 +1,32 @@
+# 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("test_helpers") {
+  testonly = true
+  sources = [
+    "dialog_test_util.h",
+    "dialog_test_util.mm",
+    "test_dialog_mediator.h",
+    "test_dialog_mediator.mm",
+    "test_dialog_view_controller.h",
+    "test_dialog_view_controller.mm",
+  ]
+
+  deps = [
+    "//base",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ui/browser_list",
+    "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/coordinators",
+    "//ios/chrome/browser/web_state_list",
+    "//ios/clean/chrome/browser/ui/commands",
+    "//ios/clean/chrome/browser/ui/dialogs:dialog_coordinator_support",
+    "//ios/clean/chrome/browser/ui/dialogs:dialog_coordinator_support",
+    "//ios/clean/chrome/browser/ui/dialogs:dialogs_ui",
+    "//ios/web",
+    "//testing/gtest",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h b/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h
new file mode 100644
index 0000000..d403e1d9
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_DIALOG_TEST_UTIL_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_DIALOG_TEST_UTIL_H_
+
+#import <Foundation/Foundation.h>
+
+@class DialogButtonConfiguration;
+@class DialogMediator;
+@class DialogTextFieldConfiguration;
+@class DialogViewController;
+
+namespace dialogs_test_util {
+
+// Tests that |view_controller| is set up appropriately for the provided
+// DialogConsumer configuration parameters.
+void TestAlertSetup(DialogViewController* view_controller,
+                    NSString* title,
+                    NSString* message,
+                    NSArray<DialogButtonConfiguration*>* buttons,
+                    NSArray<DialogTextFieldConfiguration*>* fields);
+
+// Tests that |view_controller| is set up appropriately for |mediator|.
+void TestAlertSetup(DialogViewController* view_controller,
+                    DialogMediator* mediator);
+
+}  // namespace dialogs_test_util
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_DIALOG_TEST_UTIL_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.mm b/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.mm
new file mode 100644
index 0000000..3af2f6fc
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.mm
@@ -0,0 +1,54 @@
+// 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/dialogs/test_helpers/dialog_test_util.h"
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h"
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace dialogs_test_util {
+
+void TestAlertSetup(DialogViewController* view_controller,
+                    NSString* title,
+                    NSString* message,
+                    NSArray<DialogButtonConfiguration*>* buttons,
+                    NSArray<DialogTextFieldConfiguration*>* fields) {
+  EXPECT_TRUE(view_controller.viewLoaded);
+  EXPECT_NSEQ(title, view_controller.title);
+  EXPECT_NSEQ(message, view_controller.message);
+  ASSERT_EQ(view_controller.actions.count, buttons.count);
+  for (NSUInteger index = 0; index < buttons.count; ++index) {
+    UIAlertAction* button_action = view_controller.actions[index];
+    DialogButtonConfiguration* button_config = buttons[index];
+    EXPECT_NSEQ(button_config.text, button_action.title);
+    UIAlertActionStyle button_style = [DialogViewController
+        alertStyleForDialogButtonStyle:button_config.style];
+    EXPECT_EQ(button_style, button_action.style);
+  }
+  ASSERT_EQ(view_controller.textFields.count, fields.count);
+  for (NSUInteger index = 0; index < fields.count; ++index) {
+    UITextField* text_field = view_controller.textFields[index];
+    DialogTextFieldConfiguration* field_config = fields[index];
+    EXPECT_NSEQ(field_config.defaultText, text_field.text);
+    EXPECT_NSEQ(field_config.placeholderText, text_field.placeholder);
+    EXPECT_EQ(field_config.secure, text_field.secureTextEntry);
+  }
+}
+
+void TestAlertSetup(DialogViewController* view_controller,
+                    DialogMediator* mediator) {
+  TestAlertSetup(view_controller, [mediator dialogTitle],
+                 [mediator dialogMessage], [mediator buttonConfigs],
+                 [mediator textFieldConfigs]);
+}
+
+}  // namespace dialogs_test_util
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h
new file mode 100644
index 0000000..0d5bb32a
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h
@@ -0,0 +1,22 @@
+// 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_DIALOGS_TEST_HELPERS_TEST_DIALOG_MEDIATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_MEDIATOR_H_
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h"
+
+@class DialogButtonConfiguration;
+@class DialogTextFieldConfiguration;
+
+// A DialogMediator subclass that can be configured via properties.
+@interface TestDialogMediator : DialogMediator
+@property(nonatomic, copy) NSString* title;
+@property(nonatomic, copy) NSString* message;
+@property(nonatomic, copy) NSArray<DialogButtonConfiguration*>* buttonConfigs;
+@property(nonatomic, copy)
+    NSArray<DialogTextFieldConfiguration*>* textFieldConfigs;
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_MEDIATOR_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.mm b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.mm
new file mode 100644
index 0000000..871f900
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.mm
@@ -0,0 +1,38 @@
+// 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/dialogs/test_helpers/test_dialog_mediator.h"
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation TestDialogMediator
+@synthesize title = _title;
+@synthesize message = _message;
+@synthesize buttonConfigs = _buttonConfigs;
+@synthesize textFieldConfigs = _textFieldConfigs;
+@end
+
+@implementation TestDialogMediator (DialogMediatorSubclassing)
+
+- (NSString*)dialogTitle {
+  return _title;
+}
+
+- (NSString*)dialogMessage {
+  return _message;
+}
+
+- (NSArray<DialogButtonConfiguration*>*)buttonConfigs {
+  return _buttonConfigs;
+}
+
+- (NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs {
+  return _textFieldConfigs;
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h
new file mode 100644
index 0000000..c1ad881
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h
@@ -0,0 +1,18 @@
+// 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_DIALOGS_TEST_HELPERS_TEST_DIALOG_VIEW_CONTROLLER_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_VIEW_CONTROLLER_H_
+
+#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h"
+
+// A test version of DialogViewController that doesn't dispatch commands.
+@interface TestDialogViewController : DialogViewController
+
+// A DialogViewController with |style|.
+- (instancetype)initWithStyle:(UIAlertControllerStyle)style;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_VIEW_CONTROLLER_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.mm b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.mm
new file mode 100644
index 0000000..fb046c5
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.mm
@@ -0,0 +1,46 @@
+// 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/dialogs/test_helpers/test_dialog_view_controller.h"
+
+#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Helper object that performs no-ops for DialogDismissalCommands.
+@interface DummyDialogDismissalDispatcher : NSObject<DialogDismissalCommands>
+@end
+
+@implementation DummyDialogDismissalDispatcher
+- (void)
+dismissDialogWithButtonID:(nonnull DialogConfigurationIdentifier*)buttonID
+          textFieldValues:(nonnull DialogTextFieldValues*)textFieldValues {
+}
+@end
+
+@interface TestDialogViewController ()
+@property(nonatomic, readonly, strong)
+    DummyDialogDismissalDispatcher* dispatcher;
+@end
+
+@implementation TestDialogViewController
+@synthesize dispatcher = _dispatcher;
+
+- (instancetype)initWithStyle:(UIAlertControllerStyle)style {
+  DummyDialogDismissalDispatcher* dispatcher =
+      [[DummyDialogDismissalDispatcher alloc] init];
+  if ((self = [super initWithStyle:style dispatcher:dispatcher])) {
+    _dispatcher = dispatcher;
+  }
+  return self;
+}
+
+- (instancetype)initWithStyle:(UIAlertControllerStyle)style
+                   dispatcher:(id<DialogDismissalCommands>)dispatcher {
+  return [self initWithStyle:style];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
index 29ebf7d..59e294ca 100644
--- a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
@@ -15,7 +15,6 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.h"
 #import "ios/chrome/browser/content_suggestions/content_suggestions_mediator.h"
-#import "ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h"
 #import "ios/chrome/browser/content_suggestions/ntp_home_metrics.h"
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h"
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
@@ -68,7 +67,6 @@
 @property(nonatomic, strong) ContentSuggestionsViewController* viewController;
 @property(nonatomic, strong)
     ContentSuggestionsHeaderSynchronizer* headerCollectionInteractionHandler;
-@property(nonatomic, strong) ContentSuggestionsMetricsRecorder* metricsRecorder;
 
 @end
 
@@ -81,7 +79,6 @@
 @synthesize suggestionsMediator = _suggestionsMediator;
 @synthesize headerCollectionInteractionHandler =
     _headerCollectionInteractionHandler;
-@synthesize metricsRecorder = _metricsRecorder;
 
 #pragma mark - BrowserCoordinator
 
@@ -143,15 +140,11 @@
   self.suggestionsMediator.headerProvider =
       self.headerCoordinator.headerProvider;
 
-  self.metricsRecorder = [[ContentSuggestionsMetricsRecorder alloc] init];
-  self.metricsRecorder.delegate = self.suggestionsMediator;
-
   [self.viewController setDataSource:self.suggestionsMediator];
   self.viewController.suggestionCommandHandler = self;
   self.viewController.suggestionsDelegate =
       self.headerCoordinator.collectionDelegate;
   self.viewController.audience = self;
-  self.viewController.metricsRecorder = self.metricsRecorder;
 
   [self.viewController
       addChildViewController:self.headerCoordinator.viewController];
@@ -186,7 +179,7 @@
   // TODO: implement this.
 }
 
-- (void)openPageForItemAtIndexPath:(NSIndexPath*)indexPath {
+- (void)openPageForItem:(CollectionViewItem*)item {
   // TODO: implement this.
 }
 
@@ -201,14 +194,6 @@
                         readLaterAction:(BOOL)readLaterAction {
   ContentSuggestionsItem* suggestionsItem =
       base::mac::ObjCCastStrict<ContentSuggestionsItem>(item);
-
-  [self.metricsRecorder
-      onMenuOpenedForSuggestion:suggestionsItem
-                    atIndexPath:indexPath
-          suggestionsShownAbove:[self.viewController
-                                    numberOfSuggestionsAbove:indexPath
-                                                                 .section]];
-
   self.alertCoordinator = [ContentSuggestionsAlertFactory
       alertCoordinatorForSuggestionItem:suggestionsItem
                        onViewController:self.viewController
@@ -256,7 +241,11 @@
 }
 
 - (void)addItemToReadingList:(ContentSuggestionsItem*)item {
-  // TODO: implement this.
+  base::RecordAction(base::UserMetricsAction("MobileReadingListAdd"));
+  ReadingListModel* readingModel = ReadingListModelFactory::GetForBrowserState(
+      self.browser->browser_state());
+  readingModel->AddEntry(item.URL, base::SysNSStringToUTF8(item.title),
+                         reading_list::ADDED_VIA_CURRENT_APP);
 }
 
 - (void)dismissSuggestion:(ContentSuggestionsItem*)item
@@ -268,6 +257,7 @@
         [self.viewController.collectionViewModel indexPathForItem:item];
   }
 
+  // TODO(crbug.com/691979): Add metrics.
   [self.suggestionsMediator dismissSuggestion:item.suggestionIdentifier];
   [self.viewController dismissEntryAtIndexPath:itemIndexPath];
 }
diff --git a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_parent_coordinator.mm b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_parent_coordinator.mm
index 63ae4fa..9eab14f 100644
--- a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_parent_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_parent_coordinator.mm
@@ -15,20 +15,34 @@
 #endif
 
 @interface TestOverlayParentCoordinator ()
+// The parent view controller's window.  Necessary for UIViewControllers to
+// present properly.
+@property(nonatomic, readonly, strong) UIWindow* window;
 // The parent UIViewController.
-@property(nonatomic, readonly) UIViewController* viewController;
+@property(nonatomic, readonly, strong) UIViewController* viewController;
 @end
 
 @implementation TestOverlayParentCoordinator
+@synthesize window = _window;
 @synthesize viewController = _viewController;
 
 - (instancetype)init {
   if ((self = [super init])) {
+    _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
     _viewController = [[UIViewController alloc] init];
+    _window.rootViewController = _viewController;
+    [_window makeKeyAndVisible];
   }
   return self;
 }
 
+- (void)dealloc {
+  [_window removeFromSuperview];
+  [_window resignKeyWindow];
+}
+
+#pragma mark - Public
+
 - (OverlayCoordinator*)presentedOverlay {
   NSUInteger childCount = self.children.count;
   DCHECK_LE(childCount, 1U);
@@ -38,3 +52,17 @@
 }
 
 @end
+
+@implementation TestOverlayParentCoordinator (Internal)
+
+- (void)childCoordinatorDidStart:(BrowserCoordinator*)childCoordinator {
+  [self.viewController presentViewController:childCoordinator.viewController
+                                    animated:NO
+                                  completion:nil];
+}
+
+- (void)childCoordinatorWillStop:(BrowserCoordinator*)childCoordinator {
+  [self.viewController dismissViewControllerAnimated:NO completion:nil];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h
index 65849b6..1b3a263 100644
--- a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h
+++ b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h
@@ -7,6 +7,7 @@
 
 #import "ios/clean/chrome/browser/ui/overlays/overlay_queue.h"
 
+class Browser;
 @class BrowserCoordinator;
 @class OverlayCoordinator;
 
@@ -18,9 +19,16 @@
   void StartNextOverlay() override;
   void AddOverlay(OverlayCoordinator* overlay);
 
+  // Seting the Browser also sets the Browser of |parent_|, which will be passed
+  // along to OverlayCoordinators presented by this queue when they are started.
+  Browser* browser() const { return browser_; }
+  void SetBrowser(Browser* browser);
+
  private:
   // The coordinator to use as the parent for overlays added via AddOverlay().
   __strong BrowserCoordinator* parent_;
+  // The Browser to use, if any.
+  Browser* browser_;
 
   DISALLOW_COPY_AND_ASSIGN(TestOverlayQueue);
 };
diff --git a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.mm b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.mm
index 28db4a0..80797a1 100644
--- a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.mm
+++ b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.mm
@@ -23,3 +23,8 @@
 void TestOverlayQueue::AddOverlay(OverlayCoordinator* overlay) {
   OverlayQueue::AddOverlay(overlay);
 }
+
+void TestOverlayQueue::SetBrowser(Browser* browser) {
+  browser_ = browser;
+  parent_.browser = browser;
+}
diff --git a/ios/clean/chrome/test/BUILD.gn b/ios/clean/chrome/test/BUILD.gn
index 9035a2d..b9cf0ca5 100644
--- a/ios/clean/chrome/test/BUILD.gn
+++ b/ios/clean/chrome/test/BUILD.gn
@@ -24,6 +24,7 @@
     "//ios/clean/chrome/app/steps:unit_tests",
     "//ios/clean/chrome/browser/ui/bookmarks:unit_tests",
     "//ios/clean/chrome/browser/ui/context_menu:unit_tests",
+    "//ios/clean/chrome/browser/ui/dialogs:unit_tests",
     "//ios/clean/chrome/browser/ui/find_in_page:unit_tests",
     "//ios/clean/chrome/browser/ui/ntp:unit_tests",
     "//ios/clean/chrome/browser/ui/omnibox:unit_tests",
diff --git a/ios/showcase/content_suggestions/sc_content_suggestions_item.mm b/ios/showcase/content_suggestions/sc_content_suggestions_item.mm
index c2c6a7af..ab193be7 100644
--- a/ios/showcase/content_suggestions/sc_content_suggestions_item.mm
+++ b/ios/showcase/content_suggestions/sc_content_suggestions_item.mm
@@ -21,7 +21,6 @@
 @synthesize publisher;
 @synthesize publicationDate;
 @synthesize availableOffline;
-@synthesize metricsRecorded;
 
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
diff --git a/ios/showcase/content_suggestions/sc_content_suggestions_most_visited_item.mm b/ios/showcase/content_suggestions/sc_content_suggestions_most_visited_item.mm
index a457c687..3a81f63 100644
--- a/ios/showcase/content_suggestions/sc_content_suggestions_most_visited_item.mm
+++ b/ios/showcase/content_suggestions/sc_content_suggestions_most_visited_item.mm
@@ -16,7 +16,6 @@
 @synthesize attributes;
 @synthesize suggestionIdentifier;
 @synthesize title;
-@synthesize metricsRecorded;
 
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index d5294db9..42621399 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -151,6 +151,9 @@
   sources = [
     "ipc.mojom",
   ]
+  public_deps = [
+    "//mojo/common:read_only_buffer",
+  ]
 }
 
 mojom("test_interfaces") {
diff --git a/ipc/ipc.mojom b/ipc/ipc.mojom
index f986c47..3ab4d4ef 100644
--- a/ipc/ipc.mojom
+++ b/ipc/ipc.mojom
@@ -4,6 +4,8 @@
 
 module IPC.mojom;
 
+import "mojo/common/read_only_buffer.mojom";
+
 // NOTE: This MUST match the value of MSG_ROUTING_NONE in src/ipc/ipc_message.h.
 const int32 kRoutingIdNone = -2;
 
@@ -31,7 +33,8 @@
   SetPeerPid(int32 pid);
 
   // Transmits a classical Chrome IPC message.
-  Receive(array<uint8> data, array<SerializedHandle>? handles);
+  Receive(mojo.common.mojom.ReadOnlyBuffer data,
+          array<SerializedHandle>? handles);
 
   // Requests a Channel-associated interface.
   GetAssociatedInterface(string name, associated GenericInterface& request);
diff --git a/ipc/ipc_message_pipe_reader.cc b/ipc/ipc_message_pipe_reader.cc
index cd174a25..8b22681 100644
--- a/ipc/ipc_message_pipe_reader.cc
+++ b/ipc/ipc_message_pipe_reader.cc
@@ -60,15 +60,12 @@
   if (result != MOJO_RESULT_OK)
     return false;
 
-  std::vector<uint8_t> data(message->size());
-  std::copy(reinterpret_cast<const uint8_t*>(message->data()),
-            reinterpret_cast<const uint8_t*>(message->data()) + message->size(),
-            data.data());
-
   if (!sender_)
     return false;
 
-  sender_->Receive(data, std::move(handles));
+  sender_->Receive(base::make_span(static_cast<const uint8_t*>(message->data()),
+                                   message->size()),
+                   std::move(handles));
 
   DVLOG(4) << "Send " << message->type() << ": " << message->size();
   return true;
@@ -88,7 +85,7 @@
 }
 
 void MessagePipeReader::Receive(
-    const std::vector<uint8_t>& data,
+    base::span<const uint8_t> data,
     base::Optional<std::vector<mojom::SerializedHandlePtr>> handles) {
   Message message(
       data.empty() ? "" : reinterpret_cast<const char*>(data.data()),
diff --git a/ipc/ipc_message_pipe_reader.h b/ipc/ipc_message_pipe_reader.h
index 3fca2bb..b2f5044 100644
--- a/ipc/ipc_message_pipe_reader.h
+++ b/ipc/ipc_message_pipe_reader.h
@@ -94,7 +94,7 @@
   // mojom::Channel:
   void SetPeerPid(int32_t peer_pid) override;
   void Receive(
-      const std::vector<uint8_t>& data,
+      base::span<const uint8_t> data,
       base::Optional<std::vector<mojom::SerializedHandlePtr>> handles) override;
   void GetAssociatedInterface(
       const std::string& name,
diff --git a/ipc/ipc_mojo_bootstrap_unittest.cc b/ipc/ipc_mojo_bootstrap_unittest.cc
index c6cde3e..52e261f 100644
--- a/ipc/ipc_mojo_bootstrap_unittest.cc
+++ b/ipc/ipc_mojo_bootstrap_unittest.cc
@@ -42,7 +42,7 @@
     on_peer_pid_set_.Run();
   }
 
-  void Receive(const std::vector<uint8_t>& data,
+  void Receive(base::span<const uint8_t> data,
                base::Optional<std::vector<IPC::mojom::SerializedHandlePtr>>
                    handles) override {}
 
diff --git a/mash/catalog_viewer/catalog_viewer.cc b/mash/catalog_viewer/catalog_viewer.cc
index 5439b05..3d8c406 100644
--- a/mash/catalog_viewer/catalog_viewer.cc
+++ b/mash/catalog_viewer/catalog_viewer.cc
@@ -53,8 +53,7 @@
     SetBorder(views::CreateEmptyBorder(gfx::Insets(kPadding)));
     SetBackground(views::CreateStandardPanelBackground());
 
-    views::GridLayout* layout = new views::GridLayout(this);
-    SetLayoutManager(layout);
+    views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
 
     views::ColumnSet* columns = layout->AddColumnSet(0);
     columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
diff --git a/mash/example/window_type_launcher/window_type_launcher.cc b/mash/example/window_type_launcher/window_type_launcher.cc
index 0a9f0be..6ad2aa3 100644
--- a/mash/example/window_type_launcher/window_type_launcher.cc
+++ b/mash/example/window_type_launcher/window_type_launcher.cc
@@ -275,8 +275,7 @@
             MdTextButton::Create(this, base::ASCIIToUTF16("Jank for (s):"))),
         jank_duration_field_(new views::Textfield) {
     SetBorder(views::CreateEmptyBorder(gfx::Insets(5)));
-    views::GridLayout* layout = new views::GridLayout(this);
-    SetLayoutManager(layout);
+    views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
     views::ColumnSet* column_set = layout->AddColumnSet(0);
     column_set->AddColumn(views::GridLayout::LEADING,
                           views::GridLayout::CENTER,
diff --git a/media/base/android/android_overlay.cc b/media/base/android/android_overlay.cc
index e24c39c..fb05573 100644
--- a/media/base/android/android_overlay.cc
+++ b/media/base/android/android_overlay.cc
@@ -7,7 +7,12 @@
 namespace media {
 
 AndroidOverlay::AndroidOverlay() : weak_factory_(this) {}
-AndroidOverlay::~AndroidOverlay() {}
+AndroidOverlay::~AndroidOverlay() {
+  // Don't permit any other callbacks once we start sending deletion cbs.
+  weak_factory_.InvalidateWeakPtrs();
+  for (auto& cb : deletion_cbs_)
+    std::move(cb).Run(this);
+}
 
 void AndroidOverlay::AddSurfaceDestroyedCallback(
     AndroidOverlayConfig::DestroyedCB cb) {
@@ -33,4 +38,9 @@
   }
 }
 
+void AndroidOverlay::AddOverlayDeletedCallback(
+    AndroidOverlayConfig::DeletedCB cb) {
+  deletion_cbs_.push_back(std::move(cb));
+}
+
 }  // namespace media
diff --git a/media/base/android/android_overlay.h b/media/base/android/android_overlay.h
index c94e963..05fef5d 100644
--- a/media/base/android/android_overlay.h
+++ b/media/base/android/android_overlay.h
@@ -60,12 +60,16 @@
   // These will be called in the same order that they're added.
   void AddSurfaceDestroyedCallback(AndroidOverlayConfig::DestroyedCB cb);
 
+  // Add a callback to notify when |this| has been deleted.
+  void AddOverlayDeletedCallback(AndroidOverlayConfig::DeletedCB cb);
+
  protected:
   AndroidOverlay();
 
   void RunSurfaceDestroyedCallbacks();
 
   std::list<AndroidOverlayConfig::DestroyedCB> destruction_cbs_;
+  std::list<AndroidOverlayConfig::DeletedCB> deletion_cbs_;
 
   base::WeakPtrFactory<AndroidOverlay> weak_factory_;
 
diff --git a/media/base/android_overlay_config.h b/media/base/android_overlay_config.h
index 4286114a..1759d0ef 100644
--- a/media/base/android_overlay_config.h
+++ b/media/base/android_overlay_config.h
@@ -21,13 +21,25 @@
   using ReadyCB = base::OnceCallback<void(AndroidOverlay*)>;
 
   // Called when overlay has failed before |ReadyCB| is called.  Will not be
-  // called after ReadyCB.  It will be the last callback for the overlay.
+  // called after ReadyCB.  It will be the last callback for the overlay, except
+  // for any DeletedCB.
   using FailedCB = base::OnceCallback<void(AndroidOverlay*)>;
 
-  // Called when the overlay has been destroyed.  This will not be called unless
-  // ReadyCB has been called.  It will be the last callback for the overlay.
+  // Called when the surface has been destroyed.  This will not be called unless
+  // ReadyCB has been called.  It will be the last callback for the overlay,
+  // except for any DeletedCB.  In response, the client is expected to quit
+  // using the surface and delete the overlay object.
   using DestroyedCB = base::OnceCallback<void(AndroidOverlay*)>;
 
+  // Called when the overlay object has been deleted.  This is unrelated to
+  // any DestroyedCB, which informs us when the surface is no longer usable and
+  // that we should delete the AndroidOverlay object.  DeletedCB is called when
+  // the AndroidOverlay object is deleted for any reason.  It is guaranteed that
+  // the overlay pointer will still be a valid pointer, but it is not safe to
+  // access it.  It's provided just to make it easier to tell which overlay is
+  // being deleted.
+  using DeletedCB = base::OnceCallback<void(AndroidOverlay*)>;
+
   // Configuration used to create an overlay.
   AndroidOverlayConfig();
   AndroidOverlayConfig(AndroidOverlayConfig&&);
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index fa6cd87..cbbb590ff 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -654,8 +654,11 @@
     //   lost frames if we were to fallback then).
     pending_buffers_.clear();
 
+    const DecoderConfig& config = StreamTraits::GetDecoderConfig(stream_);
+    traits_.OnConfigChanged(config);
+
     if (!config_change_observer_cb_.is_null())
-      config_change_observer_cb_.Run(StreamTraits::GetDecoderConfig(stream_));
+      config_change_observer_cb_.Run(config);
 
     state_ = STATE_FLUSHING_DECODER;
     if (!reset_cb_.is_null()) {
diff --git a/media/filters/decoder_stream_traits.cc b/media/filters/decoder_stream_traits.cc
index 68b1b1a..e103ddf5 100644
--- a/media/filters/decoder_stream_traits.cc
+++ b/media/filters/decoder_stream_traits.cc
@@ -84,6 +84,13 @@
   audio_ts_validator_->RecordOutputDuration(buffer);
 }
 
+void DecoderStreamTraits<DemuxerStream::AUDIO>::OnConfigChanged(
+    const DecoderConfigType& config) {
+  // Reset validator with the latest config. Also ensures that we do not attempt
+  // to match timestamps across config boundaries.
+  audio_ts_validator_.reset(new AudioTimestampValidator(config, media_log_));
+}
+
 // Video decoder stream traits implementation.
 
 // static
diff --git a/media/filters/decoder_stream_traits.h b/media/filters/decoder_stream_traits.h
index e84e37da..58878f4 100644
--- a/media/filters/decoder_stream_traits.h
+++ b/media/filters/decoder_stream_traits.h
@@ -56,6 +56,7 @@
   void OnDecode(const scoped_refptr<DecoderBuffer>& buffer);
   void OnDecodeDone(const scoped_refptr<OutputType>& buffer);
   void OnStreamReset(DemuxerStream* stream);
+  void OnConfigChanged(const DecoderConfigType& config);
 
  private:
   // Validates encoded timestamps match decoded output duration. MEDIA_LOG warns
@@ -93,6 +94,7 @@
   void OnDecode(const scoped_refptr<DecoderBuffer>& buffer);
   void OnDecodeDone(const scoped_refptr<OutputType>& buffer) {}
   void OnStreamReset(DemuxerStream* stream);
+  void OnConfigChanged(const DecoderConfigType& config) {}
 
  private:
   base::TimeDelta last_keyframe_timestamp_;
diff --git a/media/gpu/android_video_surface_chooser_impl.cc b/media/gpu/android_video_surface_chooser_impl.cc
index d2f6ae8c..06839d1 100644
--- a/media/gpu/android_video_surface_chooser_impl.cc
+++ b/media/gpu/android_video_surface_chooser_impl.cc
@@ -154,6 +154,13 @@
 }
 
 void AndroidVideoSurfaceChooserImpl::SwitchToSurfaceTexture() {
+  // Invalidate any outstanding deletion callbacks for any overlays that we've
+  // provided to the client already.  We assume that it will eventually drop
+  // them in response to the callback.  Ready / failed callbacks aren't affected
+  // by this, since we own the overlay until those occur.  We're about to
+  // drop |overlay_|, if we have one, which cancels them.
+  weak_factory_.InvalidateWeakPtrs();
+
   // Cancel any outstanding overlay request, in case we're switching to overlay.
   if (overlay_)
     overlay_ = nullptr;
@@ -181,6 +188,11 @@
   // We don't modify |client_overlay_state_| yet, since we don't call the client
   // back yet.
 
+  // Invalidate any outstanding callbacks.  This is needed only for the deletion
+  // callback, since for ready/failed callbacks, we still have ownership of the
+  // object.  If we delete the object, then callbacks are cancelled anyway.
+  weak_factory_.InvalidateWeakPtrs();
+
   AndroidOverlayConfig config;
   // We bind all of our callbacks with weak ptrs, since we don't know how long
   // the client will hold on to overlays.  They could, in principle, show up
@@ -206,6 +218,12 @@
   // back yet.
   DCHECK_EQ(overlay, overlay_.get());
 
+  // Notify the overlay that we'd like to know if it's destroyed, so that we can
+  // update our internal state if the client drops it without being told.
+  overlay_->AddOverlayDeletedCallback(
+      base::Bind(&AndroidVideoSurfaceChooserImpl::OnOverlayDeleted,
+                 weak_factory_.GetWeakPtr()));
+
   client_overlay_state_ = kUsingOverlay;
   use_overlay_cb_.Run(std::move(overlay_));
 }
@@ -225,4 +243,10 @@
   SwitchToSurfaceTexture();
 }
 
+void AndroidVideoSurfaceChooserImpl::OnOverlayDeleted(AndroidOverlay* overlay) {
+  client_overlay_state_ = kUsingSurfaceTexture;
+  // We don't call SwitchToSurfaceTexture since the client dropped the overlay.
+  // It's already using SurfaceTexture.
+}
+
 }  // namespace media
diff --git a/media/gpu/android_video_surface_chooser_impl.h b/media/gpu/android_video_surface_chooser_impl.h
index 74a2979..d993473 100644
--- a/media/gpu/android_video_surface_chooser_impl.h
+++ b/media/gpu/android_video_surface_chooser_impl.h
@@ -52,6 +52,7 @@
   // AndroidOverlay callbacks.
   void OnOverlayReady(AndroidOverlay*);
   void OnOverlayFailed(AndroidOverlay*);
+  void OnOverlayDeleted(AndroidOverlay*);
 
   // Client callbacks.
   UseOverlayCB use_overlay_cb_;
diff --git a/media/gpu/android_video_surface_chooser_impl_unittest.cc b/media/gpu/android_video_surface_chooser_impl_unittest.cc
index 69e811d0..958ba4f 100644
--- a/media/gpu/android_video_surface_chooser_impl_unittest.cc
+++ b/media/gpu/android_video_surface_chooser_impl_unittest.cc
@@ -43,6 +43,11 @@
   // Note that this won't clear |overlay_|, which is helpful.
   MOCK_METHOD0(UseSurfaceTexture, void(void));
 
+  // Let the test have the overlay.
+  std::unique_ptr<AndroidOverlay> ReleaseOverlay() {
+    return std::move(overlay_);
+  }
+
  private:
   std::unique_ptr<AndroidOverlay> overlay_;
 };
@@ -299,6 +304,34 @@
   overlay_callbacks_.OverlayReady.Run();
 }
 
+TEST_F(AndroidVideoSurfaceChooserImplTest,
+       UpdateStateAfterDeleteRetriesOverlay) {
+  // Make sure that SurfaceChooser notices that we delete the overlay, and have
+  // switched back to SurfaceTexture mode.
+
+  chooser_state_.is_fullscreen = true;
+  EXPECT_CALL(*this, MockOnOverlayCreated());
+  StartChooser(FactoryFor(std::move(overlay_)));
+  EXPECT_CALL(client_, UseOverlay(NotNull()));
+  overlay_callbacks_.OverlayReady.Run();
+
+  // Delete the overlay.
+  destruction_observer_ = nullptr;
+  client_.ReleaseOverlay();
+
+  // Force chooser to choose again.  We expect that it will retry the overlay,
+  // since the delete should have informed it that we've switched back to
+  // SurfaceTexture without a callback from SurfaceChooser.  If it didn't know
+  // this, then it would think that the client is still using an overlay, and
+  // take no action.
+
+  // Note that if it enforces a delay here before retrying, that might be okay
+  // too.  For now, we assume that it doesn't.
+  EXPECT_CALL(*this, MockOnOverlayCreated());
+  chooser_->UpdateState(base::Optional<AndroidOverlayFactoryCB>(),
+                        chooser_state_);
+}
+
 TEST_P(AndroidVideoSurfaceChooserImplTest, OverlayIsUsedOrNotBasedOnState) {
   // Provide a factory, and verify that it is used when the state says that it
   // should be.  If the overlay is used, then we also verify that it does not
diff --git a/media/gpu/surface_texture_gl_owner_unittest.cc b/media/gpu/surface_texture_gl_owner_unittest.cc
index 1eee962f..4db4eea 100644
--- a/media/gpu/surface_texture_gl_owner_unittest.cc
+++ b/media/gpu/surface_texture_gl_owner_unittest.cc
@@ -34,7 +34,7 @@
  protected:
   void SetUp() override {
     gl::init::InitializeGLOneOffImplementation(gl::kGLImplementationEGLGLES2,
-                                               false, false, false);
+                                               false, false, false, true);
     surface_ = new gl::PbufferGLSurfaceEGL(gfx::Size(320, 240));
     surface_->Initialize();
 
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 1710d02..aafb52f 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -24,6 +24,7 @@
 #include "media/base/media.h"
 #include "media/base/media_switches.h"
 #include "media/base/media_tracks.h"
+#include "media/base/mock_media_log.h"
 #include "media/base/test_data_util.h"
 #include "media/base/timestamp_constants.h"
 #include "media/cdm/aes_decryptor.h"
@@ -76,11 +77,12 @@
 #define MAYBE_TEXT(test) test
 #endif
 
-using testing::_;
-using testing::AnyNumber;
-using testing::AtLeast;
-using testing::AtMost;
-using testing::SaveArg;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::AtMost;
+using ::testing::HasSubstr;
+using ::testing::SaveArg;
 
 namespace media {
 
@@ -1202,6 +1204,41 @@
   Stop();
 }
 
+TEST_F(PipelineIntegrationTest, MediaSource_AudioConfigChange_WebM) {
+  MockMediaSource source("bear-320x240-audio-only.webm", kAudioOnlyWebM,
+                         kAppendWholeFile);
+  EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+
+  const int kNewSampleRate = 48000;
+  EXPECT_CALL(*this,
+              OnAudioConfigChange(::testing::Property(
+                  &AudioDecoderConfig::samples_per_second, kNewSampleRate)))
+      .Times(1);
+
+  // A higher sample rate will cause the audio buffer durations to change. This
+  // should not manifest as a timestamp gap in AudioTimestampValidator.
+  // Timestamp expectations should be reset across config changes.
+  EXPECT_MEDIA_LOG(Not(HasSubstr("Large timestamp gap detected")))
+      .Times(AnyNumber());
+
+  scoped_refptr<DecoderBuffer> second_file =
+      ReadTestDataFile("bear-320x240-audio-only-48khz.webm");
+  ASSERT_TRUE(source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec),
+                                  second_file->data(),
+                                  second_file->data_size()));
+  source.EndOfStream();
+
+  Play();
+  EXPECT_TRUE(WaitUntilOnEnded());
+
+  EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+  EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+  EXPECT_EQ(3773, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+  source.Shutdown();
+  Stop();
+}
+
 TEST_F(PipelineIntegrationTest, MediaSource_Remove_Updates_BufferedRanges) {
   const char* input_filename = "bear-320x240.webm";
   MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
diff --git a/media/test/pipeline_integration_test_base.h b/media/test/pipeline_integration_test_base.h
index 19c199b..11e62e82 100644
--- a/media/test/pipeline_integration_test_base.h
+++ b/media/test/pipeline_integration_test_base.h
@@ -15,6 +15,7 @@
 #include "media/audio/clockless_audio_sink.h"
 #include "media/audio/null_audio_sink.h"
 #include "media/base/demuxer.h"
+#include "media/base/mock_media_log.h"
 #include "media/base/null_video_sink.h"
 #include "media/base/pipeline_impl.h"
 #include "media/base/pipeline_status.h"
@@ -25,6 +26,8 @@
 #include "media/renderers/video_renderer_impl.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::testing::NiceMock;
+
 namespace media {
 
 class FakeEncryptedMedia;
@@ -150,7 +153,7 @@
       CreateAudioDecodersCB prepend_audio_decoders_cb);
 
  protected:
-  MediaLog media_log_;
+  NiceMock<MockMediaLog> media_log_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   base::MD5Context md5_context_;
   bool hashing_enabled_;
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn
index 00e4a11..cef26db 100644
--- a/mojo/common/BUILD.gn
+++ b/mojo/common/BUILD.gn
@@ -30,6 +30,12 @@
   js_bindings_mode = "both"
 }
 
+mojom("read_only_buffer") {
+  sources = [
+    "read_only_buffer.mojom",
+  ]
+}
+
 component("common_base") {
   output_name = "mojo_common_lib"
 
diff --git a/mojo/common/read_only_buffer.mojom b/mojo/common/read_only_buffer.mojom
new file mode 100644
index 0000000..19d08ce4
--- /dev/null
+++ b/mojo/common/read_only_buffer.mojom
@@ -0,0 +1,12 @@
+// 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.
+
+module mojo.common.mojom;
+
+// This struct is typemapped to base::span<const uint8_t>.
+// Note: At the receiving end, this base::span<const uint8_t> refers to a memory
+// range within the received message.
+struct ReadOnlyBuffer {
+  array<uint8> buffer;
+};
diff --git a/mojo/common/read_only_buffer.typemap b/mojo/common/read_only_buffer.typemap
new file mode 100644
index 0000000..c55a855
--- /dev/null
+++ b/mojo/common/read_only_buffer.typemap
@@ -0,0 +1,8 @@
+# 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.
+
+mojom = "//mojo/common/read_only_buffer.mojom"
+public_headers = [ "//base/containers/span.h" ]
+traits_headers = [ "//mojo/common/read_only_buffer_struct_traits.h" ]
+type_mappings = [ "mojo.common.mojom.ReadOnlyBuffer=base::span<const uint8_t>[copyable_pass_by_value]" ]
diff --git a/mojo/common/read_only_buffer_struct_traits.h b/mojo/common/read_only_buffer_struct_traits.h
new file mode 100644
index 0000000..c35efac
--- /dev/null
+++ b/mojo/common/read_only_buffer_struct_traits.h
@@ -0,0 +1,35 @@
+// 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 MOJO_COMMON_READ_ONLY_BUFFER_STRUCT_TRAITS_H_
+#define MOJO_COMMON_READ_ONLY_BUFFER_STRUCT_TRAITS_H_
+
+#include "base/containers/span.h"
+#include "mojo/common/read_only_buffer.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<common::mojom::ReadOnlyBufferDataView,
+                    base::span<const uint8_t>> {
+  static base::span<const uint8_t> buffer(base::span<const uint8_t> input) {
+    return input;
+  }
+
+  static bool Read(common::mojom::ReadOnlyBufferDataView input,
+                   base::span<const uint8_t>* out) {
+    ArrayDataView<uint8_t> data_view;
+    input.GetBufferDataView(&data_view);
+
+    // NOTE: This output directly refers to memory owned by the message.
+    // Therefore, the message must stay valid while the output is passed to the
+    // user code.
+    *out = base::span<const uint8_t>(data_view.data(), data_view.size());
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_COMMON_READ_ONLY_BUFFER_STRUCT_TRAITS_H_
diff --git a/mojo/common/typemaps.gni b/mojo/common/typemaps.gni
index 6890c8a6..b1d54a5a 100644
--- a/mojo/common/typemaps.gni
+++ b/mojo/common/typemaps.gni
@@ -7,6 +7,7 @@
   "//mojo/common/file_path.typemap",
   "//mojo/common/memory_allocator_dump_cross_process_uid.typemap",
   "//mojo/common/process_id.typemap",
+  "//mojo/common/read_only_buffer.typemap",
   "//mojo/common/string16.typemap",
   "//mojo/common/text_direction.typemap",
   "//mojo/common/time.typemap",
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc
index 422a8e8..ec790c4b 100644
--- a/net/dns/host_resolver_impl.cc
+++ b/net/dns/host_resolver_impl.cc
@@ -31,7 +31,6 @@
 #include "base/callback_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/debug/debugger.h"
-#include "base/debug/leak_annotations.h"
 #include "base/debug/stack_trace.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -1044,13 +1043,6 @@
       : resolver_(resolver), result_(false) {
     DCHECK(resolver.get());
 
-    // |worker_task_runner| may posts tasks to the WorkerPool, so need this to
-    // avoid reporting worker pool leaks in tests. The WorkerPool doesn't have a
-    // flushing API, so can't do anything about them, other than using another
-    // task runner.
-    // http://crbug.com/248513
-    ANNOTATE_SCOPED_MEMORY_LEAK;
-
     worker_task_runner->PostTaskAndReply(
         FROM_HERE,
         base::Bind(&LoopbackProbeJob::DoProbe, base::Unretained(this)),
diff --git a/net/quic/core/congestion_control/bandwidth_sampler.h b/net/quic/core/congestion_control/bandwidth_sampler.h
index 0550bcc9..b42e8b9 100644
--- a/net/quic/core/congestion_control/bandwidth_sampler.h
+++ b/net/quic/core/congestion_control/bandwidth_sampler.h
@@ -241,8 +241,13 @@
     // PacketNumberIndexedQueue.
     ConnectionStateOnSentPacket()
         : sent_time(QuicTime::Zero()),
+          size(0),
+          total_bytes_sent(0),
+          total_bytes_sent_at_last_acked_packet(0),
           last_acked_packet_sent_time(QuicTime::Zero()),
-          last_acked_packet_ack_time(QuicTime::Zero()) {}
+          last_acked_packet_ack_time(QuicTime::Zero()),
+          total_bytes_acked_at_the_last_acked_packet(0),
+          is_app_limited(false) {}
   };
 
   typedef QuicLinkedHashMap<QuicPacketNumber, ConnectionStateOnSentPacket>
diff --git a/remoting/host/file_proxy_wrapper.h b/remoting/host/file_proxy_wrapper.h
index a674cba..88e7f5ae 100644
--- a/remoting/host/file_proxy_wrapper.h
+++ b/remoting/host/file_proxy_wrapper.h
@@ -7,6 +7,7 @@
 
 #include "base/callback.h"
 #include "base/files/file_proxy.h"
+#include "base/optional.h"
 #include "remoting/proto/file_transfer.pb.h"
 
 namespace remoting {
@@ -18,10 +19,6 @@
 // thread, and possibly a different process depending on the platform.
 class FileProxyWrapper {
  public:
-  typedef base::Callback<void(protocol::FileTransferResponse_ErrorCode)>
-      ErrorCallback;
-  typedef base::Callback<void()> SuccessCallback;
-
   enum State {
     // Created, but Init() has not been called yet.
     kUninitialized = 0,
@@ -29,7 +26,8 @@
     // Init() has been called.
     kInitialized = 1,
 
-    // CreateFile() has been called. The file may or may not exist yet.
+    // CreateFile() has been called. The file may or may not exist yet, but
+    // this means that WriteChunk() can now be called.
     kFileCreated = 2,
 
     // Close() has been called. WriteChunk() can no longer be called, but not
@@ -44,25 +42,29 @@
     kFailed = 5,
   };
 
+  // If an error occured while writing the file, State will be kFailed and the
+  // Optional will contain the error which occured. If the file was written to
+  // and closed successfully, State will be kClosed and the Optional will be
+  // empty.
+  typedef base::OnceCallback<
+      void(State, base::Optional<protocol::FileTransferResponse_ErrorCode>)>
+      StatusCallback;
+
   // Creates a platform-specific FileProxyWrapper.
   static std::unique_ptr<FileProxyWrapper> Create();
 
   FileProxyWrapper();
   virtual ~FileProxyWrapper();
 
-  // |error_callback| must not immediately destroy this FileProxyWrapper.
-  virtual void Init(const ErrorCallback& error_callback) = 0;
-  // TODO(jarhar): Remove |success_callback| from CreateFile(), and instead
-  // allow WriteChunk() to be called immediately after CreateFile(). This also
-  // means that FileTransferMessageHandler will no longer send a
-  // FileTransferResponse with the "READY" state.
+  // |status_callback| is called either when FileProxyWrapper encounters an
+  // error or when Close() has been called and the file has been written
+  // successfully. |status_callback| must not immediately destroy this
+  // FileProxyWrapper.
+  virtual void Init(StatusCallback status_callback) = 0;
   virtual void CreateFile(const base::FilePath& directory,
-                          const std::string& filename,
-                          const SuccessCallback& success_callback) = 0;
+                          const std::string& filename) = 0;
   virtual void WriteChunk(std::unique_ptr<CompoundBuffer> buffer) = 0;
-  // TODO(jarhar): Remove |success_callback| from Close() and instead use the
-  // ErrorCallback sent to Init() to signify success after Close().
-  virtual void Close(const SuccessCallback& success_callback) = 0;
+  virtual void Close() = 0;
   virtual void Cancel() = 0;
   virtual State state() = 0;
 };
diff --git a/remoting/host/file_proxy_wrapper_linux.cc b/remoting/host/file_proxy_wrapper_linux.cc
index b4029c2..5793f32 100644
--- a/remoting/host/file_proxy_wrapper_linux.cc
+++ b/remoting/host/file_proxy_wrapper_linux.cc
@@ -48,12 +48,11 @@
   ~FileProxyWrapperLinux() override;
 
   // FileProxyWrapper implementation.
-  void Init(const ErrorCallback& error_callback) override;
+  void Init(StatusCallback status_callback) override;
   void CreateFile(const base::FilePath& directory,
-                  const std::string& filename,
-                  const SuccessCallback& success_callback) override;
+                  const std::string& filename) override;
   void WriteChunk(std::unique_ptr<CompoundBuffer> buffer) override;
-  void Close(const SuccessCallback& success_callback) override;
+  void Close() override;
   void Cancel() override;
   State state() override;
 
@@ -64,10 +63,8 @@
   };
 
   // Callbacks for CreateFile().
-  void CreateTempFile(const SuccessCallback& success_callback,
-                      int unique_path_number);
-  void CreateTempFileCallback(const SuccessCallback& success_callback,
-                              base::File::Error error);
+  void CreateTempFile(int unique_path_number);
+  void CreateTempFileCallback(base::File::Error error);
 
   // Callbacks for WriteChunk().
   void WriteFileChunk(std::unique_ptr<FileChunk> chunk);
@@ -86,8 +83,9 @@
   scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
   std::unique_ptr<base::FileProxy> file_proxy_;
 
-  ErrorCallback error_callback_;
+  StatusCallback status_callback_;
 
+  bool temp_file_created_ = false;
   base::FilePath temp_filepath_;
   base::FilePath destination_filepath_;
 
@@ -97,8 +95,6 @@
   // empty if nothing is being written to disk right now.
   std::unique_ptr<FileChunk> active_file_chunk_;
 
-  SuccessCallback close_success_callback_;
-
   base::ThreadChecker thread_checker_;
   base::WeakPtr<FileProxyWrapperLinux> weak_ptr_;
   base::WeakPtrFactory<FileProxyWrapperLinux> weak_factory_;
@@ -112,11 +108,11 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 }
 
-void FileProxyWrapperLinux::Init(const ErrorCallback& error_callback) {
+void FileProxyWrapperLinux::Init(StatusCallback status_callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   SetState(kInitialized);
-  error_callback_ = error_callback;
+  status_callback_ = std::move(status_callback);
 
   file_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
       {base::MayBlock(), base::TaskPriority::BACKGROUND});
@@ -129,10 +125,8 @@
   }
 }
 
-void FileProxyWrapperLinux::CreateFile(
-    const base::FilePath& directory,
-    const std::string& filename,
-    const SuccessCallback& success_callback) {
+void FileProxyWrapperLinux::CreateFile(const base::FilePath& directory,
+                                       const std::string& filename) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   SetState(kFileCreated);
@@ -144,34 +138,37 @@
       file_task_runner_.get(), FROM_HERE,
       base::BindOnce(&base::GetUniquePathNumber, temp_filepath_,
                      base::FilePath::StringType()),
-      base::BindOnce(&FileProxyWrapperLinux::CreateTempFile, weak_ptr_,
-                     success_callback));
+      base::BindOnce(&FileProxyWrapperLinux::CreateTempFile, weak_ptr_));
 }
 
-void FileProxyWrapperLinux::CreateTempFile(
-    const SuccessCallback& success_callback,
-    int unique_path_number) {
+void FileProxyWrapperLinux::CreateTempFile(int unique_path_number) {
   if (unique_path_number > 0) {
     temp_filepath_ = temp_filepath_.InsertBeforeExtensionASCII(
         base::StringPrintf(" (%d)", unique_path_number));
   }
   if (!file_proxy_->CreateOrOpen(
           temp_filepath_, base::File::FLAG_CREATE | base::File::FLAG_WRITE,
-          base::Bind(&FileProxyWrapperLinux::CreateTempFileCallback, weak_ptr_,
-                     success_callback))) {
+          base::Bind(&FileProxyWrapperLinux::CreateTempFileCallback,
+                     weak_ptr_))) {
     // file_proxy_ failed to post a task to file_task_runner_.
     CancelWithError(protocol::FileTransferResponse_ErrorCode_UNEXPECTED_ERROR);
   }
 }
 
-void FileProxyWrapperLinux::CreateTempFileCallback(
-    const SuccessCallback& success_callback,
-    base::File::Error error) {
+void FileProxyWrapperLinux::CreateTempFileCallback(base::File::Error error) {
   if (error) {
     LOG(ERROR) << "Creating the temp file failed with error: " << error;
     CancelWithError(FileErrorToResponseError(error));
   } else {
-    success_callback.Run();
+    temp_file_created_ = true;
+    // Chunks to write may have been queued while we were creating the file,
+    // start writing them now if there were any.
+    if (!file_chunks_.empty()) {
+      std::unique_ptr<FileChunk> chunk_to_write =
+          std::move(file_chunks_.front());
+      file_chunks_.pop();
+      WriteFileChunk(std::move(chunk_to_write));
+    }
   }
 }
 
@@ -190,7 +187,9 @@
   new_file_chunk->write_offset = next_write_file_offset_;
   next_write_file_offset_ += new_file_chunk->data.size();
 
-  if (active_file_chunk_) {
+  // If the file hasn't been created yet or there is another chunk currently
+  // being written, we have to queue this chunk to be written later.
+  if (!temp_file_created_ || active_file_chunk_) {
     // TODO(jarhar): When flow control enabled QUIC-based WebRTC data channels
     // are implemented, block the flow of incoming chunks here if
     // file_chunks_ has reached a maximum size. This implementation will
@@ -235,11 +234,10 @@
   }
 }
 
-void FileProxyWrapperLinux::Close(const SuccessCallback& success_callback) {
+void FileProxyWrapperLinux::Close() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   SetState(kClosing);
-  close_success_callback_ = success_callback;
 
   if (!active_file_chunk_ && file_chunks_.empty()) {
     // All writes are complete, so we can finish up now.
@@ -282,7 +280,9 @@
 
   if (success) {
     SetState(kClosed);
-    std::move(close_success_callback_).Run();
+    std::move(status_callback_)
+        .Run(state_,
+             base::Optional<protocol::FileTransferResponse_ErrorCode>());
   } else {
     CancelWithError(protocol::FileTransferResponse_ErrorCode_FILE_IO_ERROR);
   }
@@ -312,7 +312,9 @@
 void FileProxyWrapperLinux::CancelWithError(
     protocol::FileTransferResponse_ErrorCode error) {
   Cancel();
-  std::move(error_callback_).Run(error);
+  std::move(status_callback_)
+      .Run(state_,
+           base::Optional<protocol::FileTransferResponse_ErrorCode>(error));
 }
 
 void FileProxyWrapperLinux::SetState(State state) {
diff --git a/remoting/host/file_proxy_wrapper_linux_unittest.cc b/remoting/host/file_proxy_wrapper_linux_unittest.cc
index 038b240..a8ce17c1 100644
--- a/remoting/host/file_proxy_wrapper_linux_unittest.cc
+++ b/remoting/host/file_proxy_wrapper_linux_unittest.cc
@@ -48,18 +48,18 @@
     return dir_.GetPath().Append(kTestFilename);
   }
 
-  void ErrorCallback(protocol::FileTransferResponse_ErrorCode error);
-  void CreateFileCallback();
-  void CloseCallback();
+  void StatusCallback(
+      FileProxyWrapper::State state,
+      base::Optional<protocol::FileTransferResponse_ErrorCode> error);
 
  protected:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   base::ScopedTempDir dir_;
 
   std::unique_ptr<FileProxyWrapper> file_proxy_wrapper_;
-  std::unique_ptr<protocol::FileTransferResponse_ErrorCode> error_;
-  bool create_callback_called_;
-  bool close_callback_called_;
+  base::Optional<protocol::FileTransferResponse_ErrorCode> error_;
+  FileProxyWrapper::State final_state_;
+  bool done_callback_succeeded_;
 };
 
 FileProxyWrapperLinuxTest::FileProxyWrapperLinuxTest()
@@ -72,53 +72,39 @@
   ASSERT_TRUE(dir_.CreateUniqueTempDir());
 
   file_proxy_wrapper_ = FileProxyWrapper::Create();
-  file_proxy_wrapper_->Init(base::Bind(
-      &FileProxyWrapperLinuxTest::ErrorCallback, base::Unretained(this)));
+  file_proxy_wrapper_->Init(base::BindOnce(
+      &FileProxyWrapperLinuxTest::StatusCallback, base::Unretained(this)));
 
-  error_.reset();
-  create_callback_called_ = false;
-  close_callback_called_ = false;
+  error_ = base::Optional<protocol::FileTransferResponse_ErrorCode>();
+  final_state_ = FileProxyWrapper::kUninitialized;
+  done_callback_succeeded_ = false;
 }
 
 void FileProxyWrapperLinuxTest::TearDown() {
   file_proxy_wrapper_.reset();
 }
 
-void FileProxyWrapperLinuxTest::ErrorCallback(
-    protocol::FileTransferResponse_ErrorCode error) {
-  error_.reset(new protocol::FileTransferResponse_ErrorCode(error));
-}
-
-void FileProxyWrapperLinuxTest::CreateFileCallback() {
-  create_callback_called_ = true;
-}
-
-void FileProxyWrapperLinuxTest::CloseCallback() {
-  close_callback_called_ = true;
+void FileProxyWrapperLinuxTest::StatusCallback(
+    FileProxyWrapper::State state,
+    base::Optional<protocol::FileTransferResponse_ErrorCode> error) {
+  final_state_ = state;
+  error_ = error;
+  done_callback_succeeded_ = !error_.has_value();
 }
 
 // Verifies that FileProxyWrapper can write three chunks to a file without
 // throwing any errors.
 TEST_F(FileProxyWrapperLinuxTest, WriteThreeChunks) {
-  file_proxy_wrapper_->CreateFile(
-      TestDir(), kTestFilename,
-      base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback,
-                 base::Unretained(this)));
-
-  scoped_task_environment_.RunUntilIdle();
-  ASSERT_FALSE(error_);
-  ASSERT_TRUE(create_callback_called_);
-
+  file_proxy_wrapper_->CreateFile(TestDir(), kTestFilename);
   file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataOne));
   file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataTwo));
   file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataThree));
-
-  file_proxy_wrapper_->Close(base::Bind(
-      &FileProxyWrapperLinuxTest::CloseCallback, base::Unretained(this)));
-
+  file_proxy_wrapper_->Close();
   scoped_task_environment_.RunUntilIdle();
+
   ASSERT_FALSE(error_);
-  ASSERT_TRUE(close_callback_called_);
+  ASSERT_EQ(final_state_, FileProxyWrapper::kClosed);
+  ASSERT_TRUE(done_callback_succeeded_);
 
   std::string actual_file_data;
   ASSERT_TRUE(base::ReadFileToString(TestFilePath(), &actual_file_data));
@@ -127,12 +113,7 @@
 
 // Verifies that calling Cancel() deletes any temporary or destination files.
 TEST_F(FileProxyWrapperLinuxTest, CancelDeletesFiles) {
-  file_proxy_wrapper_->CreateFile(
-      TestDir(), kTestFilename,
-      base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback,
-                 base::Unretained(this)));
-  scoped_task_environment_.RunUntilIdle();
-
+  file_proxy_wrapper_->CreateFile(TestDir(), kTestFilename);
   file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataOne));
   scoped_task_environment_.RunUntilIdle();
 
@@ -148,21 +129,18 @@
 TEST_F(FileProxyWrapperLinuxTest, FileAlreadyExists) {
   WriteFile(TestFilePath(), kTestDataOne.data(), kTestDataOne.size());
 
-  file_proxy_wrapper_->CreateFile(
-      TestDir(), kTestFilename,
-      base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback,
-                 base::Unretained(this)));
-  scoped_task_environment_.RunUntilIdle();
-
+  file_proxy_wrapper_->CreateFile(TestDir(), kTestFilename);
   file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataTwo));
-  file_proxy_wrapper_->Close(base::Bind(
-      &FileProxyWrapperLinuxTest::CloseCallback, base::Unretained(this)));
+  file_proxy_wrapper_->Close();
   scoped_task_environment_.RunUntilIdle();
 
   std::string actual_file_data;
   base::FilePath secondary_filepath = TestDir().Append(kTestFilenameSecondary);
   ASSERT_TRUE(base::ReadFileToString(secondary_filepath, &actual_file_data));
   ASSERT_STREQ(kTestDataTwo.data(), actual_file_data.data());
+
+  ASSERT_FALSE(error_);
+  ASSERT_EQ(final_state_, FileProxyWrapper::kClosed);
 }
 
 }  // namespace remoting
diff --git a/remoting/proto/file_transfer.proto b/remoting/proto/file_transfer.proto
index a9bb7228..12acd48e 100644
--- a/remoting/proto/file_transfer.proto
+++ b/remoting/proto/file_transfer.proto
@@ -13,9 +13,8 @@
 // Next Id: 4
 message FileTransferResponse {
   enum TransferState {
-    READY = 1;
-    IN_PROGRESS = 2;
-    DONE = 3;
+    IN_PROGRESS = 1;
+    DONE = 2;
   }
   optional TransferState state = 1;
 
diff --git a/services/ui/gpu/gpu_main.cc b/services/ui/gpu/gpu_main.cc
index e11d21ea..4af3ff6 100644
--- a/services/ui/gpu/gpu_main.cc
+++ b/services/ui/gpu/gpu_main.cc
@@ -173,7 +173,8 @@
   DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread());
   gpu_command_service_ = new gpu::GpuInProcessThreadService(
       gpu_thread_task_runner_, gpu_service_->sync_point_manager(),
-      gpu_service_->mailbox_manager(), gpu_service_->share_group());
+      gpu_service_->mailbox_manager(), gpu_service_->share_group(),
+      gpu_service_->gpu_feature_info());
 
   compositor_thread_task_runner_->PostTask(
       FROM_HERE,
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index 5d265c4..3956b3c7 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -28,6 +28,8 @@
     "cursor_state.cc",
     "cursor_state.h",
     "cursor_state_delegate.h",
+    "debug_utils.cc",
+    "debug_utils.h",
     "default_access_policy.cc",
     "default_access_policy.h",
     "display.cc",
diff --git a/services/ui/ws/debug_utils.cc b/services/ui/ws/debug_utils.cc
new file mode 100644
index 0000000..351b416
--- /dev/null
+++ b/services/ui/ws/debug_utils.cc
@@ -0,0 +1,17 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/ui/ws/debug_utils.h"
+
+#include "services/ui/ws/server_window.h"
+
+namespace ui {
+namespace ws {
+
+std::string DebugWindowId(const ServerWindow* window) {
+  return window ? window->id().ToString() : "null";
+}
+
+}  // namespace ws
+}  // namespace ui
diff --git a/services/ui/ws/debug_utils.h b/services/ui/ws/debug_utils.h
new file mode 100644
index 0000000..dbac1c6
--- /dev/null
+++ b/services/ui/ws/debug_utils.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_UI_WS_DEBUG_UTILS_H_
+#define SERVICES_UI_WS_DEBUG_UTILS_H_
+
+#include <string>
+
+namespace ui {
+namespace ws {
+
+class ServerWindow;
+
+// Returns a debug string for |window|, returns "null" if |window| is null.
+std::string DebugWindowId(const ServerWindow* window);
+
+}  // namespace ws
+}  // namespace ui
+
+#endif  // SERVICES_UI_WS_DEBUG_UTILS_H_
diff --git a/services/ui/ws/display.cc b/services/ui/ws/display.cc
index 377fc2c2..318d34b1 100644
--- a/services/ui/ws/display.cc
+++ b/services/ui/ws/display.cc
@@ -14,6 +14,7 @@
 #include "services/ui/common/types.h"
 #include "services/ui/display/viewport_metrics.h"
 #include "services/ui/public/interfaces/cursor/cursor.mojom.h"
+#include "services/ui/ws/debug_utils.h"
 #include "services/ui/ws/display_binding.h"
 #include "services/ui/ws/display_manager.h"
 #include "services/ui/ws/focus_controller.h"
@@ -138,6 +139,8 @@
 }
 
 bool Display::SetFocusedWindow(ServerWindow* new_focused_window) {
+  DVLOG(3) << "Display::SetFocusedWindow id="
+           << DebugWindowId(new_focused_window);
   ServerWindow* old_focused_window = focus_controller_->GetFocusedWindow();
   if (old_focused_window == new_focused_window)
     return true;
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc
index b6acc43..c0f26a6 100644
--- a/services/ui/ws/event_dispatcher.cc
+++ b/services/ui/ws/event_dispatcher.cc
@@ -517,8 +517,8 @@
       if (is_mouse_event)
         SetMouseCursorSourceWindow(pointer_target.window);
       if (!any_pointers_down) {
-        if (pointer_target.window)
-          delegate_->SetFocusedWindowFromEventDispatcher(pointer_target.window);
+        // Don't attempt to change focus on pointer downs. We assume client code
+        // will do that.
         ServerWindow* capture_window = pointer_target.window;
         if (!capture_window) {
           gfx::Point event_location =
diff --git a/services/ui/ws/event_dispatcher_unittest.cc b/services/ui/ws/event_dispatcher_unittest.cc
index ebafea0..a107fd6 100644
--- a/services/ui/ws/event_dispatcher_unittest.cc
+++ b/services/ui/ws/event_dispatcher_unittest.cc
@@ -1003,18 +1003,16 @@
 }
 
 TEST_P(EventDispatcherTest, DontFocusOnSecondDown) {
-  std::unique_ptr<ServerWindow> child1 = CreateChildWindow(WindowId(1, 3));
-  std::unique_ptr<ServerWindow> child2 = CreateChildWindow(WindowId(1, 4));
+  std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
 
   root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
-  child1->SetBounds(gfx::Rect(10, 10, 20, 20));
-  child2->SetBounds(gfx::Rect(50, 51, 11, 12));
+  child->SetBounds(gfx::Rect(10, 10, 20, 20));
 
   TestEventDispatcherDelegate* event_dispatcher_delegate =
       test_event_dispatcher_delegate();
   EventDispatcher* dispatcher = event_dispatcher();
 
-  // Press on child1. First press event should change focus.
+  // Press on |child|. Press should not change focus.
   const ui::PointerEvent press_event(ui::MouseEvent(
       ui::ET_MOUSE_PRESSED, gfx::Point(12, 12), gfx::Point(12, 12),
       base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
@@ -1023,20 +1021,7 @@
   std::unique_ptr<DispatchedEventDetails> details =
       event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
   EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
-  EXPECT_EQ(child1.get(), details->window);
-  EXPECT_EQ(child1.get(),
-            event_dispatcher_delegate->GetAndClearLastFocusedWindow());
-
-  // Press (with a different pointer id) on child2. Event should go to child2,
-  // but focus should not change.
-  const ui::PointerEvent touch_event(ui::TouchEvent(
-      ui::ET_TOUCH_PRESSED, gfx::Point(53, 54), base::TimeTicks(),
-      ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 2)));
-  DispatchEvent(dispatcher, touch_event, 0,
-                EventDispatcher::AcceleratorMatchPhase::ANY);
-  details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails();
-  EXPECT_FALSE(event_dispatcher_delegate->has_queued_events());
-  EXPECT_EQ(child2.get(), details->window);
+  EXPECT_EQ(child.get(), details->window);
   EXPECT_EQ(nullptr, event_dispatcher_delegate->GetAndClearLastFocusedWindow());
 }
 
diff --git a/services/ui/ws/window_tree.cc b/services/ui/ws/window_tree.cc
index dab1369..7970153 100644
--- a/services/ui/ws/window_tree.cc
+++ b/services/ui/ws/window_tree.cc
@@ -15,6 +15,7 @@
 #include "mojo/public/cpp/bindings/map.h"
 #include "services/ui/display/screen_manager.h"
 #include "services/ui/ws/cursor_location_manager.h"
+#include "services/ui/ws/debug_utils.h"
 #include "services/ui/ws/default_access_policy.h"
 #include "services/ui/ws/display.h"
 #include "services/ui/ws/display_manager.h"
@@ -650,6 +651,9 @@
 bool WindowTree::SetFocus(const ClientWindowId& window_id) {
   ServerWindow* window = GetWindowByClientId(window_id);
   ServerWindow* currently_focused = window_server_->GetFocusedWindow();
+  DVLOG(3) << "SetFocusedWindow client=" << id_
+           << " client window_id=" << window_id.id
+           << " window=" << DebugWindowId(window);
   if (!currently_focused && !window) {
     DVLOG(1) << "SetFocus failed (no focused window to clear)";
     return false;
diff --git a/services/ui/ws/window_tree_unittest.cc b/services/ui/ws/window_tree_unittest.cc
index b47a93c0..b3e3bd6 100644
--- a/services/ui/ws/window_tree_unittest.cc
+++ b/services/ui/ws/window_tree_unittest.cc
@@ -225,8 +225,8 @@
   FirstRoot(*window_tree)->set_is_activation_parent(true);
 }
 
-// Verifies focus correctly changes on pointer events.
-TEST_F(WindowTreeTest, FocusOnPointer) {
+// Verifies focus does not change on pointer events.
+TEST_F(WindowTreeTest, DontFocusOnPointer) {
   const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1);
   EXPECT_TRUE(
       wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties()));
@@ -262,54 +262,12 @@
   child1->SetVisible(true);
   child1->SetBounds(gfx::Rect(20, 20, 20, 20));
 
-  TestWindowTreeClient* tree1_client = last_window_tree_client();
-  tree1_client->tracker()->changes()->clear();
-  wm_client()->tracker()->changes()->clear();
+  embed_window->set_is_activation_parent(true);
 
-  // Focus should not go to |child1| yet, since the parent still doesn't allow
-  // active children.
+  // Dispatch a pointer event to the child1, focus should be null.
   DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22));
   Display* display1 = tree1->GetDisplay(embed_window);
   EXPECT_EQ(nullptr, display1->GetFocusedWindow());
-  DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22));
-  tree1_client->tracker()->changes()->clear();
-  wm_client()->tracker()->changes()->clear();
-
-  embed_window->set_is_activation_parent(true);
-
-  // Focus should go to child1. This result in notifying both the window
-  // manager and client client being notified.
-  DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22));
-  EXPECT_EQ(child1, display1->GetFocusedWindow());
-  ASSERT_GE(wm_client()->tracker()->changes()->size(), 1u);
-  EXPECT_EQ("Focused id=2,1",
-            ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
-  ASSERT_GE(tree1_client->tracker()->changes()->size(), 1u);
-  EXPECT_EQ("Focused id=2,1",
-            ChangesToDescription1(*tree1_client->tracker()->changes())[0]);
-
-  DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22));
-  wm_client()->tracker()->changes()->clear();
-  tree1_client->tracker()->changes()->clear();
-
-  // Press outside of the embedded window. Note that root cannot be focused
-  // (because it cannot be activated). So the focus would not move in this case.
-  DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22));
-  EXPECT_EQ(child1, display()->GetFocusedWindow());
-
-  DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22));
-  wm_client()->tracker()->changes()->clear();
-  tree1_client->tracker()->changes()->clear();
-
-  // Press in the same location. Should not get a focus change event (only input
-  // event).
-  DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22));
-  EXPECT_EQ(child1, display()->GetFocusedWindow());
-  ASSERT_EQ(1u, wm_client()->tracker()->changes()->size())
-      << SingleChangeToDescription(*wm_client()->tracker()->changes());
-  EXPECT_EQ("InputEvent window=0,3 event_action=16",
-            ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
-  EXPECT_TRUE(tree1_client->tracker()->changes()->empty());
 }
 
 TEST_F(WindowTreeTest, BasicInputEventTarget) {
@@ -322,14 +280,10 @@
   // Send an event to |v1|. |embed_client| should get the event, not
   // |wm_client|, since |v1| lives inside an embedded window.
   DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22));
-  ASSERT_EQ(1u, wm_client()->tracker()->changes()->size());
-  EXPECT_EQ("Focused id=2,1",
-            ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
-  ASSERT_EQ(2u, embed_client->tracker()->changes()->size());
-  EXPECT_EQ("Focused id=2,1",
-            ChangesToDescription1(*embed_client->tracker()->changes())[0]);
+  ASSERT_EQ(0u, wm_client()->tracker()->changes()->size());
+  ASSERT_EQ(1u, embed_client->tracker()->changes()->size());
   EXPECT_EQ("InputEvent window=2,1 event_action=16",
-            ChangesToDescription1(*embed_client->tracker()->changes())[1]);
+            ChangesToDescription1(*embed_client->tracker()->changes())[0]);
 }
 
 // Verifies SetChildModalParent() works correctly.
@@ -417,13 +371,11 @@
   ui::PointerEvent pointer_down = CreatePointerDownEvent(25, 25);
   DispatchEventAndAckImmediately(pointer_down);
 
-  // Expect two changes, the first is focus, the second the pointer watcher
-  // event.
-  ASSERT_EQ(2u, wm_client()->tracker()->changes()->size());
+  ASSERT_EQ(1u, wm_client()->tracker()->changes()->size());
   EXPECT_EQ(
       "PointerWatcherEvent event_action=16 window=" +
           ClientWindowIdToString(ClientWindowIdForWindow(wm_tree(), window)),
-      ChangesToDescription1(*wm_client()->tracker()->changes())[1]);
+      ChangesToDescription1(*wm_client()->tracker()->changes())[0]);
 }
 
 // Tests that a client using a pointer watcher does not receive events that
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings.mojom b/services/viz/privileged/interfaces/compositing/renderer_settings.mojom
index 7d924041..daf2009 100644
--- a/services/viz/privileged/interfaces/compositing/renderer_settings.mojom
+++ b/services/viz/privileged/interfaces/compositing/renderer_settings.mojom
@@ -21,4 +21,5 @@
   bool should_clear_root_render_pass;
   bool show_overdraw_feedback;
   int32 slow_down_compositing_scale_factor;
+  bool use_skia_renderer;
 };
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc
index e1a0c36..83c2c36ca 100644
--- a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc
+++ b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc
@@ -28,6 +28,7 @@
       data.disallow_non_exact_resource_reuse();
   out->slow_down_compositing_scale_factor =
       data.slow_down_compositing_scale_factor();
+  out->use_skia_renderer = data.use_skia_renderer();
   return data.ReadResourceSettings(&out->resource_settings);
 }
 
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h
index f9ce66ae..3f7e4c9 100644
--- a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h
+++ b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h
@@ -75,6 +75,10 @@
     return input.slow_down_compositing_scale_factor;
   }
 
+  static bool use_skia_renderer(const viz::RendererSettings& input) {
+    return input.use_skia_renderer;
+  }
+
   static bool Read(viz::mojom::RendererSettingsDataView data,
                    viz::RendererSettings* out);
 };
diff --git a/services/viz/privileged/interfaces/struct_traits_unittest.cc b/services/viz/privileged/interfaces/struct_traits_unittest.cc
index e6b826a1..f13e9be 100644
--- a/services/viz/privileged/interfaces/struct_traits_unittest.cc
+++ b/services/viz/privileged/interfaces/struct_traits_unittest.cc
@@ -43,6 +43,7 @@
   input.enable_color_correct_rendering = true;
   input.highp_threshold_min = -1;
   input.disallow_non_exact_resource_reuse = true;
+  input.use_skia_renderer = true;
 
   RendererSettings output;
   mojom::RendererSettings::Deserialize(
@@ -72,6 +73,7 @@
   EXPECT_EQ(input.highp_threshold_min, output.highp_threshold_min);
   EXPECT_EQ(input.disallow_non_exact_resource_reuse,
             output.disallow_non_exact_resource_reuse);
+  EXPECT_EQ(input.use_skia_renderer, output.use_skia_renderer);
 }
 
 }  // namespace
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 587a977..a7acd2143 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -747,13 +747,6 @@
       },
       {
         "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "swarming": {
           "can_use_on_swarming_builders": true
         },
         "test": "cacheinvalidation_unittests"
diff --git a/testing/buildbot/filters/mojo.fyi.mus.browser_tests.filter b/testing/buildbot/filters/mojo.fyi.mus.browser_tests.filter
index 5c11698..1137157 100644
--- a/testing/buildbot/filters/mojo.fyi.mus.browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.mus.browser_tests.filter
@@ -10,6 +10,7 @@
 -ForceMaximizeOnFirstRunTest.TwoRuns
 
 # TODO: fails because of screen capture. http://crbug.com/754899.
+-ChromeScreenshotGrabberBrowserTest.TakeScreenshot
 -LoginFeedbackTest.Basic
 -TabCaptureApiPixelTest.EndToEndThroughWebRTC
 -TabCaptureApiPixelTest.EndToEndWithoutRemoting
@@ -19,6 +20,9 @@
 # TODO: fix, see http://crbug.com/755272.
 -PDFExtensionTest.RedirectsFailInPlugin
 
+# TODO: fix, http://crbug.com/759156.
+-PolicyDisplayRotationDefault/DisplayRotationDefaultTest.RefreshSecondDisplay/2
+
 # TODO: fix, see http://crbug.com/755303.
 -StartupMetricsTest.ReportsValues
 
@@ -52,7 +56,3 @@
 -WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/0
 -WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/1
 -WebViewTests/WebViewTest.InterstitialPageFocusedWidget/1
-
-# TODO(sky): file bugs for these.
--ChromeScreenshotGrabberBrowserTest.TakeScreenshot
--PolicyDisplayRotationDefault/DisplayRotationDefaultTest.RefreshSecondDisplay/2
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index fa433ab..f423c9e4 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -108,7 +108,6 @@
 -ResourceDispatcherHostBrowserTest.PageTransitionClientRedirect
 -ResourceDispatcherHostBrowserTest.SniffHTMLWithNoContentType
 -ResourceDispatcherHostBrowserTest.SniffNoContentTypeNoData
--ResourceDispatcherHostBrowserTest.SyncXMLHttpRequest
 -SessionHistoryTest.HistoryLength
 -SitePerProcessBrowserTest.CrossProcessFocusChangeFiresBlurEvents
 -SitePerProcessBrowserTest.CrossSiteIframeBlockedByCSPInheritedBySrcDocParent
@@ -137,6 +136,12 @@
 # sometimes.
 -SRC_ClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
 
-# https://crbug.com/754827
--ResourceDispatcherHostBrowserTest.SyncXMLHttpRequest_Cancelled
+# Cross-origin request to file:// URL should be blocked. crbug.com/759230
 -BrowserSideNavigationBrowserDisableWebSecurityTest.ValidateBaseUrlForDataUrl
+
+# This test validates that the renderer does not hang indefinitely in a sync
+# XMLHttpRequest when ResourceDispatcherHostImpl::CancelRequestsForProcess is
+# called to tear down the browser-side request state without signaling
+# completion. With the network service this will be equivalent to a network
+# service process crash.
+-ResourceDispatcherHostBrowserTest.SyncXMLHttpRequest_Cancelled
diff --git a/testing/buildbot/filters/mus.browser_tests.filter b/testing/buildbot/filters/mus.browser_tests.filter
index 48acfee..1137157 100644
--- a/testing/buildbot/filters/mus.browser_tests.filter
+++ b/testing/buildbot/filters/mus.browser_tests.filter
@@ -1,390 +1,58 @@
-# Failing test
--BrowserTest.FullscreenBookmarkBar
--BrowserTest.DisableOptionsAndImportMenuItemsConsistently
--BrowserTest.ReattachDevToolsWindow
+# TODO: Investigate failure, http://crbug.com/754864.
+-CastStreamingApiTestWithPixelOutput.EndToEnd
 
-# Flaky tests -crbug.com/707195
--BrowserTest.AboutVersion
--BrowserTest.CanDuplicateTab
--BrowserTest.ClearPendingOnFailUnlessNT
--BrowserTest.DefaultMediaDevices
--BrowserTest.GetSizeForNewRenderView
--BrowserTest.JavascriptAlertActivatesTab
--BrowserTest.Title
+# TODO: Fails because RenderWidget::DidReceiveCompositorFrameAck() is
+# not called as often as it was before. http://crbug.com/754846.
+-ConstrainedWebDialogBrowserTest.ContentResizeInAutoResizingDialog
 
-# Trying to whitelist
-AutofillRendererTest.DontCrashWhileAssociatingForms
-AutofillRendererTest.DynamicallyAddedUnownedFormElements
-AutofillRendererTest.EnsureNoFormSeenIfTooFewFields
-AutofillRendererTest.IgnoreNonUserGestureTextFieldChanges
-AutofillRendererTest.SendForms
-BrowserDialogTest.Invoke
-CastSessionBrowserTest.CreateAndDestroy
-ChromeContentRendererClientSearchBoxTest.RewriteThumbnailURL
-ChromeRenderFrameObserverTest.SkipCapturingSubFrames
-ChromeRenderViewTest.AutoplayContentSettings
-ChromeRenderViewTest.ContentSettingsAllowScripts
-ChromeRenderViewTest.ContentSettingsBlockScripts
-ChromeRenderViewTest.ContentSettingsInterstitialPages
-ChromeRenderViewTest.ContentSettingsNoscriptTag
-ChromeRenderViewTest.ContentSettingsSameDocumentNavigation
-ChromeRenderViewTest.DidBlockContentType
-ChromeRenderViewTest.ImagesAllowedByDefault
-ChromeRenderViewTest.ImagesBlockedByDefault
-ChromeRenderViewTest.JSBlockSentAfterPageLoad
-ChromeRenderViewTest.PluginsTemporarilyAllowed
-ChromeServiceWorkerManifestFetchTest.OtherOrigin
-ChromeServiceWorkerManifestFetchTest.OtherOriginUseCredentials
-ChromeServiceWorkerManifestFetchTest.SameOrigin
-ChromeServiceWorkerManifestFetchTest.SameOriginUseCredentials
-ChromeServiceWorkerTest.CanCloseIncognitoWindowWithServiceWorkerController
-ChromeServiceWorkerTest.CanShutDownWithRegisteredServiceWorker
-ChromeServiceWorkerTest.FailRegisterServiceWorkerWhenJSDisabled
-ChromeServiceWorkerTest.FallbackMainResourceRequestWhenJSDisabled
-CloudPolicyProtoTest.VerifyProtobufEquivalence
-DeviceStatusCollectorNetworkInterfacesTest.NetworkInterfaces
-DeviceStatusCollectorNetworkInterfacesTest.ReportIfPublicSession
-DeviceStatusCollectorTest.ActivityCrossingMidnight
-DeviceStatusCollectorTest.ActivityTimesEnabledByDefault
-DeviceStatusCollectorTest.ActivityTimesKeptUntilSubmittedSuccessfully
-DeviceStatusCollectorTest.ActivityTimesOff
-DeviceStatusCollectorTest.AllActive
-DeviceStatusCollectorTest.AllIdle
-DeviceStatusCollectorTest.DevSwitchBootMode
-DeviceStatusCollectorTest.MaxStoredPeriods
-DeviceStatusCollectorTest.MixedStates
-DeviceStatusCollectorTest.NoOsUpdateStatusByDefault
-DeviceStatusCollectorTest.NoRegularUserAndroidReportingWhenDisabled
-DeviceStatusCollectorTest.NoRegularUserAndroidReportingWhenNotAffiliated
-DeviceStatusCollectorTest.NoSessionStatusIfNoSession
-DeviceStatusCollectorTest.NoSessionStatusIfSessionReportingDisabled
-DeviceStatusCollectorTest.RegularUserAndroidReporting
-DeviceStatusCollectorTest.ReportOsUpdateStatus
-DeviceStatusCollectorTest.ReportOsUpdateStatusUpToDate
-DeviceStatusCollectorTest.ReportUsers
-DeviceStatusCollectorTest.StateKeptInPref
-DeviceStatusCollectorTest.StateKeptInPref
-DeviceStatusCollectorTest.TestAvailableMemory
-DeviceStatusCollectorTest.TestCPUSamples
-DeviceStatusCollectorTest.TestCPUTemp
-DeviceStatusCollectorTest.TestSoundVolume
-DeviceStatusCollectorTest.TestVolumeInfo
-DeviceStatusCollectorTest.Times
-DeviceStatusCollectorTest.VersionInfo
-DownloadInterruptReasonEnumsSynced.DownloadInterruptReasonEnumsSynced
-ExtensionDetermineDownloadFilenameInternal.ExtensionDetermineDownloadFilenameInternal
-FormAutocompleteTest.AjaxSucceeded_FilledFormIsInvisible
-FormAutocompleteTest.AjaxSucceeded_FilledFormStillVisible
-FormAutocompleteTest.AjaxSucceeded_FormlessElements
-FormAutocompleteTest.AjaxSucceeded_NoFormInteractionInvisible
-FormAutocompleteTest.AjaxSucceeded_NoLongerVisible
-FormAutocompleteTest.AjaxSucceeded_NoLongerVisible_DifferentActionsSameData
-FormAutocompleteTest.AjaxSucceeded_NoLongerVisible_NoAction
-FormAutocompleteTest.AjaxSucceeded_StillVisible
-FormAutocompleteTest.AutoCompleteOffFormSubmit
-FormAutocompleteTest.AutoCompleteOffInputSubmit
-FormAutocompleteTest.CollectFormlessElements
-FormAutocompleteTest.DynamicAutoCompleteOffFormSubmit
-FormAutocompleteTest.InteractedFormNoLongerFocused_FocusNoLongerOnForm
-FormAutocompleteTest.InteractingInDifferentForms_FocusNoLongerOnForm
-FormAutocompleteTest.NoLongerVisibleBothNoActions
-FormAutocompleteTest.NormalFormSubmit
-FormAutocompleteTest.SubmitEventPrevented
-FormAutofillTest.ClearFormWithNode
-FormAutofillTest.ClearFormWithNodeContainingSelectOne
-FormAutofillTest.ClearFormWithNodeContainingSelectOneForUnownedForm
-FormAutofillTest.ClearFormWithNodeForUnownedForm
-FormAutofillTest.ClearOnlyAutofilledFields
-FormAutofillTest.ClearOnlyAutofilledFieldsForUnownedForm
-FormAutofillTest.ClearPreviewedFormWithAutofilledInitiatingNode
-FormAutofillTest.ClearPreviewedFormWithAutofilledInitiatingNodeForUnownedForm
-FormAutofillTest.ClearPreviewedFormWithElement
-FormAutofillTest.ClearPreviewedFormWithElementForUnownedForm
-FormAutofillTest.ClearPreviewedFormWithElementForUnownedForm
-FormAutofillTest.ClearPreviewedFormWithNonEmptyInitiatingNode
-FormAutofillTest.ClearPreviewedFormWithNonEmptyInitiatingNode
-FormAutofillTest.ClearPreviewedFormWithNonEmptyInitiatingNodeForUnownedForm
-FormAutofillTest.ClickElement
-FormAutofillTest.DetectTextDirectionFromDirectDIRAttribute
-FormAutofillTest.DetectTextDirectionFromDirectStyle
-FormAutofillTest.DetectTextDirectionFromParentDIRAttribute
-FormAutofillTest.DetectTextDirectionFromParentStyle
-FormAutofillTest.DetectTextDirectionWhenAncestorHasInlineStyle
-FormAutofillTest.DetectTextDirectionWhenParentHasBothDIRAttributeAndStyle
-FormAutofillTest.DetectTextDirectionWhenStyleAndDIRAttributMixed
-FormAutofillTest.ExtractForms
-FormAutofillTest.ExtractFormsNoFields
-FormAutofillTest.ExtractFormsSkippedForms
-FormAutofillTest.ExtractFormsTooFewFields
-FormAutofillTest.ExtractFormsTooFewFieldsSkipsCheckable
-FormAutofillTest.ExtractMultipleForms
-FormAutofillTest.FillForm
-FormAutofillTest.FillFormEmptyFormNames
-FormAutofillTest.FillFormEmptyFormNamesForUnownedForm
-FormAutofillTest.FillFormEmptyName
-FormAutofillTest.FillFormEmptyNameForUnownedForm
-FormAutofillTest.FillFormForUnownedForm
-FormAutofillTest.FillFormForUnownedNonASCIIForm
-FormAutofillTest.FillFormForUnownedNonEnglishForm
-FormAutofillTest.FillFormForUnownedUntitledForm
-FormAutofillTest.FillFormIncludingNonFocusableElements
-FormAutofillTest.FillFormMaxLength
-FormAutofillTest.FillFormMaxLengthForUnownedForm
-FormAutofillTest.FillFormNegativeMaxLength
-FormAutofillTest.FillFormNegativeMaxLengthForUnownedForm
-FormAutofillTest.FillFormNonEmptyField
-FormAutofillTest.FillFormNonEmptyFieldForUnownedForm
-FormAutofillTest.FillFormNonEmptyFieldsWithDefaultValues
-FormAutofillTest.FillFormNonEmptyFieldsWithPlaceholderValues
-FormAutofillTest.FindFormForInputElement
-FormAutofillTest.FindFormForInputElementForUnownedForm
-FormAutofillTest.FindFormForTextAreaElement
-FormAutofillTest.FindFormForTextAreaElementForUnownedForm
-FormAutofillTest.FormCache_ExtractNewForms
-FormAutofillTest.InvalidLabels
-FormAutofillTest.Labels
-FormAutofillTest.LabelsInferredFromBold
-FormAutofillTest.LabelsInferredFromDefinitionList
-FormAutofillTest.LabelsInferredFromDefinitionListRatherThanDivTable
-FormAutofillTest.LabelsInferredFromDivSiblingTable
-FormAutofillTest.LabelsInferredFromDivTable
-FormAutofillTest.LabelsInferredFromLabelInDivTable
-FormAutofillTest.LabelsInferredFromListItem
-FormAutofillTest.LabelsInferredFromParagraph
-FormAutofillTest.LabelsInferredFromPreviousTD
-FormAutofillTest.LabelsInferredFromTableAdjacentElements
-FormAutofillTest.LabelsInferredFromTableCell
-FormAutofillTest.LabelsInferredFromTableCellNested
-FormAutofillTest.LabelsInferredFromTableCellTH
-FormAutofillTest.LabelsInferredFromTableEmptyTDs
-FormAutofillTest.LabelsInferredFromTableLabels
-FormAutofillTest.LabelsInferredFromTableRow
-FormAutofillTest.LabelsInferredFromTableTDInterveningElements
-FormAutofillTest.LabelsInferredFromTableWithSpecialElements
-FormAutofillTest.LabelsInferredFromText
-FormAutofillTest.LabelsInferredPriorToImgOrBr
-FormAutofillTest.LabelsInferredWithImageTags
-FormAutofillTest.LabelsInferredWithSameName
-FormAutofillTest.LabelsWithSpans
-FormAutofillTest.MaxLengthFields
-FormAutofillTest.MultipleLabelsPerElement
-FormAutofillTest.OneLabelElement
-FormAutofillTest.OnlyExtractNewForms
-FormAutofillTest.PreviewForm
-FormAutofillTest.PreviewFormForUnownedForm
-FormAutofillTest.PreviewFormForUnownedNonEnglishForm
-FormAutofillTest.PreviewFormForUnownedUntitledForm
-FormAutofillTest.SelectOneAsText
-FormAutofillTest.TextAlignOverridesDirection
-FormAutofillTest.ThreePartPhone
-FormAutofillTest.UnmatchedFormKeywordInQueryOnly
-FormAutofillTest.UnmatchedFormNoURL
-FormAutofillTest.UnmatchedFormNonASCII
-FormAutofillTest.UnmatchedFormPathWithoutKeywords
-FormAutofillTest.UnmatchedFormTitleWithoutKeywords
-FormAutofillTest.UnownedFormElementsAndFieldSetsToFormDataControlOutsideOfFieldset
-FormAutofillTest.UnownedFormElementsAndFieldSetsToFormDataFieldsets
-FormAutofillTest.UnownedFormElementsAndFieldSetsToFormDataWithForm
-FormAutofillTest.WebFormControlElementToClickableFormField
-FormAutofillTest.WebFormControlElementToFormField
-FormAutofillTest.WebFormControlElementToFormFieldAutocompleteOff
-FormAutofillTest.WebFormControlElementToFormFieldAutocompletetype
-FormAutofillTest.WebFormControlElementToFormFieldAutofilled
-FormAutofillTest.WebFormControlElementToFormFieldInvalidType
-FormAutofillTest.WebFormControlElementToFormFieldLongSelect
-FormAutofillTest.WebFormControlElementToFormFieldMaxLength
-FormAutofillTest.WebFormControlElementToFormFieldMonthInput
-FormAutofillTest.WebFormControlElementToFormFieldSelect
-FormAutofillTest.WebFormControlElementToFormFieldSelect_ExtraAttributes
-FormAutofillTest.WebFormControlElementToFormFieldTextArea
-FormAutofillTest.WebFormControlElementToPasswordFormField
-FormAutofillTest.WebFormElementConsiderNonControlLabelableElements
-FormAutofillTest.WebFormElementToFormData
-FormAutofillTest.WebFormElementToFormDataAutocomplete
-FormAutofillTest.WebFormElementToFormData_AutocompleteOff_OnField
-FormAutofillTest.WebFormElementToFormData_AutocompleteOff_OnForm
-FormAutofillTest.WebFormElementToFormData_CssClasses
-FormAutofillTest.WebFormElementToFormData_IdAttributes
-FormClassifierTest.SigninForm
-FormClassifierTest.SigninFormWithInvisibleFieldsHTML
-FormClassifierTest.SigninFormWithTextFeatureInFormTagHTML
-FormClassifierTest.SignupFormWithManyCheckboxesHTML
-FormClassifierTest.SignupFormWithOtherFieldsHTML
-FormClassifierTest.SignupFormWithSeveralPasswordFieldsHTML
-FormClassifierTest.SignupFormWithSeveralTextFields
-FormClassifierTest.SignupFormWithSigninButtonHTML
-FormClassifierTest.SignupFormWithSigninTextFeatureAndManyFieldsHTML
-FormClassifierTest.SignupFormWithTextFeatureInInputElementHTML
-FormClassifierTest.SomeFormWithoutPasswordFields
-FormClassifierTest.kChangeFormWithTreePasswordFieldsHTML
-InstantProcessNavigationTest.ForkForNavigationsFromInstantProcess
-InstantProcessNavigationTest.ForkForNavigationsToSearchURLs
-PageClickTrackerTest.PageClickTrackerClickDisabledInputDoesNotResetClickCounter
-PageClickTrackerTest.PageClickTrackerDisabledInputClickedNoEvent
-PageClickTrackerTest.PageClickTrackerInputClicked
-PageClickTrackerTest.PageClickTrackerInputFocusedAndClicked
-PageClickTrackerTest.PageClickTrackerInputRightClicked
-PageClickTrackerTest.PageClickTrackerScaledTextareaClicked
-PageClickTrackerTest.PageClickTrackerScaledTextareaTapped
-PageClickTrackerTest.PageClickTrackerTapNearEdgeIsPageClick
-PageClickTrackerTest.PageClickTrackerTextAreaClicked
-PageClickTrackerTest.PageClickTrackerTextAreaFocusedAndClicked
-PasswordAutofillAgentTest.AutocompletePasswordForReadonlyUsernameMatched
-PasswordAutofillAgentTest.AutofillNoUsernameWhenOtherCredentialsStored
-PasswordAutofillAgentTest.ClearPreviewWithAutofilledUsernameAndPassword
-PasswordAutofillAgentTest.ClearPreviewWithNotAutofilledUsernameAndPassword
-PasswordAutofillAgentTest.ClearPreviewWithPasswordAutofilled
-PasswordAutofillAgentTest.ClearPreviewWithUsernameAutofilled
-PasswordAutofillAgentTest.ClickAndSelect
-PasswordAutofillAgentTest.CredentialsOnClick
-PasswordAutofillAgentTest.FillOnAccountSelectOnly
-PasswordAutofillAgentTest.FillOnAccountSelectOnlyCredentialsOnPasswordClick
-PasswordAutofillAgentTest.FillOnAccountSelectOnlyNoCredentialsOnPasswordClick
-PasswordAutofillAgentTest.FillOnAccountSelectOnlyNoUsername
-PasswordAutofillAgentTest.FillOnAccountSelectOnlyReadonlyNotPreferredUsername
-PasswordAutofillAgentTest.FillOnAccountSelectOnlyReadonlyUnknownUsername
-PasswordAutofillAgentTest.FillOnAccountSelectOnlyReadonlyUsername
-PasswordAutofillAgentTest.FillSuggestion
-PasswordAutofillAgentTest.FillSuggestionIfUsernameReadOnly
-PasswordAutofillAgentTest.FillSuggestionPasswordChangeForms
-PasswordAutofillAgentTest.FillSuggestionPasswordChangeFormsOnlyPassword
-PasswordAutofillAgentTest.FindingFieldsWithAutofillPredictions
-PasswordAutofillAgentTest.FindingUsernameWithoutAutofillPredictions
-PasswordAutofillAgentTest.FormFillDataMustHaveUsername
-PasswordAutofillAgentTest.GestureRequiredTest
-PasswordAutofillAgentTest.IgnoreNotPasswordFields
-PasswordAutofillAgentTest.InPageNavigationSubmissionUsernameIsEmpty
-PasswordAutofillAgentTest.InitialAutocomplete
-PasswordAutofillAgentTest.InitialAutocompleteForEmptyAction
-PasswordAutofillAgentTest.InitialAutocompleteForMatchingFilledField
-PasswordAutofillAgentTest.InputWithNoForms
-PasswordAutofillAgentTest.IsWebElementVisibleTest
-PasswordAutofillAgentTest.NoAction_NoPromptForAJAXSubmitWithoutNavigationAndNewElementAppeared
-PasswordAutofillAgentTest.NoAutocompleteForFilledFieldUnmatched
-PasswordAutofillAgentTest.NoAutocompleteForPasswordFieldUsernames
-PasswordAutofillAgentTest.NoAutocompleteForTextFieldPasswords
-PasswordAutofillAgentTest.NoAutocompletePasswordForReadonlyUsernameUnmatched
-PasswordAutofillAgentTest.NoCredentialsOnPasswordClick
-PasswordAutofillAgentTest.NoDOMActivationTest
-PasswordAutofillAgentTest.NoForm_NoPromptForAJAXSubmitWithoutNavigationAndElementsVisible
-PasswordAutofillAgentTest.NoForm_NoPromptForAJAXSubmitWithoutNavigationAndNewElementAppeared
-PasswordAutofillAgentTest.NoForm_PromptForAJAXSubmitWithoutNavigation
-PasswordAutofillAgentTest.NoInitialAutocompleteForReadOnlyPassword
-PasswordAutofillAgentTest.NoPartialMatchForPrefilledUsername
-PasswordAutofillAgentTest.NoopEditingDoesNotOverwriteManuallyEditedPassword
-PasswordAutofillAgentTest.NotAutofillNoUsername
-PasswordAutofillAgentTest.NotShowPopupPasswordField
-PasswordAutofillAgentTest.OnChangeLoggingState_Activated
-PasswordAutofillAgentTest.OnChangeLoggingState_Deactivated
-PasswordAutofillAgentTest.OnChangeLoggingState_NoMessage
-PasswordAutofillAgentTest.PasswordAutofillTriggersOnChangeEventsOnLoad
-PasswordAutofillAgentTest.PasswordAutofillTriggersOnChangeEventsWaitForUsername
-PasswordAutofillAgentTest.PasswordGenerationSupersedesAutofill
-PasswordAutofillAgentTest.PasswordGenerationTriggered_GeneratedPassword
-PasswordAutofillAgentTest.PasswordGenerationTriggered_TypedPassword
-PasswordAutofillAgentTest.PasswordNotClearedOnEdit
-PasswordAutofillAgentTest.PreviewSuggestion
-PasswordAutofillAgentTest.PreviewSuggestionIfUsernameReadOnly
-PasswordAutofillAgentTest.PreviewSuggestionSelectionRange
-PasswordAutofillAgentTest.ReadonlyPasswordFieldOnSubmit
-PasswordAutofillAgentTest.RememberAutofilledUsername
-PasswordAutofillAgentTest.RememberChosenUsernamePassword
-PasswordAutofillAgentTest.RememberFieldPropertiesOnInPageNavigation
-PasswordAutofillAgentTest.RememberFieldPropertiesOnSubmit
-PasswordAutofillAgentTest.RememberLastAutofilledUsernameAndPasswordOnSubmit_ScriptChanged
-PasswordAutofillAgentTest.RememberLastNonEmptyUsernameAndPasswordOnSubmit_New
-PasswordAutofillAgentTest.RememberLastNonEmptyUsernameAndPasswordOnSubmit_ScriptCleared
-PasswordAutofillAgentTest.RememberLastNonEmptyUsernameAndPasswordOnSubmit_UserCleared
-PasswordAutofillAgentTest.RememberLastTypedAfterAutofilledUsernameAndPasswordOnSubmit_ScriptChanged
-PasswordAutofillAgentTest.RememberLastTypedUsernameAndPasswordOnSubmit_ScriptChanged
-PasswordAutofillAgentTest.SendPasswordFormsTest_CannotCreatePasswordForm
-PasswordAutofillAgentTest.SendPasswordFormsTest_EmptyForm
-PasswordAutofillAgentTest.SendPasswordFormsTest_FormWithoutPasswords
-PasswordAutofillAgentTest.SendPasswordFormsTest_NonDisplayedForm
-PasswordAutofillAgentTest.SendPasswordFormsTest_NonVisibleForm
-PasswordAutofillAgentTest.SendPasswordFormsTest_PasswordChangeForm
-PasswordAutofillAgentTest.SendPasswordFormsTest_Redirection
-PasswordAutofillAgentTest.SendPasswordFormsTest_ReloadTab
-PasswordAutofillAgentTest.SendPasswordFormsTest_VisibleFormWithNoUsername
-PasswordAutofillAgentTest.ShowAutofillSignaturesFlag
-PasswordAutofillAgentTest.ShowPopupOnAutofilledPasswordField
-PasswordAutofillAgentTest.ShowPopupOnEmptyPasswordField
-PasswordAutofillAgentTest.ShowSuggestionForNonUsernameFieldForms
-PasswordAutofillAgentTest.SuggestMultiplePasswordFields
-PasswordAutofillAgentTest.SuggestPasswordFieldSignInForm
-PasswordAutofillAgentTest.SuggestWhenJavaScriptUpdatesFieldNames
-PasswordAutofillAgentTest.SuggestionsOnFormContainingAmbiguousOrEmptyNames
-PasswordAutofillAgentTest.SuggestionsOnPasswordFieldOfChangePasswordForm
-PasswordAutofillAgentTest.SuggestionsOnUsernameFieldOfChangePasswordForm
-PasswordAutofillAgentTest.UsernameChangedAfterPasswordInput_FormSubmitted
-PasswordAutofillAgentTest.UsernameChangedAfterPasswordInput_InPageNavigation
-PasswordAutofillAgentTest.WaitUsername
-PasswordGenerationAgentTest.AccountCreationFormsDetectedTest
-PasswordGenerationAgentTest.AutocompleteAttributesTest
-PasswordGenerationAgentTest.BlacklistedTest
-PasswordGenerationAgentTest.BlurTest
-PasswordGenerationAgentTest.ChangePasswordFormDetectionTest
-PasswordGenerationAgentTest.ConfirmationFieldVoteFromServer
-PasswordGenerationAgentTest.DetectionTest
-PasswordGenerationAgentTest.DynamicFormTest
-PasswordGenerationAgentTest.EditingTest
-PasswordGenerationAgentTest.FillTest
-PasswordGenerationAgentTest.FormClassifierDisabled
-PasswordGenerationAgentTest.FormClassifierVotesSigninForm
-PasswordGenerationAgentTest.FormClassifierVotesSignupForm
-PasswordGenerationAgentTest.JavascriptClearedTheField
-PasswordGenerationAgentTest.ManualGenerationChangeFocusTest
-PasswordGenerationAgentTest.ManualGenerationInFormTest
-PasswordGenerationAgentTest.ManualGenerationNoFormTest
-PasswordGenerationAgentTest.MaximumOfferSize
-PasswordGenerationAgentTest.MessagesAfterAccountSignupFormFound
-PasswordGenerationAgentTest.MultiplePasswordFormsTest
-PasswordGenerationAgentTest.PresavingGeneratedPassword
-PasswordGenerationAgentTest.RevealPassword
-PhishingClassifierDelegateTest.DuplicatePageCapture
-PhishingClassifierDelegateTest.IgnorePreliminaryCapture
-PhishingClassifierDelegateTest.Navigation
-PhishingClassifierDelegateTest.NoScorer
-PhishingClassifierDelegateTest.NoScorer_Ref
-PhishingClassifierDelegateTest.NoStartPhishingDetection
-PhishingClassifierDelegateTest.PhishingDetectionDone
-PhishingDOMFeatureExtractorTest.Continuation
-PhishingDOMFeatureExtractorTest.FormFeatures
-PhishingDOMFeatureExtractorTest.LinkFeatures
-PhishingDOMFeatureExtractorTest.ScriptAndImageFeatures
-PhishingDOMFeatureExtractorTest.SubFrames
-PhishingDOMFeatureExtractorTest.SubframeRemoval
-PnaclHeaderTest.TestHasPnaclHeader
-PolicyCertVerifierTest.VerifyTrustedCert
-PolicyCertVerifierTest.VerifyUntrustedCert
-PolicyCertVerifierTest.VerifyUsingAdditionalTrustAnchor
-PrerenderBrowserTestWithNaCl.PrerenderNaClPluginEnabled
-ScriptContextTest.GetEffectiveDocumentURL
-SiteEngagementUiBrowserTest.Basic
-TabCaptureCaptureOffscreenTabTest.DetermineInitialSize
-TestStatsDictionaryTest.ReportFilterStats
-TestStatsDictionaryTest.ReportForEach
-TestStatsDictionaryTest.ReportGetAll
-TestStatsDictionaryTest.ReportGetByType
-TestStatsDictionaryTest.ReportGetStats
-TestStatsDictionaryTest.StatsVerifyMembers
-TestStatsDictionaryTest.TestStatsDictionaryShouldKeepReportAlive
-ThreatDOMDetailsTest.Everything
-TranslateHelperBrowserTest.BackToTranslatablePage
-TranslateHelperBrowserTest.LanguageCommonMistakesAreCorrected
-TranslateHelperBrowserTest.LanguageMetaTag
-TranslateHelperBrowserTest.LanguageMetaTagCase
-TranslateHelperBrowserTest.MultipleDifferentTranslations
-TranslateHelperBrowserTest.MultipleSimilarTranslations
-TranslateHelperBrowserTest.TranslatablePage
-TranslateHelperBrowserTest.TranslateFailure
-TranslateHelperBrowserTest.TranslateLibNeverReady
-TranslateHelperBrowserTest.TranslateSuccess
-TranslateHelperBrowserTest.UndefinedSourceLang
-TranslateScriptBrowserTest.CallbackGetBooleanError
-TranslateScriptBrowserTest.CallbackGetNumberError1
-TranslateScriptBrowserTest.CallbackGetNumberError2
-TranslateScriptBrowserTest.ElementLoadFailure
-TranslateScriptBrowserTest.ElementLoadSuccess
-TranslateScriptBrowserTest.TranslateFail
-TranslateScriptBrowserTest.TranslateSuccess
+# TODO: reenable, fails because of http://crbug.com/753593.
+-ForceMaximizeOnFirstRunTest.PRE_TwoRuns
+-ForceMaximizeOnFirstRunTest.TwoRuns
+
+# TODO: fails because of screen capture. http://crbug.com/754899.
+-ChromeScreenshotGrabberBrowserTest.TakeScreenshot
+-LoginFeedbackTest.Basic
+-TabCaptureApiPixelTest.EndToEndThroughWebRTC
+-TabCaptureApiPixelTest.EndToEndWithoutRemoting
+-TabCaptureApiPixelTest.OffscreenTabEndToEnd
+-TabCaptureApiPixelTest.OffscreenTabEvilTests
+
+# TODO: fix, see http://crbug.com/755272.
+-PDFExtensionTest.RedirectsFailInPlugin
+
+# TODO: fix, http://crbug.com/759156.
+-PolicyDisplayRotationDefault/DisplayRotationDefaultTest.RefreshSecondDisplay/2
+
+# TODO: fix, see http://crbug.com/755303.
+-StartupMetricsTest.ReportsValues
+
+# TODO: fix, see http://crbug.com/755318.
+-WebViewScrollBubbling/WebViewGuestScrollTouchTest.TestGuestGestureScrollsBubble/0
+-WebViewScrollBubbling/WebViewGuestScrollTouchTest.TestGuestGestureScrollsBubble/1
+-WebViewScrollBubbling/WebViewGuestScrollTouchTest.TestGuestGestureScrollsBubble/2
+-WebViewScrollBubbling/WebViewGuestScrollTouchTest.TestGuestGestureScrollsBubble/3
+
+# TODO: fix, http://crbug.com/755328
+-NaClExtensionTest.MainFrameIsRemote
+-WebViewTests/WebViewDPITest.Shim_TestAutosizeBeforeNavigation/0
+-WebViewTests/WebViewDPITest.Shim_TestAutosizeBeforeNavigation/1
+-WebViewTests/WebViewDPITest.Shim_TestAutosizeHeight/0
+-WebViewTests/WebViewDPITest.Shim_TestAutosizeHeight/1
+-WebViewTests/WebViewDPITest.Shim_TestAutosizeRemoveAttributes/0
+-WebViewTests/WebViewDPITest.Shim_TestAutosizeRemoveAttributes/1
+-WebViewTests/WebViewFocusTest.TouchFocusesEmbedder/0
+-WebViewTests/WebViewSizeTest.AutoSize/0
+-WebViewTests/WebViewSizeTest.AutoSize/1
+-WebViewTests/WebViewSizeTest.Shim_TestAutosizeBeforeNavigation/0
+-WebViewTests/WebViewSizeTest.Shim_TestAutosizeBeforeNavigation/1
+-WebViewTests/WebViewSizeTest.Shim_TestAutosizeHeight/0
+-WebViewTests/WebViewSizeTest.Shim_TestAutosizeHeight/1
+-WebViewTests/WebViewSizeTest.Shim_TestAutosizeRemoveAttributes/0
+-WebViewTests/WebViewSizeTest.Shim_TestAutosizeRemoveAttributes/1
+-WebViewTests/WebViewSizeTest.Shim_TestResizeEvents/0
+-WebViewTests/WebViewSizeTest.Shim_TestResizeEvents/1
+-WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewResizesContent/0
+-WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewResizesContent/1
+-WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/0
+-WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/1
+-WebViewTests/WebViewTest.InterstitialPageFocusedWidget/1
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index f18e0ee..6582acc 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -672,7 +672,7 @@
             ]
         }
     ],
-    "DataReductionProxyPreviewsBlacklistTransition": [
+    "DataReductionProxyPreviewsBlackListTransition": [
         {
             "platforms": [
                 "android"
@@ -3283,6 +3283,21 @@
             ]
         }
     ],
+    "UpdateMenuItem": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "StorageRequired500",
+                    "params": {
+                        "min_required_storage_for_update_mb": "500"
+                    }
+                }
+            ]
+        }
+    ],
     "V8AsmJSToWasm": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index cf7db01..b46d0815 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -1542,6 +1542,7 @@
 Bug(none) svg/custom/marker-zero-length-linecaps.svg [ Failure ]
 
 # Text failures due to layerization differences
+Bug(none) compositing/overflow/no-excessive-clip-parent-if-parent-escaped.html [ Failure ]
 Bug(none) compositing/overflow/scroller-with-border-radius.html [ Failure ]
 Bug(none) compositing/will-change/will-change-contents-suppresses-compositing.html [ Failure Crash ]
 Bug(none) css3/blending/mix-blend-mode-2nd-stacking-context-composited.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index e2b460d0..04df249 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -195,6 +195,29 @@
 [ Mac ] external/wpt/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009b.html [ WontFix ]
 [ Mac ] external/wpt/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html [ WontFix ]
 
+# Failing css-transforms-2 web platform tests.
+crbug.com/753080 external/wpt/css/css-transforms-2/backface-visibility-hidden-001.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/rotate_x_45deg.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/rotate_y_45deg.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform-3d-rotateY-stair-below-001.xht [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-image-scale-001.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-image-scale-002.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-matrix3d-001.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-perspective-003.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-perspective-004.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-perspective-005.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-preserve3d-010.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-preserve3d-013.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-rotatex-perspective-003.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-scale-005.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-scale-006.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-scale-007.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-sorting-002.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-sorting-004.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-sorting-006.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/ttwf-css-3d-polygon-cycle-mismatch.html [ WontFix ]
+crbug.com/753080 external/wpt/css/css-transforms-2/ttwf-css-3d-polygon-cycle.html [ WontFix ]
+
 # We could fix this test for us and upstream it if the test shell user agent
 # would let us differentiate test_shell and WebKit DumpTreeNode.
 crbug.com/7482 [ Win Mac ] http/tests/misc/timer-vs-loading.html [ WontFix ]
@@ -249,7 +272,6 @@
 # WPT subdirectories without owners.
 external/wpt/accelerometer [ WontFix ]
 external/wpt/assumptions [ WontFix ]
-external/wpt/css/css-transforms-2 [ WontFix ]
 external/wpt/css-values [ WontFix ]
 external/wpt/css/css-values-3 [ WontFix ]
 external/wpt/gyroscope [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary-expected.html
new file mode 100644
index 0000000..13372c93
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary-expected.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<div style="margin-left:100px; margin-top:100px; width:100px; height:100px; background:green;"></div>
+This test succeeds if a non-rotated 100x100 green box is shown above.
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary.html b/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary.html
new file mode 100644
index 0000000..7ef81b7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<div style="overflow:hidden; margin-left:100px; margin-top:100px; width:100px; height:100px;">
+  <div style="position:relative; left:-50px; top:-50px; width:200px; height:200px; transform:rotate(30deg);">
+    <div style="will-change:opacity; overflow:hidden;">
+      <div style="position:absolute; width:200px; height:200px; background:green;"></div>
+    </div>
+  </div>
+</div>
+This test succeeds if a non-rotated 100x100 green box is shown above.
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent-expected.html
new file mode 100644
index 0000000..8cf0a8f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent-expected.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+This test succeeds if a box escapes clip from a clipped stacking context
+correctly while its clipping container paints after it.
+<div style="width:200px; height:200px; background:blue;"></div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent.html b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent.html
new file mode 100644
index 0000000..ce5e73c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+This test succeeds if a box escapes clip from a clipped stacking context
+correctly while its clipping container paints after it.
+<div style="overflow:hidden; width:100px; height:100px;">
+  <div style="will-change:opacity;">
+    <div style="position:absolute; overflow:hidden; width:200px; height:200px; background:blue;">
+      <div style="position:relative; z-index:-1; width:250px; height:250px; background:green; will-change:opacity;">
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip-expected.html
new file mode 100644
index 0000000..ab20a0f2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip-expected.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+This test verifies fixed-pos element escapes clip correctly, and then apply clip from its true clipping container even if the container is not composited.
+It succeeds if a 100x100 green box is shown below.
+<div style="width:100px; height:100px; background:green;"></div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip.html
new file mode 100644
index 0000000..ab98daf2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+This test verifies fixed-pos element escapes clip correctly, and then apply clip from its true clipping container even if the container is not composited.
+It succeeds if a 100x100 green box is shown below.
+<div style="overflow:hidden; width:100px; height:100px;">
+  <div style="transform:translateX(0); width:100px; height:100px;">
+    <div style="position:relative; width:10px; height:10px; overflow:hidden;">
+      <div style="will-change:transform;">Promote</div>
+      <div style="position:absolute; z-index:0; top:0; left:0; width:300px; height:200px;">
+        <div style="position:fixed; left:0; top:0; width:200px; height:200px; background:green;">
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container-expected.html
new file mode 100644
index 0000000..430063a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container-expected.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+This test verifies fixed-pos element escapes clip correctly, even if its true clipping container is not a stacking context.
+It succeeds if a 50x50 green box is shown below.
+<div style="width:50px; height:50px; background:green;"></div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container.html
new file mode 100644
index 0000000..0ac0617
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+This test verifies fixed-pos element escapes clip correctly, even if its true clipping container is not a stacking context.
+It succeeds if a 50x50 green box is shown below.
+<div style="overflow:hidden; width:100px; height:100px;">
+  <div style="transform:translateX(0); width:100px; height:100px;">
+    <div style="position:relative; width:10px; height:10px; overflow:hidden;">
+      <div style="will-change:transform;">Promote</div>
+      <div style="position:absolute; z-index:0; top:0; left:0; width:300px; height:200px;">
+        <div style="position:fixed; left:0; top:0; width:50px; height:50px; background:green;">
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.png b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.png
new file mode 100644
index 0000000..cba76e35
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.txt
new file mode 100644
index 0000000..85819e0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.txt
@@ -0,0 +1,31 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "drawsContent": true
+    },
+    {
+      "name": "LayoutBlockFlow DIV",
+      "position": [8, 8],
+      "bounds": [100, 100],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "backgroundColor": "#FF0000"
+    },
+    {
+      "name": "Child Containment Layer",
+      "bounds": [100, 100]
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV",
+      "bounds": [50, 50],
+      "contentsOpaque": true,
+      "drawsContent": true,
+      "backgroundColor": "#008000",
+      "hasClipParent": true
+    }
+  ]
+}
+
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped.html b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped.html
new file mode 100644
index 0000000..c34abf3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<div style="width:100px; height:100px; will-change:opacity; overflow:hidden; background:red;">
+  <div style="position:absolute; z-index:0; width:50px; height:50px; background:green;">
+    <div style="position:absolute; width:25px; height:25px; background:blue;"></div>
+  </div>
+</div><!-- This test suceeds if the innermost element does not set clip parent, and is not composited unnecessarily. -->
+<script>
+if (window.testRunner) {
+  testRunner.dumpAsTextWithPixelResults();
+  testRunner.setCustomTextOutput(
+    internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_CLIP_AND_SCROLL_PARENTS));
+}
+</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt
index fe0f1835..9a67aee 100644
--- a/third_party/WebKit/LayoutTests/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt
@@ -37,7 +37,8 @@
       "contentsOpaque": true,
       "drawsContent": true,
       "backfaceVisibility": "hidden",
-      "backgroundColor": "#FFFF00"
+      "backgroundColor": "#FFFF00",
+      "hasClipParent": true
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='tall'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-1-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-1-expected.txt
index 064c879..a6098d03 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-1-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-1-expected.txt
@@ -8,7 +8,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutBlockFlow DIV class='box'",
-          "rect": [128, 120, 200, 200],
+          "rect": [128, 120, 100, 100],
           "reason": "style change"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-5-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-5-expected.txt
index 064c879..a6098d03 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-5-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-5-expected.txt
@@ -8,7 +8,7 @@
       "paintInvalidations": [
         {
           "object": "LayoutBlockFlow DIV class='box'",
-          "rect": [128, 120, 200, 200],
+          "rect": [128, 120, 100, 100],
           "reason": "style change"
         }
       ]
diff --git a/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt b/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt
index fe0f1835..9a67aee 100644
--- a/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt
@@ -37,7 +37,8 @@
       "contentsOpaque": true,
       "drawsContent": true,
       "backfaceVisibility": "hidden",
-      "backgroundColor": "#FFFF00"
+      "backgroundColor": "#FFFF00",
+      "hasClipParent": true
     },
     {
       "name": "LayoutBlockFlow (relative positioned) DIV id='tall'",
diff --git a/third_party/WebKit/Source/core/layout/MinMaxSize.cpp b/third_party/WebKit/Source/core/layout/MinMaxSize.cpp
index 326ebda4..44ea9d5 100644
--- a/third_party/WebKit/Source/core/layout/MinMaxSize.cpp
+++ b/third_party/WebKit/Source/core/layout/MinMaxSize.cpp
@@ -4,6 +4,8 @@
 
 #include "core/layout/MinMaxSize.h"
 
+#include <algorithm>
+
 namespace blink {
 
 LayoutUnit MinMaxSize::ShrinkToFit(LayoutUnit available_size) const {
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_margin_strut.cc b/third_party/WebKit/Source/core/layout/ng/geometry/ng_margin_strut.cc
index d7bb1168..7c06eb5f 100644
--- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_margin_strut.cc
+++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_margin_strut.cc
@@ -4,6 +4,8 @@
 
 #include "core/layout/ng/geometry/ng_margin_strut.h"
 
+#include <algorithm>
+
 namespace blink {
 
 LayoutUnit NGMarginStrut::Sum() const {
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.cc b/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.cc
index d2ceada..c6d17e0 100644
--- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.cc
+++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.cc
@@ -4,6 +4,7 @@
 
 #include "core/layout/ng/geometry/ng_physical_location.h"
 
+#include <ostream>
 #include "platform/wtf/text/WTFString.h"
 
 namespace blink {
@@ -16,4 +17,8 @@
   return String::Format("%dx%d", left.ToInt(), top.ToInt());
 }
 
+std::ostream& operator<<(std::ostream& os, const NGPhysicalLocation& value) {
+  return os << value.ToString();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.h b/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.h
index 2b40c5e8..b72dbd3 100644
--- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.h
+++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.h
@@ -24,10 +24,7 @@
   String ToString() const;
 };
 
-CORE_EXPORT inline std::ostream& operator<<(std::ostream& os,
-                                            const NGPhysicalLocation& value) {
-  return os << value.ToString();
-}
+CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGPhysicalLocation&);
 
 }  // namespace blink
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h b/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h
index 72323d5..5469037 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h
@@ -8,6 +8,8 @@
 #include "core/CoreExport.h"
 #include "core/layout/ng/ng_break_token.h"
 #include "platform/LayoutUnit.h"
+#include "platform/wtf/RefPtr.h"
+#include "platform/wtf/Vector.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc
index e7e9534..19524ffa 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc
@@ -4,6 +4,8 @@
 
 #include "core/layout/ng/ng_layout_opportunity_tree_node.h"
 
+#include "platform/wtf/text/WTFString.h"
+
 namespace blink {
 
 NGLayoutOpportunityTreeNode::NGLayoutOpportunityTreeNode(
@@ -28,4 +30,14 @@
                             : "null");
 }
 
+std::ostream& operator<<(std::ostream& stream,
+                         const NGLayoutOpportunityTreeNode& value) {
+  return stream << value.ToString();
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const NGLayoutOpportunityTreeNode* value) {
+  return out << (value ? value->ToString() : "(null)");
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h
index e0e4caed..028cc7e 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h
@@ -8,6 +8,7 @@
 #include "core/layout/ng/geometry/ng_bfc_rect.h"
 #include "core/layout/ng/geometry/ng_edge.h"
 #include "core/layout/ng/ng_exclusion.h"
+#include "platform/wtf/Vector.h"
 
 namespace blink {
 
@@ -53,15 +54,8 @@
   String ToString() const;
 };
 
-inline std::ostream& operator<<(std::ostream& stream,
-                                const NGLayoutOpportunityTreeNode& value) {
-  return stream << value.ToString();
-}
-
-inline std::ostream& operator<<(std::ostream& out,
-                                const NGLayoutOpportunityTreeNode* value) {
-  return out << (value ? value->ToString() : "(null)");
-}
+std::ostream& operator<<(std::ostream&, const NGLayoutOpportunityTreeNode&);
+std::ostream& operator<<(std::ostream&, const NGLayoutOpportunityTreeNode*);
 
 }  // namespace blink
 #endif  // NGLayoutOpportunityTreeNode_h
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
index 982b6b7..055b12b 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
@@ -4,6 +4,7 @@
 
 #include "core/layout/ng/ng_length_utils.h"
 
+#include <algorithm>
 #include "core/layout/LayoutBox.h"
 #include "core/layout/ng/ng_constraint_space.h"
 #include "core/layout/ng/ng_constraint_space_builder.h"
@@ -527,4 +528,21 @@
       FromPlatformWritingMode(style->GetWritingMode()), style->Direction());
 }
 
+NGLogicalSize CalculateContentBoxSize(
+    const NGLogicalSize border_box_size,
+    const NGBoxStrut& border_scrollbar_padding) {
+  NGLogicalSize size = border_box_size;
+  size.inline_size -= border_scrollbar_padding.InlineSum();
+  size.inline_size = std::max(size.inline_size, LayoutUnit());
+
+  // Our calculated block-axis size may still be indefinite. If so, just leave
+  // the size as NGSizeIndefinite instead of subtracting borders and padding.
+  if (size.block_size != NGSizeIndefinite) {
+    size.block_size -= border_scrollbar_padding.BlockSum();
+    size.block_size = std::max(size.block_size, LayoutUnit());
+  }
+
+  return size;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
index 9ee822f..637e2fa 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
@@ -150,22 +150,9 @@
       ComputeBlockSizeForFragment(constraint_space, style, block_content_size));
 }
 
-inline NGLogicalSize CalculateContentBoxSize(
+NGLogicalSize CalculateContentBoxSize(
     const NGLogicalSize border_box_size,
-    const NGBoxStrut& border_scrollbar_padding) {
-  NGLogicalSize size = border_box_size;
-  size.inline_size -= border_scrollbar_padding.InlineSum();
-  size.inline_size = std::max(size.inline_size, LayoutUnit());
-
-  // Our calculated block-axis size may still be indefinite. If so, just leave
-  // the size as NGSizeIndefinite instead of subtracting borders and padding.
-  if (size.block_size != NGSizeIndefinite) {
-    size.block_size -= border_scrollbar_padding.BlockSum();
-    size.block_size = std::max(size.block_size, LayoutUnit());
-  }
-
-  return size;
-}
+    const NGBoxStrut& border_scrollbar_padding);
 
 }  // namespace blink
 
diff --git a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp
index 3307695..f5e59f8 100644
--- a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp
@@ -197,47 +197,41 @@
   return false;
 }
 
-void BoxPaintInvalidator::InvalidateScrollingContentsBackgroundIfNeeded() {
-  bool paints_onto_scrolling_contents_layer =
-      BackgroundPaintsOntoScrollingContentsLayer();
-  if (!paints_onto_scrolling_contents_layer &&
-      !BackgroundGeometryDependsOnLayoutOverflowRect())
-    return;
+BoxPaintInvalidator::BackgroundInvalidationType
+BoxPaintInvalidator::ComputeBackgroundInvalidation() {
+  if (box_.BackgroundChangedSinceLastPaintInvalidation())
+    return BackgroundInvalidationType::kFull;
+
+  if (!BackgroundGeometryDependsOnLayoutOverflowRect())
+    return BackgroundInvalidationType::kNone;
 
   const LayoutRect& old_layout_overflow = box_.PreviousLayoutOverflowRect();
   LayoutRect new_layout_overflow = box_.LayoutOverflowRect();
 
-  bool should_fully_invalidate_on_scrolling_contents_layer = false;
-  if (box_.BackgroundChangedSinceLastPaintInvalidation()) {
-    if (!paints_onto_scrolling_contents_layer) {
-      // The box should have been set needing full invalidation on style change.
-      DCHECK(box_.ShouldDoFullPaintInvalidation());
-      return;
-    }
-    should_fully_invalidate_on_scrolling_contents_layer = true;
-  } else {
-    // Check change of layout overflow for full or incremental invalidation.
-    if (new_layout_overflow == old_layout_overflow)
-      return;
-    bool should_fully_invalidate =
-        ShouldFullyInvalidateBackgroundOnLayoutOverflowChange(
-            old_layout_overflow, new_layout_overflow);
-    if (!paints_onto_scrolling_contents_layer) {
-      if (should_fully_invalidate) {
-        box_.GetMutableForPainting()
-            .SetShouldDoFullPaintInvalidationWithoutGeometryChange(
-                PaintInvalidationReason::kBackground);
-      }
-      return;
-    }
-    should_fully_invalidate_on_scrolling_contents_layer =
-        should_fully_invalidate;
+  if (new_layout_overflow == old_layout_overflow)
+    return BackgroundInvalidationType::kNone;
+
+  // Layout overflow changed; decide full vs. incremental invalidation.
+  if (ShouldFullyInvalidateBackgroundOnLayoutOverflowChange(
+          old_layout_overflow, new_layout_overflow)) {
+    return BackgroundInvalidationType::kFull;
   }
+  return BackgroundInvalidationType::kIncremental;
+}
+
+void BoxPaintInvalidator::InvalidateScrollingContentsBackground(
+    BackgroundInvalidationType backgroundInvalidationType) {
+  if (!BackgroundPaintsOntoScrollingContentsLayer())
+    return;
+  if (backgroundInvalidationType == BackgroundInvalidationType::kNone)
+    return;
 
   // TODO(crbug.com/732611): Implement raster invalidation of background on
   // scrolling contents layer for SPv2.
   if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
-    if (should_fully_invalidate_on_scrolling_contents_layer) {
+    const LayoutRect& old_layout_overflow = box_.PreviousLayoutOverflowRect();
+    LayoutRect new_layout_overflow = box_.LayoutOverflowRect();
+    if (backgroundInvalidationType == BackgroundInvalidationType::kFull) {
       ObjectPaintInvalidatorWithContext(box_, context_)
           .FullyInvalidatePaint(
               PaintInvalidationReason::kBackgroundOnScrollingContentsLayer,
@@ -258,7 +252,15 @@
 }
 
 PaintInvalidationReason BoxPaintInvalidator::InvalidatePaint() {
-  InvalidateScrollingContentsBackgroundIfNeeded();
+  BackgroundInvalidationType backgroundInvalidationType =
+      ComputeBackgroundInvalidation();
+  if (backgroundInvalidationType == BackgroundInvalidationType::kFull &&
+      !BackgroundPaintsOntoScrollingContentsLayer()) {
+    box_.GetMutableForPainting()
+        .SetShouldDoFullPaintInvalidationWithoutGeometryChange(
+            PaintInvalidationReason::kBackground);
+  }
+  InvalidateScrollingContentsBackground(backgroundInvalidationType);
 
   PaintInvalidationReason reason = ComputePaintInvalidationReason();
   if (reason == PaintInvalidationReason::kIncremental) {
diff --git a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.h b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.h
index 26f4e8e2..f5092fc 100644
--- a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.h
+++ b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.h
@@ -35,7 +35,10 @@
   bool ShouldFullyInvalidateBackgroundOnLayoutOverflowChange(
       const LayoutRect& old_layout_overflow,
       const LayoutRect& new_layout_overflow);
-  void InvalidateScrollingContentsBackgroundIfNeeded();
+
+  enum BackgroundInvalidationType { kNone = 0, kIncremental, kFull };
+  BackgroundInvalidationType ComputeBackgroundInvalidation();
+  void InvalidateScrollingContentsBackground(BackgroundInvalidationType);
 
   PaintInvalidationReason ComputePaintInvalidationReason();
 
diff --git a/third_party/WebKit/Source/core/paint/ClipRect.cpp b/third_party/WebKit/Source/core/paint/ClipRect.cpp
index bb0bd63..5b09a9b 100644
--- a/third_party/WebKit/Source/core/paint/ClipRect.cpp
+++ b/third_party/WebKit/Source/core/paint/ClipRect.cpp
@@ -27,6 +27,7 @@
 #include "core/paint/ClipRect.h"
 
 #include "core/layout/HitTestLocation.h"
+#include "platform/wtf/text/WTFString.h"
 
 namespace blink {
 
@@ -34,4 +35,8 @@
   return hit_test_location.Intersects(rect_);
 }
 
+String ClipRect::ToString() const {
+  return rect_.ToString();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/ClipRect.h b/third_party/WebKit/Source/core/paint/ClipRect.h
index 8f1a258..fd14dff6 100644
--- a/third_party/WebKit/Source/core/paint/ClipRect.h
+++ b/third_party/WebKit/Source/core/paint/ClipRect.h
@@ -78,7 +78,7 @@
   bool IsEmpty() const { return rect_.IsEmpty(); }
   bool Intersects(const HitTestLocation&) const;
 
-  String ToString() const { return rect_.ToString(); }
+  String ToString() const;
 
  private:
   LayoutRect rect_;
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
index dae870d..8c222c0 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
@@ -508,16 +508,14 @@
 }
 
 bool CompositedLayerMapping::AncestorRoundedCornersWillClip(
-    const FloatRect& child_rect_in_nearest_clipping_space,
-    const PaintLayer* nearest_clipping_ancestor,
-    const PaintLayer* compositing_ancestor) {
-  // Find all clips up to the ancestor compositing container to correctly
-  // handle nested clips.
+    const FloatRect& bounds_in_ancestor_space,
+    const PaintLayer* clip_inheritance_ancestor) {
+  // Collect all border-radius clips between us and the state we inherited.
   LayoutPoint zero_offset;
   Vector<FloatRoundedRect> rounded_rect_clips;
   LayerClipRecorder::CollectRoundedRectClips(
-      *nearest_clipping_ancestor, compositing_ancestor, zero_offset, true,
-      LayerClipRecorder::kIncludeSelfForBorderRadius, rounded_rect_clips);
+      owning_layer_, clip_inheritance_ancestor, zero_offset, true,
+      LayerClipRecorder::kDoNotIncludeSelfForBorderRadius, rounded_rect_clips);
 
   for (auto clip_rect : rounded_rect_clips) {
     FloatRect inner_clip_rect = clip_rect.RadiusCenterRect();
@@ -527,8 +525,8 @@
     // entirely outside the rectangular border (ignoring rounded corners) so
     // is also unaffected by the rounded corners. In both cases the existing
     // rectangular clip is adequate and the mask is unnecessary.
-    if (!inner_clip_rect.Contains(child_rect_in_nearest_clipping_space) &&
-        child_rect_in_nearest_clipping_space.Intersects(clip_rect.Rect())) {
+    if (!inner_clip_rect.Contains(bounds_in_ancestor_space) &&
+        bounds_in_ancestor_space.Intersects(clip_rect.Rect())) {
       return true;
     }
   }
@@ -546,45 +544,30 @@
   if (!owning_layer_.Parent())
     return;
 
-  const PaintLayer* compositing_ancestor =
-      owning_layer_.EnclosingLayerWithCompositedLayerMapping(kExcludeSelf);
-  if (!compositing_ancestor)
-    return;
-
-  const LayoutBoxModelObject* clipping_container =
-      owning_layer_.ClippingContainer();
-  if (!clipping_container)
-    return;
-
-  if (clipping_container->EnclosingLayer() == scroll_parent)
-    return;
-
-  if (compositing_ancestor->GetLayoutObject().IsDescendantOf(
-          clipping_container))
-    return;
-
-  // We ignore overflow clip here; we want composited overflow content to
-  // behave as if it lives in an unclipped universe so it can prepaint, etc.
-  // This means that we need to check if we are actually clipped before
-  // setting up m_ancestorClippingLayer otherwise
-  // updateAncestorClippingLayerGeometry will fail as the clip rect will be
-  // infinite.
+  // Compute the clips below the layer we inherit clip state from.
+  // Ignore the clips of the inherited layer, because they are already a part
+  // of the inherited state.
   // FIXME: this should use cached clip rects, but this sometimes give
   // inaccurate results (and trips the ASSERTS in PaintLayerClipper).
-  ClipRectsContext clip_rects_context(compositing_ancestor, kUncachedClipRects,
+  const PaintLayer* clip_inheritance_ancestor = ClipInheritanceAncestor(
+      owning_layer_.EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
+  ClipRectsContext clip_rects_context(clip_inheritance_ancestor,
+                                      kUncachedClipRects,
                                       kIgnorePlatformOverlayScrollbarSize);
   clip_rects_context.SetIgnoreOverflowClip();
 
   ClipRect clip_rect;
   owning_layer_.Clipper(PaintLayer::kDoNotUseGeometryMapper)
       .CalculateBackgroundClipRect(clip_rects_context, clip_rect);
-  IntRect parent_clip_rect = PixelSnappedIntRect(clip_rect.Rect());
-  owning_layer_is_clipped = parent_clip_rect != LayoutRect::InfiniteIntRect();
+  if (clip_rect.Rect() == LayoutRect(LayoutRect::InfiniteIntRect()))
+    return;
+
+  owning_layer_is_clipped = true;
 
   // TODO(schenney): CSS clips are not applied to composited children, and
   // should be via mask or by compositing the parent too.
   // https://bugs.chromium.org/p/chromium/issues/detail?id=615870
-  if (!(owning_layer_is_clipped && clip_rect.HasRadius()))
+  if (!clip_rect.HasRadius())
     return;
 
   // If there are any rounded corners we must use a mask in the presence of
@@ -595,24 +578,48 @@
     return;
   }
 
+  // TODO(crbug.com/756265): Our composited in-flow descendants expect to
+  // inherit our clip, and we shouldn't omit rounded clip if some descendants
+  // could be clipped by it.
   FloatRect bounds_in_ancestor_space =
       GetLayoutObject()
           .LocalToAncestorQuad(FloatRect(composited_bounds_),
-                               &compositing_ancestor->GetLayoutObject(),
+                               &clip_inheritance_ancestor->GetLayoutObject(),
                                kUseTransforms)
           .BoundingBox();
   owning_layer_is_masked = AncestorRoundedCornersWillClip(
-      bounds_in_ancestor_space, clipping_container->Layer(),
-      compositing_ancestor);
+      bounds_in_ancestor_space, clip_inheritance_ancestor);
 }
 
-const PaintLayer* CompositedLayerMapping::ScrollParent() {
+const PaintLayer* CompositedLayerMapping::ScrollParent() const {
   const PaintLayer* scroll_parent = owning_layer_.ScrollParent();
   if (scroll_parent && !scroll_parent->NeedsCompositedScrolling())
     return nullptr;
   return scroll_parent;
 }
 
+const PaintLayer* CompositedLayerMapping::CompositedClipParent() const {
+  const PaintLayer* clip_parent = owning_layer_.ClipParent();
+  return clip_parent ? clip_parent->EnclosingLayerWithCompositedLayerMapping(
+                           kIncludeSelf)
+                     : nullptr;
+}
+
+const PaintLayer* CompositedLayerMapping::ClipInheritanceAncestor(
+    const PaintLayer* compositing_container) const {
+  // Determine the clip state we are going to inherit.
+  // There are three sources a layer inherits its clip state from, in the
+  // order of priority (see cc/trees/property_tree_builder.cc):
+  // 1. Clip parent
+  // 2. Scroll parent
+  // 3. Parent layer
+  if (const PaintLayer* clip_parent = CompositedClipParent())
+    return clip_parent;
+  if (const PaintLayer* scroll_parent = ScrollParent())
+    return scroll_parent;
+  return compositing_container;
+}
+
 bool CompositedLayerMapping::UpdateGraphicsLayerConfiguration() {
   DCHECK_EQ(owning_layer_.Compositor()->Lifecycle().GetState(),
             DocumentLifecycle::kInCompositingUpdate);
@@ -1271,28 +1278,65 @@
   if (!compositing_container || !ancestor_clipping_layer_)
     return;
 
-  ClipRectsContext clip_rects_context(compositing_container,
+  const PaintLayer* clip_inheritance_ancestor =
+      ClipInheritanceAncestor(compositing_container);
+  ClipRectsContext clip_rects_context(clip_inheritance_ancestor,
                                       kPaintingClipRectsIgnoringOverflowClip,
                                       kIgnorePlatformOverlayScrollbarSize);
+  // Note: kPaintingClipRectsIgnoringOverflowClip implies SetIgnoreOverflowClip.
 
-  ClipRect parent_clip_rect;
+  ClipRect clip_rect;
   owning_layer_.Clipper(PaintLayer::kDoNotUseGeometryMapper)
-      .CalculateBackgroundClipRect(clip_rects_context, parent_clip_rect);
+      .CalculateBackgroundClipRect(clip_rects_context, clip_rect);
+  DCHECK(clip_rect.Rect() != LayoutRect(LayoutRect::InfiniteIntRect()));
 
-  IntRect snapped_parent_clip_rect(
-      PixelSnappedIntRect(parent_clip_rect.Rect()));
+  // The accumulated clip rect is in the space of clip_inheritance_ancestor.
+  // It needs to be converted to the space of our compositing container because
+  // our layer position is based on that.
+  LayoutRect clip_rect_in_compositing_container_space = clip_rect.Rect();
+  // The following two branches are doing exact the same conversion, but
+  // ConvertToLayerCoords can only handle descendant-to-ancestor conversion.
+  // Inversion needs to be done manually if clip_inheritance_container is not
+  // a descendant of compositing_container.
+  if (clip_inheritance_ancestor == compositing_container) {
+    // No needs to convert.
+  } else if (clip_inheritance_ancestor == ScrollParent()) {
+    // Having a scroll parent implies that the inherited clip is a sibling to
+    // us in paint order, thus our compositing container must be an ancestor
+    // of the scroll parent.
+    // See CompositingInputsUpdater::UpdateRecursive().
+    DCHECK(clip_inheritance_ancestor->GetLayoutObject().IsDescendantOf(
+        &compositing_container->GetLayoutObject()));
+    clip_inheritance_ancestor->ConvertToLayerCoords(
+        compositing_container, clip_rect_in_compositing_container_space);
+  } else {
+    // Inherits from clip parent. The clip parent is set only when we need to
+    // escape some clip that was applied to our compositing container. As such,
+    // the clip parent must be some ancestor of our compositing container.
+    DCHECK(compositing_container->GetLayoutObject().IsDescendantOf(
+        &clip_inheritance_ancestor->GetLayoutObject()));
+    LayoutPoint compositing_container_origin_in_clip_ancestor_space;
+    compositing_container->ConvertToLayerCoords(
+        clip_inheritance_ancestor,
+        compositing_container_origin_in_clip_ancestor_space);
+    clip_rect_in_compositing_container_space.MoveBy(
+        -compositing_container_origin_in_clip_ancestor_space);
+  }
+  clip_rect_in_compositing_container_space.Move(
+      compositing_container->SubpixelAccumulation());
 
-  DCHECK(snapped_parent_clip_rect != LayoutRect::InfiniteIntRect());
+  IntRect snapped_clip_rect =
+      PixelSnappedIntRect(clip_rect_in_compositing_container_space);
+
   ancestor_clipping_layer_->SetPosition(FloatPoint(
-      snapped_parent_clip_rect.Location() - graphics_layer_parent_location));
-  ancestor_clipping_layer_->SetSize(FloatSize(snapped_parent_clip_rect.Size()));
+      snapped_clip_rect.Location() - graphics_layer_parent_location));
+  ancestor_clipping_layer_->SetSize(FloatSize(snapped_clip_rect.Size()));
 
   // backgroundRect is relative to compositingContainer, so subtract
   // snappedOffsetFromCompositedAncestor.X/snappedOffsetFromCompositedAncestor.Y
   // to get back to local coords.
   ancestor_clipping_layer_->SetOffsetFromLayoutObject(
-      snapped_parent_clip_rect.Location() -
-      snapped_offset_from_composited_ancestor);
+      snapped_clip_rect.Location() - snapped_offset_from_composited_ancestor);
 
   if (ancestor_clipping_mask_layer_) {
     ancestor_clipping_mask_layer_->SetOffsetFromLayoutObject(
@@ -1303,7 +1347,7 @@
 
   // The primary layer is then parented in, and positioned relative to this
   // clipping layer.
-  graphics_layer_parent_location = snapped_parent_clip_rect.Location();
+  graphics_layer_parent_location = snapped_clip_rect.Location();
 }
 
 void CompositedLayerMapping::UpdateOverflowControlsHostLayerGeometry(
@@ -2461,11 +2505,7 @@
 }
 
 void CompositedLayerMapping::UpdateClipParent(const PaintLayer* scroll_parent) {
-  const PaintLayer* clip_parent = owning_layer_.ClipParent();
-  if (clip_parent) {
-    clip_parent =
-        clip_parent->EnclosingLayerWithCompositedLayerMapping(kIncludeSelf);
-  }
+  const PaintLayer* clip_parent = CompositedClipParent();
 
   if (ScrollingCoordinator* scrolling_coordinator =
           owning_layer_.GetScrollingCoordinator()) {
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.h b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.h
index 4243f90..d92ab80f 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.h
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.h
@@ -494,35 +494,37 @@
       const GraphicsLayerPaintInfo&,
       const Vector<GraphicsLayerPaintInfo>& layers);
 
-  // Conservatively check that a sequence of border-radius clips do not clip
-  // this layer. The rectangle to check for clipping is the child's layer
-  // bound in the nearest clipping ancestor's space. The
-  // nearest_clipping_ancestor is the place where we need to start the search
-  // for border radius clips. The compositing_ancestor is the nearest
-  // compositing ancestor layer and we can stop checking clips at that layer
-  // because higher layer clips will be applied elsewhere.
-  // This is a fast approximate test. Depending on the shape of the child and
-  // the size of the clips, this method may return true when in fact
-  // the child is not clipped. We accept the approximation because most border
-  // radii are small and the outcome is used to reduce the number of layers,
-  // not influence correctness.
+  // Conservatively check whether there exists any border-radius clip that
+  // must be applied by an ancestor clipping mask layer. There are two inputs
+  // to this function: the bounds of contents that are going to be clipped
+  // by ancestor clipping layer, and the compositing ancestor which we are
+  // going to inherit clip state from.
+  // The function works by collecting all border-radius clips between the
+  // current layer and the inherited clip, i.e. those are the clips that are
+  // going to be applied by the ancestor clipping mask layer. A fast
+  // approximation test is used to determine whether the contents exceed
+  // the bounds of any of the clips. The function may return false positive
+  // (apply mask layer when not strictly needed), but never false negative,
+  // as its purpose is only for optimization.
   bool AncestorRoundedCornersWillClip(
-      const FloatRect& child_rect_in_nearest_clipping_space,
-      const PaintLayer* nearest_clipping_ancestor,
-      const PaintLayer* compositing_ancestor);
+      const FloatRect& bounds_in_ancestor_space,
+      const PaintLayer* clip_inheritance_ancestor);
 
-  // Return true in |owningLayerIsClipped| iff |m_owningLayer|'s compositing
-  // ancestor is not a descendant (inclusive) of the clipping container for
-  // |m_owningLayer|. Return true in |owningLayerIsMasked| iff
-  // |owningLayerIsClipped| is true and |m_owningLayer|'s compositing ancestor
-  // is not a descendant (inclusive) of a container that applies a mask for
-  // |m_owningLayer|.
+  // Return true in |owningLayerIsClipped| iff there is any clip in between
+  // the current layer and the inherited clip state. The inherited clip state
+  // is determined by the interoperation between compositing container, clip
+  // parent, and scroll parent.
+  // Return true in |owningLayerIsMasked| iff |owningLayerIsClipped| is true
+  // and any of the clip needs to be applied as a painted mask.
   void OwningLayerClippedOrMaskedByLayerNotAboveCompositedAncestor(
       const PaintLayer* scroll_parent,
       bool& owning_layer_is_clipped,
       bool& owning_layer_is_masked);
 
-  const PaintLayer* ScrollParent();
+  const PaintLayer* ScrollParent() const;
+  const PaintLayer* CompositedClipParent() const;
+  const PaintLayer* ClipInheritanceAncestor(
+      const PaintLayer* compositing_container) const;
 
   // Clear the groupedMapping entry on the layer at the given index, only if
   // that layer does not appear earlier in the set of layers for this object.
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp
index b9e592fd..dd517c3d 100644
--- a/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp
+++ b/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp
@@ -71,29 +71,17 @@
   return nullptr;
 }
 
-static bool HasClippedStackingAncestor(const PaintLayer* layer,
-                                       const PaintLayer* clipping_layer) {
-  if (layer == clipping_layer)
+static bool NeedsToEscapeClipInheritedFromCompositingContainer(
+    const PaintLayer* layer,
+    const LayoutObject& desired_clip) {
+  const PaintLayer* compositing_parent = layer->CompositingContainer();
+  DCHECK(compositing_parent);
+  const LayoutObject* inherited_clip = &compositing_parent->GetLayoutObject();
+  if (!inherited_clip->HasClipRelatedProperty())
+    inherited_clip = compositing_parent->ClippingContainer();
+  if (!inherited_clip)
     return false;
-  bool found_intervening_clip = false;
-  const LayoutObject& clipping_layout_object =
-      clipping_layer->GetLayoutObject();
-  for (const PaintLayer* current = layer->CompositingContainer(); current;
-       current = current->CompositingContainer()) {
-    if (current == clipping_layer)
-      return found_intervening_clip;
-
-    if (current->GetLayoutObject().HasClipRelatedProperty() &&
-        !clipping_layout_object.IsDescendantOf(&current->GetLayoutObject()))
-      found_intervening_clip = true;
-
-    if (const LayoutObject* container = current->ClippingContainer()) {
-      if (&clipping_layout_object != container &&
-          !clipping_layout_object.IsDescendantOf(container))
-        found_intervening_clip = true;
-    }
-  }
-  return false;
+  return !desired_clip.IsDescendantOf(inherited_clip);
 }
 
 void CompositingInputsUpdater::UpdateRecursive(PaintLayer* layer,
@@ -200,6 +188,8 @@
           layer_is_fixed_position ? layer : parent->NearestFixedPositionLayer();
 
       if (info.has_ancestor_with_clip_related_property) {
+        // This is the ancestor that is |layer|'s containing block, or has a
+        // CSS clip, which ever is the closest.
         const PaintLayer* parent_layer_on_clipping_container_chain =
             FindParentLayerOnClippingContainerChain(layer);
         const bool parent_has_clip_related_property =
@@ -210,22 +200,26 @@
                 ? &parent_layer_on_clipping_container_chain->GetLayoutObject()
                 : parent_layer_on_clipping_container_chain->ClippingContainer();
 
-        const PaintLayer* clipping_layer =
-            properties.clipping_container
-                ? properties.clipping_container->EnclosingLayer()
-                : layer->Compositor()->RootLayer();
-
         if (!layer->SubtreeIsInvisible()) {
-          if (layer->GetLayoutObject().IsOutOfFlowPositioned()) {
-            if (HasClippedStackingAncestor(layer, clipping_layer))
-              properties.clip_parent = clipping_layer;
-          } else {
-            if (clipping_layer && clipping_layer->CompositingContainer() ==
-                                      layer->CompositingContainer()) {
-              // If the clipping container of |layer| is a sibling in the
-              // stacking tree, and it escapes a stacking ancestor clip,
-              // this layer should escape that clip also.
-              properties.clip_parent = clipping_layer->ClipParent();
+          if (layer->GetLayoutObject().IsOutOfFlowPositioned() &&
+              NeedsToEscapeClipInheritedFromCompositingContainer(
+                  layer, parent_layer_on_clipping_container_chain
+                             ->GetLayoutObject())) {
+            properties.clip_parent = parent_layer_on_clipping_container_chain;
+          } else if (parent_layer_on_clipping_container_chain
+                         ->CompositingContainer() ==
+                     layer->CompositingContainer()) {
+            // If the clipping container of |layer| is a sibling in the
+            // stacking tree, and it escapes a stacking ancestor clip,
+            // this layer should escape that clip also.
+            if (parent_layer_on_clipping_container_chain->ClipParent()) {
+              // It may be attempting to inherit the clip state of
+              // parent_layer_on_clipping_container_chain directly, but our
+              // paint order can be before the clipping parent due to negative
+              // z-index. Our compositor implementation currently only allow
+              // inheriting clip from layer that paints before us.
+              properties.clip_parent =
+                  parent_layer_on_clipping_container_chain->ClipParent();
             }
           }
         }
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index b0f9b72..2a96530 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1809,6 +1809,7 @@
     "graphics/compositing/ContentLayerClientImplTest.cpp",
     "graphics/compositing/PaintArtifactCompositorTest.cpp",
     "graphics/compositing/PaintChunksToCcLayerTest.cpp",
+    "graphics/filters/FECompositeTest.cpp",
     "graphics/filters/ImageFilterBuilderTest.cpp",
     "graphics/gpu/DrawingBufferTest.cpp",
     "graphics/gpu/SharedGpuContextTest.cpp",
diff --git a/third_party/WebKit/Source/platform/CalculationValue.h b/third_party/WebKit/Source/platform/CalculationValue.h
index c641246..57c7d77 100644
--- a/third_party/WebKit/Source/platform/CalculationValue.h
+++ b/third_party/WebKit/Source/platform/CalculationValue.h
@@ -33,7 +33,9 @@
 
 #include "platform/Length.h"
 #include "platform/LengthFunctions.h"
+#include "platform/wtf/PassRefPtr.h"
 #include "platform/wtf/RefCounted.h"
+#include "platform/wtf/RefPtr.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/LayoutUnit.cpp b/third_party/WebKit/Source/platform/LayoutUnit.cpp
index 3c29ca72..70e752d 100644
--- a/third_party/WebKit/Source/platform/LayoutUnit.cpp
+++ b/third_party/WebKit/Source/platform/LayoutUnit.cpp
@@ -4,6 +4,7 @@
 
 #include "platform/LayoutUnit.h"
 
+#include <ostream>
 #include "platform/wtf/text/WTFString.h"
 
 namespace blink {
@@ -20,4 +21,8 @@
   return String::Number(ToDouble());
 }
 
+std::ostream& operator<<(std::ostream& stream, const LayoutUnit& value) {
+  return stream << value.ToString();
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/LayoutUnit.h b/third_party/WebKit/Source/platform/LayoutUnit.h
index 344e5f6..50d0677 100644
--- a/third_party/WebKit/Source/platform/LayoutUnit.h
+++ b/third_party/WebKit/Source/platform/LayoutUnit.h
@@ -31,17 +31,14 @@
 #ifndef LayoutUnit_h
 #define LayoutUnit_h
 
-#include <limits.h>
-#include <math.h>
-#include <stdlib.h>
-#include <algorithm>
+#include <iosfwd>
 #include <limits>
 #include "base/numerics/safe_conversions.h"
 #include "platform/PlatformExport.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Assertions.h"
+#include "platform/wtf/Forward.h"
 #include "platform/wtf/SaturatedArithmetic.h"
-#include "platform/wtf/text/WTFString.h"
 
 namespace blink {
 
@@ -166,11 +163,11 @@
   }
 
   LayoutUnit ClampNegativeToZero() const {
-    return std::max(*this, LayoutUnit());
+    return value_ < 0 ? LayoutUnit() : *this;
   }
 
   LayoutUnit ClampPositiveToZero() const {
-    return std::min(*this, LayoutUnit());
+    return value_ > 0 ? LayoutUnit() : *this;
   }
 
   LayoutUnit Fraction() const {
@@ -707,9 +704,7 @@
   return value.ToInt() == value;
 }
 
-inline std::ostream& operator<<(std::ostream& stream, const LayoutUnit& value) {
-  return stream << value.ToString();
-}
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const LayoutUnit&);
 
 }  // namespace blink
 
diff --git a/third_party/WebKit/Source/platform/Length.cpp b/third_party/WebKit/Source/platform/Length.cpp
index be1ca46..35b8c24c 100644
--- a/third_party/WebKit/Source/platform/Length.cpp
+++ b/third_party/WebKit/Source/platform/Length.cpp
@@ -27,6 +27,7 @@
 
 #include "platform/CalculationValue.h"
 #include "platform/animation/AnimationUtilities.h"
+#include "platform/wtf/HashMap.h"
 
 namespace blink {
 
diff --git a/third_party/WebKit/Source/platform/graphics/filters/FEComposite.cpp b/third_party/WebKit/Source/platform/graphics/filters/FEComposite.cpp
index b034c46..e85fd49 100644
--- a/third_party/WebKit/Source/platform/graphics/filters/FEComposite.cpp
+++ b/third_party/WebKit/Source/platform/graphics/filters/FEComposite.cpp
@@ -113,42 +113,51 @@
 }
 
 FloatRect FEComposite::MapInputs(const FloatRect& rect) const {
-  FloatRect input1_rect = InputEffect(1)->MapRect(rect);
+  FloatRect i1 = InputEffect(0)->MapRect(rect);
+  FloatRect i2 = InputEffect(1)->MapRect(rect);
   switch (type_) {
     case FECOMPOSITE_OPERATOR_IN:
       // 'in' has output only in the intersection of both inputs.
-      return Intersection(input1_rect, InputEffect(0)->MapRect(input1_rect));
+      return Intersection(i1, i2);
     case FECOMPOSITE_OPERATOR_ATOP:
       // 'atop' has output only in the extents of the second input.
-      return input1_rect;
+      return i2;
     case FECOMPOSITE_OPERATOR_ARITHMETIC:
       // result(i1,i2) = k1*i1*i2 + k2*i1 + k3*i2 + k4
       //
       // (The below is not a complete breakdown of cases.)
       //
-      // Arithmetic with non-zero k4 may influence the complete filter primitive
-      // region. [k4 > 0 => result(0,0) = k4 => result(a,b) >= k4]
+      // Arithmetic with positive k4 may influence the complete filter primitive
+      // region. [k4 > 0 => result(0,0) = k4 => result(i1,i2) >= k4]
+      // Fall through to use union. If this effect clips to bounds,
+      // ApplyBounds() will return AbsoluteBounds() regardless of the return
+      // value of this function because AffectsTransparentPixels() is true.
       if (K4() > 0)
-        return rect;
-      // Additionally, if k2 = 0, input 0 can only appear where input 1 also
-      // appears. [k2 = k4 = 0 => result(a,b) = k1*a*b + k3*b = (k1*a + k3)*b]
-      // Hence for k2 > 0, both inputs can still appear. (Except if k3 = 0.)
-      if (K2() <= 0) {
-        // If k3 > 0, output can be produced wherever input 1 is
-        // non-transparent.
-        if (K3() > 0)
-          return input1_rect;
-        // If just k1 is positive, output will only be produce where both
-        // inputs are non-transparent. Use intersection.
-        // [k1 >= 0 and k2 = k3 = k4 = 0 => result(a,b) = k1 * a * b]
-        return Intersection(input1_rect, InputEffect(0)->MapRect(input1_rect));
-      }
-    // else fall through to use union
+        break;
+      // If both K2 or K3 are positive, both i1 and i2 appear. Fall through to
+      // use union.
+      if (K2() > 0 && K3() > 0)
+        break;
+      // If k2 > 0, output can be produced whenever i1 is non-transparent.
+      // [k3 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k2*i1 = (k1*i2 + k2)*i1]
+      if (K2() > 0)
+        return i1;
+      // If k3 > 0, output can be produced whenever i2 is non-transparent.
+      // [k2 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k3*i2 = (k1*i1 + k3)*i2]
+      if (K3() > 0)
+        return i2;
+      // If just k1 is positive, output will only be produce where both inputs
+      // are non-transparent. Use intersection.
+      // [k1 > 0 and k2 = k3 = k4 = 0 => result(i1,i2) = k1*i1*i2]
+      if (K1() > 0)
+        return Intersection(i1, i2);
+      // [k1 = k2 = k3 = k4 = 0 => result(i1,i2) = 0]
+      return FloatRect();
     default:
       break;
   }
   // Take the union of both input effects.
-  return UnionRect(input1_rect, InputEffect(0)->MapRect(rect));
+  return UnionRect(i1, i2);
 }
 
 SkBlendMode ToBlendMode(CompositeOperationType mode) {
diff --git a/third_party/WebKit/Source/platform/graphics/filters/FECompositeTest.cpp b/third_party/WebKit/Source/platform/graphics/filters/FECompositeTest.cpp
new file mode 100644
index 0000000..dcea59d
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/filters/FECompositeTest.cpp
@@ -0,0 +1,133 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/graphics/filters/FEComposite.h"
+
+#include "platform/graphics/filters/FEOffset.h"
+#include "platform/graphics/filters/Filter.h"
+#include "platform/graphics/filters/SourceGraphic.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class FECompositeTest : public ::testing::Test {
+ protected:
+  FEComposite* CreateComposite(CompositeOperationType type,
+                               float k1 = 0,
+                               float k2 = 0,
+                               float k3 = 0,
+                               float k4 = 0) {
+    // Use big filter region to avoid it from affecting FEComposite's MapRect
+    // results.
+    FloatRect filter_region(-10000, -10000, 20000, 20000);
+    auto* filter =
+        Filter::Create(FloatRect(), filter_region, 1, Filter::kUserSpace);
+
+    // Input 1 of composite has a fixed output rect.
+    auto* source_graphic1 = SourceGraphic::Create(filter);
+    source_graphic1->SetClipsToBounds(false);
+    source_graphic1->SetSourceRect(kInput1Rect);
+
+    // Input 2 of composite will pass composite->MapRect()'s parameter as its
+    // output.
+    auto* source_graphic2 = SourceGraphic::Create(filter);
+    source_graphic2->SetClipsToBounds(false);
+
+    // Composite input 1 and input 2.
+    auto* composite = FEComposite::Create(filter, type, k1, k2, k3, k4);
+    composite->SetClipsToBounds(false);
+    composite->InputEffects().push_back(source_graphic1);
+    composite->InputEffects().push_back(source_graphic2);
+    return composite;
+  }
+
+  const IntRect kInput1Rect = {50, -50, 100, 100};
+};
+
+#define EXPECT_INTERSECTION(c)                                  \
+  do {                                                          \
+    EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty());             \
+    EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 50, 50)).IsEmpty()); \
+    EXPECT_EQ(FloatRect(50, 0, 100, 50),                        \
+              c->MapRect(FloatRect(0, 0, 200, 200)));           \
+  } while (false)
+
+#define EXPECT_INPUT1(c)                                                      \
+  do {                                                                        \
+    EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect()));               \
+    EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect(0, 0, 50, 50)));   \
+    EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect(0, 0, 200, 200))); \
+  } while (false)
+
+#define EXPECT_INPUT2(c)                                                     \
+  do {                                                                       \
+    EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty());                          \
+    EXPECT_EQ(FloatRect(0, 0, 50, 50), c->MapRect(FloatRect(0, 0, 50, 50))); \
+    EXPECT_EQ(FloatRect(0, 0, 200, 200),                                     \
+              c->MapRect(FloatRect(0, 0, 200, 200)));                        \
+  } while (false)
+
+#define EXPECT_UNION(c)                                         \
+  do {                                                          \
+    EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect())); \
+    EXPECT_EQ(FloatRect(0, -50, 150, 100),                      \
+              c->MapRect(FloatRect(0, 0, 50, 50)));             \
+    EXPECT_EQ(FloatRect(0, -50, 200, 250),                      \
+              c->MapRect(FloatRect(0, 0, 200, 200)));           \
+  } while (false)
+
+#define EXPECT_EMPTY(c)                                           \
+  do {                                                            \
+    EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty());               \
+    EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 50, 50)).IsEmpty());   \
+    EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 200, 200)).IsEmpty()); \
+  } while (false)
+
+TEST_F(FECompositeTest, MapRectIn) {
+  EXPECT_INTERSECTION(CreateComposite(FECOMPOSITE_OPERATOR_IN));
+}
+
+TEST_F(FECompositeTest, MapRectATop) {
+  EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ATOP));
+}
+
+TEST_F(FECompositeTest, MapRectOtherOperators) {
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_OVER));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_OUT));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_XOR));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_LIGHTER));
+}
+
+TEST_F(FECompositeTest, MapRectArithmetic) {
+  EXPECT_EMPTY(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 0, 0));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 0, 1));
+  EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 1, 0));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 1, 1));
+  EXPECT_INPUT1(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 0, 0));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 0, 1));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 1, 0));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 1, 1));
+  EXPECT_INTERSECTION(
+      CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 0, 0));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 0, 1));
+  EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 1, 0));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 1, 1));
+  EXPECT_INPUT1(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 0, 0));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 0, 1));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 0));
+  EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 1));
+}
+
+TEST_F(FECompositeTest, MapRectArithmeticK4Clipped) {
+  // Arithmetic operator with positive K4 will always affect the whole primitive
+  // subregion.
+  auto* c = CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 1);
+  c->SetClipsToBounds(true);
+  FloatRect bounds(222, 333, 444, 555);
+  c->SetFilterPrimitiveSubregion(bounds);
+  EXPECT_EQ(bounds, c->MapRect(FloatRect()));
+  EXPECT_EQ(bounds, c->MapRect(FloatRect(100, 200, 300, 400)));
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/loader/BUILD.gn b/third_party/WebKit/Source/platform/loader/BUILD.gn
index b7c1c22..3d9deaa 100644
--- a/third_party/WebKit/Source/platform/loader/BUILD.gn
+++ b/third_party/WebKit/Source/platform/loader/BUILD.gn
@@ -97,6 +97,8 @@
     "//components/link_header_util:link_header_util",
     "//mojo/public/cpp/system:system",
     "//storage/public/interfaces:interfaces_blink__generator",
+    "//third_party/WebKit/Source/platform:make_platform_generated",
+    "//third_party/WebKit/public:mojo_bindings_blink",
   ]
 
   public_deps = [
diff --git a/third_party/WebKit/Source/platform/testing/GeometryPrinters.cpp b/third_party/WebKit/Source/platform/testing/GeometryPrinters.cpp
index 64c44d88..92f3efc 100644
--- a/third_party/WebKit/Source/platform/testing/GeometryPrinters.cpp
+++ b/third_party/WebKit/Source/platform/testing/GeometryPrinters.cpp
@@ -19,6 +19,7 @@
 #include "platform/geometry/LayoutPoint.h"
 #include "platform/geometry/LayoutRect.h"
 #include "platform/geometry/LayoutSize.h"
+#include "platform/wtf/text/WTFString.h"
 #include <ostream>  // NOLINT
 
 namespace blink {
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl.py
index 729d1d6..bebdc8c 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl.py
@@ -82,7 +82,7 @@
             self._host.sleep(poll_delay_seconds)
             try_results = self.try_job_results()
             _log.debug('Fetched try results: %s', try_results)
-            if self.all_finished(try_results):
+            if try_results and self.all_finished(try_results):
                 self._host.print_('All jobs finished.')
                 return try_results
             self._host.print_('Waiting. %d seconds passed.' % (self._host.time() - start))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl_unittest.py
index f5b81c9..2343bf794 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl_unittest.py
@@ -72,6 +72,22 @@
             'Waiting. 6600 seconds passed.\n'
             'Timed out waiting for try results.\n')
 
+    def test_wait_for_try_jobs_no_results_not_considered_finished(self):
+        host = MockHost()
+        git_cl = GitCL(host)
+        git_cl.fetch_raw_try_job_results = lambda: []
+        git_cl.wait_for_try_jobs()
+        self.assertEqual(
+            host.stdout.getvalue(),
+            'Waiting for try jobs (timeout: 7200 seconds).\n'
+            'Waiting. 600 seconds passed.\n'
+            'Waiting. 1800 seconds passed.\n'
+            'Waiting. 3000 seconds passed.\n'
+            'Waiting. 4200 seconds passed.\n'
+            'Waiting. 5400 seconds passed.\n'
+            'Waiting. 6600 seconds passed.\n'
+            'Timed out waiting for try results.\n')
+
     def test_wait_for_try_jobs_done(self):
         host = MockHost()
         git_cl = GitCL(host)
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 981faf04..6a4788f 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
@@ -480,7 +480,10 @@
                 help='If specified, upload results json files to this appengine server.'),
         ]))
 
-    option_parser = optparse.OptionParser()
+    option_parser = optparse.OptionParser(
+        prog='run-webkit-tests',
+        usage='%prog [options] [tests]',
+        description='Runs Blink layout tests as described in docs/testing/layout_tests.md')
 
     for group_name, group_options in option_group_definitions:
         option_group = optparse.OptionGroup(option_parser, group_name)
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 49bb879..a06e301 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -768,6 +768,7 @@
     "//content/common:mojo_bindings_blink",
     "//chrome/common:mojo_bindings_blink",
     "//third_party/WebKit/Source/platform",
+    "//third_party/WebKit/Source/platform/loader",
   ]
   sources = [
     "platform/display_mode.mojom",
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 38dda18..33b1c42 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -520,6 +520,7 @@
 
     'tryserver.chromium.angle': {
       'android_angle_rel_ng': 'gpu_tests_deqp_android_release_trybot_arm64',
+      'android_angle_deqp_rel_ng': 'gpu_tests_deqp_android_release_trybot_arm64',
       'linux_angle_chromeos_rel_ng': 'gpu_fyi_tests_chromeos_cros_release_trybot',
       'linux_angle_dbg_ng': 'gpu_fyi_tests_debug_trybot',
       'linux_angle_deqp_rel_ng': 'gpu_fyi_tests_release_trybot',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8ee4c35..0bd5fca 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -24543,13 +24543,23 @@
   </summary>
 </histogram>
 
+<histogram name="GoogleUpdate.InfoBar.DeviceFreeSpace" units="MB">
+  <owner>shaktisahu@chromium.org</owner>
+  <summary>
+    (Android-only) The amount of internal memory storage that is free on the
+    file system and available to the applications when the InfoBar or update
+    menu item is shown.
+  </summary>
+</histogram>
+
 <histogram name="GoogleUpdate.InfoBar.InternalStorageSizeAvailable" units="MB">
   <owner>yfriedman@chromium.org</owner>
   <owner>dfalcantara@chromium.org</owner>
   <owner>khushalsagar@chromium.org</owner>
   <summary>
     (Android-only) The amount of internal memory storage available on the user's
-    device when the InfoBar or update menu item is shown.
+    device when the InfoBar or update menu item is shown. Deprecating soon in
+    M64.
   </summary>
 </histogram>
 
diff --git a/ui/app_list/views/suggestions_container_view.cc b/ui/app_list/views/suggestions_container_view.cc
index ee04a7a..c86ac21 100644
--- a/ui/app_list/views/suggestions_container_view.cc
+++ b/ui/app_list/views/suggestions_container_view.cc
@@ -133,7 +133,8 @@
 
 void SuggestionsContainerView::CreateAppsGrid(int apps_num) {
   DCHECK(search_result_tile_views_.empty());
-  views::GridLayout* tiles_layout_manager = new views::GridLayout(this);
+  views::GridLayout* tiles_layout_manager =
+      views::GridLayout::CreateAndInstall(this);
   SetLayoutManager(tiles_layout_manager);
 
   views::ColumnSet* column_set = tiles_layout_manager->AddColumnSet(0);
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 3edbc80d..99d5a7f9 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -154,21 +154,6 @@
   return ui::GetScaleFactorForNativeView(window);
 }
 
-void ConvertEventLocationToDip(int64_t display_id, ui::LocatedEvent* event) {
-  display::Screen* screen = display::Screen::GetScreen();
-  display::Display display;
-  if (!screen->GetDisplayWithDisplayId(display_id, &display) ||
-      display.device_scale_factor() == 1.f) {
-    return;
-  }
-  const gfx::Point host_location =
-      gfx::ConvertPointToDIP(display.device_scale_factor(), event->location());
-  event->set_location(host_location);
-  const gfx::Point root_location = gfx::ConvertPointToDIP(
-      display.device_scale_factor(), event->root_location());
-  event->set_root_location(root_location);
-}
-
 // Create and return a MouseEvent or TouchEvent from |event| if |event| is a
 // PointerEvent, otherwise return the copy of |event|.
 std::unique_ptr<ui::Event> MapEvent(const ui::Event& event) {
@@ -390,6 +375,63 @@
   return windows_.count(window_mus->server_id()) > 0;
 }
 
+void WindowTreeClient::ConvertPointerEventLocationToDip(
+    int64_t display_id,
+    WindowMus* window,
+    ui::LocatedEvent* event) const {
+  // PointerEvents shouldn't have the target set.
+  DCHECK(!event->target());
+  if (window_manager_delegate_) {
+    ConvertPointerEventLocationToDipInWindowManager(display_id, window, event);
+    return;
+  }
+  display::Screen* screen = display::Screen::GetScreen();
+  display::Display display;
+  // TODO(sky): this needs to take into account the ui display scale.
+  // http://crbug.com/758399.
+  if (!screen->GetDisplayWithDisplayId(display_id, &display) ||
+      display.device_scale_factor() == 1.f) {
+    return;
+  }
+  const gfx::Point root_location = gfx::ConvertPointToDIP(
+      display.device_scale_factor(), event->root_location());
+  event->set_root_location(root_location);
+  if (window) {
+    const gfx::Point host_location = gfx::ConvertPointToDIP(
+        display.device_scale_factor(), event->location());
+    event->set_location(host_location);
+  } else {
+    // When there is no window force the root and location to be the same. They
+    // may differ it |window| was valid at the time of the event, but was since
+    // deleted.
+    event->set_location(root_location);
+  }
+}
+
+void WindowTreeClient::ConvertPointerEventLocationToDipInWindowManager(
+    int64_t display_id,
+    WindowMus* window,
+    ui::LocatedEvent* event) const {
+  const WindowTreeHostMus* window_tree_host =
+      GetWindowTreeHostForDisplayId(display_id);
+  if (!window_tree_host)
+    return;
+
+  ui::Event::DispatcherApi dispatcher_api(event);
+  if (window) {
+    dispatcher_api.set_target(window->GetWindow());
+  } else {
+    // UpdateForRootTransform() in the case of no target uses |location_|.
+    // |location_| may be relative to a window that wasn't found. To ensure we
+    // convert from the root, reset |location_| to |root_location_|.
+    event->set_location_f(event->root_location_f());
+  }
+  event->UpdateForRootTransform(
+      window_tree_host->GetInverseRootTransform(),
+      window_tree_host->GetInverseRootTransformForLocalEventCoordinates());
+  dispatcher_api.set_target(nullptr);
+}
+
 InFlightChange* WindowTreeClient::GetOldestInFlightChangeMatching(
     const InFlightChange& change) {
   for (const auto& pair : in_flight_map_) {
@@ -481,6 +523,20 @@
     window->SetPropertyFromServer(pair.first, &pair.second);
 }
 
+const WindowTreeHostMus* WindowTreeClient::GetWindowTreeHostForDisplayId(
+    int64_t display_id) const {
+  if (!window_manager_delegate_)
+    return nullptr;
+
+  for (WindowMus* window : roots_) {
+    WindowTreeHostMus* window_tree_host =
+        static_cast<WindowTreeHostMus*>(window->GetWindow()->GetHost());
+    if (window_tree_host->display_id() == display_id)
+      return window_tree_host;
+  }
+  return nullptr;
+}
+
 std::unique_ptr<WindowTreeHostMus> WindowTreeClient::CreateWindowTreeHost(
     WindowMusType window_mus_type,
     const ui::mojom::WindowData& window_data,
@@ -1366,7 +1422,8 @@
   if (matches_pointer_watcher && has_pointer_watcher_) {
     DCHECK(event->IsPointerEvent());
     std::unique_ptr<ui::Event> event_in_dip(ui::Event::Clone(*event));
-    ConvertEventLocationToDip(display_id, event_in_dip->AsLocatedEvent());
+    ConvertPointerEventLocationToDip(display_id, window,
+                                     event_in_dip->AsLocatedEvent());
     delegate_->OnPointerEventObserved(*event_in_dip->AsPointerEvent(),
                                       window ? window->GetWindow() : nullptr);
   }
@@ -1435,8 +1492,9 @@
   if (!has_pointer_watcher_)
     return;
 
-  ConvertEventLocationToDip(display_id, event->AsLocatedEvent());
   WindowMus* target_window = GetWindowByServerId(window_id);
+  ConvertPointerEventLocationToDip(display_id, target_window,
+                                   event->AsLocatedEvent());
   delegate_->OnPointerEventObserved(
       *event->AsPointerEvent(),
       target_window ? target_window->GetWindow() : nullptr);
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index c343903c..a81fa20 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -207,6 +207,22 @@
 
   bool IsWindowKnown(aura::Window* window);
 
+  // Updates the coordinates of |event| to be in DIPs. |window| is the source
+  // of the event, and may be null. A null |window| means either there is no
+  // local window the event is targeted at *or* |window| was valid at the time
+  // the event was generated at the server but was deleted locally before the
+  // event was received.
+  void ConvertPointerEventLocationToDip(int64_t display_id,
+                                        WindowMus* window,
+                                        ui::LocatedEvent* event) const;
+
+  // Variant of ConvertPointerEventLocationToDip() that is used when in
+  // the window-manager.
+  void ConvertPointerEventLocationToDipInWindowManager(
+      int64_t display_id,
+      WindowMus* window,
+      ui::LocatedEvent* event) const;
+
   // Returns the oldest InFlightChange that matches |change|.
   InFlightChange* GetOldestInFlightChangeMatching(const InFlightChange& change);
 
@@ -236,6 +252,9 @@
       WindowMus* window,
       const ui::mojom::WindowData& window_data);
 
+  const WindowTreeHostMus* GetWindowTreeHostForDisplayId(
+      int64_t display_id) const;
+
   // Creates a new WindowTreeHostMus.
   std::unique_ptr<WindowTreeHostMus> CreateWindowTreeHost(
       WindowMusType window_mus_type,
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc
index 4e12e26..85f2444 100644
--- a/ui/aura/mus/window_tree_client_unittest.cc
+++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -52,6 +52,7 @@
 #include "ui/events/test/test_event_handler.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/transform.h"
 
 namespace aura {
 
@@ -718,9 +719,9 @@
       : test_window_tree_(test_window_tree) {}
   ~InputEventBasicTestWindowDelegate() override {}
 
-  bool got_move() const { return got_move_; }
-  bool got_press() const { return got_press_; }
-  bool got_release() const { return got_release_; }
+  int move_count() const { return move_count_; }
+  int press_count() const { return press_count_; }
+  int release_count() const { return release_count_; }
   bool was_acked() const { return was_acked_; }
   const gfx::Point& last_event_location() const { return last_event_location_; }
   void set_event_id(uint32_t event_id) { event_id_ = event_id; }
@@ -735,11 +736,11 @@
   void OnMouseEvent(ui::MouseEvent* event) override {
     was_acked_ = test_window_tree_->WasEventAcked(event_id_);
     if (event->type() == ui::ET_MOUSE_MOVED)
-      got_move_ = true;
+      ++move_count_;
     else if (event->type() == ui::ET_MOUSE_PRESSED)
-      got_press_ = true;
+      ++press_count_;
     else if (event->type() == ui::ET_MOUSE_RELEASED)
-      got_release_ = true;
+      ++release_count_;
     last_event_location_ = event->location();
     last_mouse_event_had_native_event_ = event->HasNativeEvent();
     if (event->HasNativeEvent()) {
@@ -752,18 +753,18 @@
   void OnTouchEvent(ui::TouchEvent* event) override {
     was_acked_ = test_window_tree_->WasEventAcked(event_id_);
     if (event->type() == ui::ET_TOUCH_PRESSED)
-      got_press_ = true;
+      ++press_count_;
     else if (event->type() == ui::ET_TOUCH_RELEASED)
-      got_release_ = true;
+      ++release_count_;
     last_event_location_ = event->location();
     event->SetHandled();
   }
 
   void reset() {
     was_acked_ = false;
-    got_move_ = false;
-    got_press_ = false;
-    got_release_ = false;
+    move_count_ = 0;
+    press_count_ = 0;
+    release_count_ = 0;
     last_event_location_ = gfx::Point();
     event_id_ = 0;
   }
@@ -771,9 +772,9 @@
  private:
   TestWindowTree* test_window_tree_;
   bool was_acked_ = false;
-  bool got_move_ = false;
-  bool got_press_ = false;
-  bool got_release_ = false;
+  int move_count_ = 0;
+  int press_count_ = 0;
+  int release_count_ = false;
   gfx::Point last_event_location_;
   uint32_t event_id_ = 0;
   bool last_mouse_event_had_native_event_ = false;
@@ -788,7 +789,7 @@
       : target_window_(target_window) {}
   ~InputEventBasicTestEventHandler() override {}
 
-  bool got_move() const { return got_move_; }
+  int move_count() const { return move_count_; }
   const gfx::Point& last_event_location() const { return last_event_location_; }
   void set_event_id(uint32_t event_id) { event_id_ = event_id; }
 
@@ -796,21 +797,21 @@
   void OnMouseEvent(ui::MouseEvent* event) override {
     if (event->target() == target_window_) {
       if (event->type() == ui::ET_MOUSE_MOVED)
-        got_move_ = true;
+        ++move_count_;
       last_event_location_ = event->location();
       event->SetHandled();
     }
   }
 
   void reset() {
-    got_move_ = false;
+    move_count_ = 0;
     last_event_location_ = gfx::Point();
     event_id_ = 0;
   }
 
  private:
   Window* target_window_ = nullptr;
-  bool got_move_ = false;
+  int move_count_ = 0;
   gfx::Point last_event_location_;
   uint32_t event_id_ = 0;
 
@@ -835,7 +836,7 @@
   top_level->AddChild(&child);
   child.SetBounds(gfx::Rect(10, 10, 100, 100));
   child.Show();
-  EXPECT_FALSE(window_delegate.got_move());
+  EXPECT_EQ(0, window_delegate.move_count());
   EXPECT_FALSE(window_delegate.was_acked());
   const gfx::Point event_location_in_child(2, 3);
   const uint32_t event_id = 1;
@@ -849,7 +850,7 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate.got_move());
+  EXPECT_EQ(1, window_delegate.move_count());
   EXPECT_FALSE(window_delegate.was_acked());
   EXPECT_EQ(event_location_in_child, window_delegate.last_event_location());
 }
@@ -870,7 +871,7 @@
   top_level->AddChild(&child);
   child.SetBounds(gfx::Rect(10, 10, 100, 100));
   child.Show();
-  EXPECT_FALSE(window_delegate.got_move());
+  EXPECT_EQ(0, window_delegate.move_count());
   const gfx::Point event_location(2, 3);
   const uint32_t event_id = 1;
   window_delegate.set_event_id(event_id);
@@ -884,7 +885,7 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate.got_move());
+  EXPECT_EQ(1, window_delegate.move_count());
   EXPECT_EQ(event_location, window_delegate.last_event_location());
 }
 
@@ -912,8 +913,8 @@
   child2.SetBounds(gfx::Rect(20, 30, 100, 100));
   child2.Show();
 
-  EXPECT_FALSE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
+  EXPECT_EQ(0, window_delegate1.move_count());
+  EXPECT_EQ(0, window_delegate2.move_count());
 
   // child1 has a targeter set and event_location is (50, 60), child2
   // should get the event even though mus-ws wants to send to child1.
@@ -930,8 +931,8 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_FALSE(window_delegate1.got_move());
-  EXPECT_TRUE(window_delegate2.got_move());
+  EXPECT_EQ(0, window_delegate1.move_count());
+  EXPECT_EQ(1, window_delegate2.move_count());
   EXPECT_EQ(gfx::Point(30, 30), window_delegate2.last_event_location());
   window_delegate1.reset();
   window_delegate2.reset();
@@ -951,8 +952,8 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
+  EXPECT_EQ(1, window_delegate1.move_count());
+  EXPECT_EQ(0, window_delegate2.move_count());
   EXPECT_EQ(gfx::Point(50, 60), window_delegate1.last_event_location());
 }
 
@@ -980,8 +981,8 @@
   child2.SetBounds(gfx::Rect(20, 30, 100, 100));
   child2.Show();
 
-  EXPECT_FALSE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
+  EXPECT_EQ(0, window_delegate1.move_count());
+  EXPECT_EQ(0, window_delegate2.move_count());
 
   // child1 has a custom targeter set which would always return itself as the
   // target window therefore event should go to child1 unlike
@@ -999,8 +1000,8 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
+  EXPECT_EQ(1, window_delegate1.move_count());
+  EXPECT_EQ(0, window_delegate2.move_count());
   EXPECT_EQ(gfx::Point(50, 60), window_delegate1.last_event_location());
   window_delegate1.reset();
   window_delegate2.reset();
@@ -1016,8 +1017,8 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
+  EXPECT_EQ(1, window_delegate1.move_count());
+  EXPECT_EQ(0, window_delegate2.move_count());
   EXPECT_EQ(gfx::Point(70, 90), window_delegate1.last_event_location());
 }
 
@@ -1050,8 +1051,8 @@
   child2->SetBounds(gfx::Rect(20, 30, 100, 100));
   child2->Show();
 
-  EXPECT_FALSE(window_delegate1->got_move());
-  EXPECT_FALSE(window_delegate2->got_move());
+  EXPECT_EQ(0, window_delegate1->move_count());
+  EXPECT_EQ(0, window_delegate2->move_count());
 
   // child1 has a custom targeter set which would always return itself as the
   // target window therefore event should go to child1.
@@ -1068,8 +1069,8 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1->got_move());
-  EXPECT_FALSE(window_delegate2->got_move());
+  EXPECT_EQ(1, window_delegate1->move_count());
+  EXPECT_EQ(0, window_delegate2->move_count());
   EXPECT_EQ(gfx::Point(50, 60), window_delegate1->last_event_location());
   window_delegate1->reset();
   window_delegate2->reset();
@@ -1089,8 +1090,8 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_FALSE(window_delegate1->got_move());
-  EXPECT_TRUE(window_delegate2->got_move());
+  EXPECT_EQ(0, window_delegate1->move_count());
+  EXPECT_EQ(1, window_delegate2->move_count());
   EXPECT_EQ(gfx::Point(30, 30), window_delegate2->last_event_location());
   child2.reset();
   child1.reset();
@@ -1117,8 +1118,8 @@
   child.SetBounds(gfx::Rect(10, 10, 100, 100));
   child.Show();
 
-  EXPECT_FALSE(root_handler.got_move());
-  EXPECT_FALSE(child_delegate.got_move());
+  EXPECT_EQ(0, root_handler.move_count());
+  EXPECT_EQ(0, child_delegate.move_count());
 
   const gfx::Point event_location_in_child(20, 30);
   const uint32_t event_id = 1;
@@ -1134,9 +1135,9 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(root_handler.got_move());
+  EXPECT_EQ(1, root_handler.move_count());
   EXPECT_EQ(gfx::Point(20, 30), root_handler.last_event_location());
-  EXPECT_FALSE(child_delegate.got_move());
+  EXPECT_EQ(0, child_delegate.move_count());
   EXPECT_EQ(gfx::Point(), child_delegate.last_event_location());
 }
 
@@ -1158,7 +1159,7 @@
   child.SetBounds(gfx::Rect(10, 10, 100, 100));
   child.Show();
 
-  EXPECT_FALSE(window_delegate.got_press());
+  EXPECT_EQ(0, window_delegate.press_count());
   EXPECT_FALSE(env->IsMouseButtonDown());
   EXPECT_FALSE(env->mouse_button_flags());
   EXPECT_EQ(gfx::Point(), env->last_mouse_location());
@@ -1177,7 +1178,7 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate.got_press());
+  EXPECT_EQ(1, window_delegate.press_count());
   EXPECT_TRUE(env->IsMouseButtonDown());
   EXPECT_EQ(1024, env->mouse_button_flags());  // ui::EF_LEFT_MOUSE_BUTTON
   EXPECT_EQ(event_location, env->last_mouse_location());
@@ -1200,7 +1201,7 @@
   // aura::Env, location shouldn't be updated.
   EXPECT_EQ(ui::mojom::EventResult::UNHANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_FALSE(window_delegate.got_release());
+  EXPECT_EQ(0, window_delegate.release_count());
   EXPECT_FALSE(env->IsMouseButtonDown());
   EXPECT_FALSE(env->mouse_button_flags());
   EXPECT_EQ(event_location, env->last_mouse_location());
@@ -1224,7 +1225,7 @@
   child.SetBounds(gfx::Rect(10, 10, 100, 100));
   child.Show();
 
-  EXPECT_FALSE(window_delegate.got_press());
+  EXPECT_EQ(0, window_delegate.press_count());
   EXPECT_FALSE(env->is_touch_down());
 
   const gfx::Point event_location(2, 3);
@@ -1240,7 +1241,7 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate.got_press());
+  EXPECT_EQ(1, window_delegate.press_count());
   EXPECT_TRUE(env->is_touch_down());
   window_delegate.reset();
 
@@ -1259,7 +1260,7 @@
   // aura::Env.
   EXPECT_EQ(ui::mojom::EventResult::UNHANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_FALSE(window_delegate.got_release());
+  EXPECT_EQ(0, window_delegate.release_count());
   EXPECT_FALSE(env->is_touch_down());
 }
 
@@ -2319,6 +2320,7 @@
   WindowTreeHostMusInitParams init_params =
       WindowTreeClientPrivate(window_tree_client_impl())
           .CallCreateInitParamsForNewDisplay();
+  init_params.display_id = display_params->display->id();
   init_params.display_init_params = std::move(display_params);
   WindowTreeHostMus window_tree_host(std::move(init_params));
   window_tree_host.InitHost();
@@ -2392,7 +2394,9 @@
   // Delegate received the event in Dips.
   const ui::PointerEvent* last_event = last_event_observed();
   ASSERT_TRUE(last_event);
-  EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, location_pixels),
+  // NOTE: the root and location are the same as there was no window supplied to
+  // OnPointerEventObserved().
+  EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, root_location_pixels),
             last_event->location());
   EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, root_location_pixels),
             last_event->root_location());
@@ -2429,8 +2433,8 @@
   child2.SetBounds(gfx::Rect(20, 30, 100, 100));
   child2.Show();
 
-  EXPECT_FALSE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
+  EXPECT_EQ(0, window_delegate1.move_count());
+  EXPECT_EQ(0, window_delegate2.move_count());
 
   // child1 has a custom targeter set which would always return itself as the
   // target window therefore event should go to child1 and should be in dip.
@@ -2447,8 +2451,8 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
+  EXPECT_EQ(1, window_delegate1.move_count());
+  EXPECT_EQ(0, window_delegate2.move_count());
   const gfx::Point event_location_in_dip(25, 30);
   EXPECT_EQ(event_location_in_dip, window_delegate1.last_event_location());
 #if defined(USE_OZONE)
@@ -2471,8 +2475,8 @@
   EXPECT_TRUE(window_tree()->WasEventAcked(event_id));
   EXPECT_EQ(ui::mojom::EventResult::HANDLED,
             window_tree()->GetEventResult(event_id));
-  EXPECT_TRUE(window_delegate1.got_move());
-  EXPECT_FALSE(window_delegate2.got_move());
+  EXPECT_EQ(1, window_delegate1.move_count());
+  EXPECT_EQ(0, window_delegate2.move_count());
   gfx::Point transformed_event_location_in_dip(event_location_in_dip.x() + 20,
                                                event_location_in_dip.y() + 30);
   EXPECT_EQ(transformed_event_location_in_dip,
@@ -2493,4 +2497,76 @@
   window2.Init(ui::LAYER_NOT_DRAWN);
 }
 
+TEST_F(WindowTreeClientWmTest, ObservedPointerEvents) {
+  const gfx::Rect bounds(1, 2, 101, 102);
+  std::unique_ptr<DisplayInitParams> display_params =
+      base::MakeUnique<DisplayInitParams>();
+  const int64_t display_id = 201;
+  float device_scale_factor = 2.0f;
+  float ui_scale_factor = 1.5f;
+  display_params->display = base::MakeUnique<display::Display>(display_id);
+  display_params->display->set_bounds(bounds);
+  display_params->viewport_metrics.bounds_in_pixels = bounds;
+  display_params->viewport_metrics.device_scale_factor = device_scale_factor;
+  display_params->viewport_metrics.ui_scale_factor = ui_scale_factor;
+  WindowTreeHostMusInitParams init_params =
+      WindowTreeClientPrivate(window_tree_client_impl())
+          .CallCreateInitParamsForNewDisplay();
+  init_params.display_id = display_id;
+  init_params.display_init_params = std::move(display_params);
+
+  WindowTreeHostMus window_tree_host(std::move(init_params));
+  window_tree_host.InitHost();
+  gfx::Transform scale_transform;
+  scale_transform.Scale(ui_scale_factor, ui_scale_factor);
+  window_tree_host.window()->SetTransform(scale_transform);
+  window_tree_host.compositor()->SetScaleAndSize(device_scale_factor,
+                                                 bounds.size());
+
+  // Start a pointer watcher for all events excluding move events.
+  window_tree_client_impl()->StartPointerWatcher(false /* want_moves */);
+
+  // Simulate the server sending an observed event.
+  const gfx::Point location_pixels(10, 12);
+  const gfx::Point root_location_pixels(14, 16);
+  std::unique_ptr<ui::PointerEvent> pointer_event_down(new ui::PointerEvent(
+      ui::ET_POINTER_DOWN, location_pixels, root_location_pixels,
+      ui::EF_CONTROL_DOWN, 0,
+      ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1),
+      base::TimeTicks()));
+  std::unique_ptr<ui::PointerEvent> pointer_event_down2(
+      ui::Event::Clone(*pointer_event_down).release()->AsPointerEvent());
+  window_tree_client()->OnPointerEventObserved(std::move(pointer_event_down),
+                                               0u, display_id);
+
+  ASSERT_FALSE(observed_pointer_events().empty());
+  const ui::PointerEvent* last_event = observed_pointer_events().back().get();
+  ASSERT_TRUE(last_event);
+  EXPECT_EQ(nullptr, last_event->target());
+  // NOTE: the root and location are the same as there was no window supplied to
+  // OnPointerEventObserved().
+  EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor,
+                                   root_location_pixels),
+            last_event->location());
+  EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor,
+                                   root_location_pixels),
+            last_event->root_location());
+
+  observed_pointer_events().clear();
+  window_tree_client()->OnPointerEventObserved(
+      std::move(pointer_event_down2),
+      WindowMus::Get(window_tree_host.window())->server_id(), display_id);
+  ASSERT_FALSE(observed_pointer_events().empty());
+  last_event = observed_pointer_events().back().get();
+  ASSERT_TRUE(last_event);
+  EXPECT_EQ(nullptr, last_event->target());
+  // |location| from the server has already had |ui_scale_factor| applied, so
+  // it won't be reapplied here.
+  EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor, location_pixels),
+            last_event->location());
+  EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor,
+                                   root_location_pixels),
+            last_event->root_location());
+}
+
 }  // namespace aura
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc
index 9213acff..066026ed 100644
--- a/ui/aura/mus/window_tree_host_mus.cc
+++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -41,8 +41,11 @@
       delegate_(init_params.window_tree_client) {
   gfx::Rect bounds_in_pixels;
   display_init_params_ = std::move(init_params.display_init_params);
-  if (display_init_params_)
+  if (display_init_params_) {
     bounds_in_pixels = display_init_params_->viewport_metrics.bounds_in_pixels;
+    if (display_init_params_->display)
+      DCHECK_EQ(display_id_, display_init_params_->display->id());
+  }
   window()->SetProperty(kWindowTreeHostMusKey, this);
   // TODO(sky): find a cleaner way to set this! Better solution is to likely
   // have constructor take aura::Window.
diff --git a/ui/aura/test/aura_test_base.cc b/ui/aura/test/aura_test_base.cc
index 37991ac..58a7b61 100644
--- a/ui/aura/test/aura_test_base.cc
+++ b/ui/aura/test/aura_test_base.cc
@@ -162,7 +162,10 @@
 void AuraTestBase::OnLostConnection(WindowTreeClient* client) {}
 
 void AuraTestBase::OnPointerEventObserved(const ui::PointerEvent& event,
-                                          Window* target) {}
+                                          Window* target) {
+  observed_pointer_events_.push_back(std::unique_ptr<ui::PointerEvent>(
+      static_cast<ui::PointerEvent*>(ui::Event::Clone(event).release())));
+}
 
 void AuraTestBase::SetWindowManagerClient(WindowManagerClient* client) {}
 
diff --git a/ui/aura/test/aura_test_base.h b/ui/aura/test/aura_test_base.h
index 1ef3d0c1..7835a515 100644
--- a/ui/aura/test/aura_test_base.h
+++ b/ui/aura/test/aura_test_base.h
@@ -92,6 +92,10 @@
   }
   ui::mojom::WindowTreeClient* window_tree_client();
 
+  std::vector<std::unique_ptr<ui::PointerEvent>>& observed_pointer_events() {
+    return observed_pointer_events_;
+  }
+
   // WindowTreeClientDelegate:
   void OnEmbed(std::unique_ptr<WindowTreeHostMus> window_tree_host) override;
   void OnUnembed(Window* root) override;
@@ -158,6 +162,7 @@
   PropertyConverter property_converter_;
   std::unique_ptr<AuraTestHelper> helper_;
   std::vector<std::unique_ptr<WindowTreeHostMus>> window_tree_hosts_;
+  std::vector<std::unique_ptr<ui::PointerEvent>> observed_pointer_events_;
 
   DISALLOW_COPY_AND_ASSIGN(AuraTestBase);
 };
diff --git a/ui/gl/init/gl_factory.cc b/ui/gl/init/gl_factory.cc
index de074f4a..643941be 100644
--- a/ui/gl/init/gl_factory.cc
+++ b/ui/gl/init/gl_factory.cc
@@ -18,37 +18,6 @@
 namespace init {
 
 namespace {
-
-bool InitializeGLOneOffImplementationHelper(GLImplementation impl,
-                                            bool fallback_to_software_gl,
-                                            bool gpu_service_logging,
-                                            bool disable_gl_drawing,
-                                            bool init_extensions) {
-  bool initialized =
-      InitializeStaticGLBindings(impl) && InitializeGLOneOffPlatform();
-  if (!initialized && fallback_to_software_gl) {
-    ShutdownGL();
-    initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation()) &&
-                  InitializeGLOneOffPlatform();
-  }
-  if (initialized && init_extensions) {
-    initialized = InitializeExtensionSettingsOneOffPlatform();
-  }
-
-  if (!initialized)
-    ShutdownGL();
-
-  if (initialized) {
-    DVLOG(1) << "Using " << GetGLImplementationName(GetGLImplementation())
-             << " GL implementation.";
-    if (gpu_service_logging)
-      InitializeDebugGLBindings();
-    if (disable_gl_drawing)
-      InitializeNullDrawGLBindings();
-  }
-  return initialized;
-}
-
 bool InitializeGLOneOffHelper(bool init_extensions) {
   DCHECK_EQ(kGLImplementationNone, GetGLImplementation());
 
@@ -86,9 +55,9 @@
   bool gpu_service_logging = cmd->HasSwitch(switches::kEnableGPUServiceLogging);
   bool disable_gl_drawing = cmd->HasSwitch(switches::kDisableGLDrawingForTests);
 
-  return InitializeGLOneOffImplementationHelper(
-      impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing,
-      init_extensions);
+  return InitializeGLOneOffImplementation(impl, fallback_to_software_gl,
+                                          gpu_service_logging,
+                                          disable_gl_drawing, init_extensions);
 }
 
 }  // namespace
@@ -106,10 +75,31 @@
 bool InitializeGLOneOffImplementation(GLImplementation impl,
                                       bool fallback_to_software_gl,
                                       bool gpu_service_logging,
-                                      bool disable_gl_drawing) {
-  return InitializeGLOneOffImplementationHelper(impl, fallback_to_software_gl,
-                                                gpu_service_logging,
-                                                disable_gl_drawing, true);
+                                      bool disable_gl_drawing,
+                                      bool init_extensions) {
+  bool initialized =
+      InitializeStaticGLBindings(impl) && InitializeGLOneOffPlatform();
+  if (!initialized && fallback_to_software_gl) {
+    ShutdownGL();
+    initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation()) &&
+                  InitializeGLOneOffPlatform();
+  }
+  if (initialized && init_extensions) {
+    initialized = InitializeExtensionSettingsOneOffPlatform();
+  }
+
+  if (!initialized)
+    ShutdownGL();
+
+  if (initialized) {
+    DVLOG(1) << "Using " << GetGLImplementationName(GetGLImplementation())
+             << " GL implementation.";
+    if (gpu_service_logging)
+      InitializeDebugGLBindings();
+    if (disable_gl_drawing)
+      InitializeNullDrawGLBindings();
+  }
+  return initialized;
 }
 
 void ShutdownGL() {
diff --git a/ui/gl/init/gl_factory.h b/ui/gl/init/gl_factory.h
index f62042a..44819da 100644
--- a/ui/gl/init/gl_factory.h
+++ b/ui/gl/init/gl_factory.h
@@ -50,7 +50,8 @@
     GLImplementation impl,
     bool fallback_to_software_gl,
     bool gpu_service_logging,
-    bool disable_gl_drawing);
+    bool disable_gl_drawing,
+    bool init_extensions);
 
 // Clears GL bindings and resets GL implementation.
 GL_INIT_EXPORT void ShutdownGL();
diff --git a/ui/gl/test/gl_surface_test_support.cc b/ui/gl/test/gl_surface_test_support.cc
index 32b45de..dcf153b 100644
--- a/ui/gl/test/gl_surface_test_support.cc
+++ b/ui/gl/test/gl_surface_test_support.cc
@@ -25,8 +25,8 @@
 
 namespace gl {
 
-// static
-void GLSurfaceTestSupport::InitializeOneOff() {
+namespace {
+void InitializeOneOffHelper(bool init_extensions) {
   DCHECK_EQ(kGLImplementationNone, GetGLImplementation());
 
 #if defined(USE_X11)
@@ -76,7 +76,19 @@
   bool disable_gl_drawing = true;
 
   CHECK(init::InitializeGLOneOffImplementation(
-      impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing));
+      impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing,
+      init_extensions));
+}
+}  // namespace
+
+// static
+void GLSurfaceTestSupport::InitializeOneOff() {
+  InitializeOneOffHelper(true);
+}
+
+// static
+void GLSurfaceTestSupport::InitializeNoExtensionsOneOff() {
+  InitializeOneOffHelper(false);
 }
 
 // static
@@ -93,8 +105,9 @@
   bool gpu_service_logging = false;
   bool disable_gl_drawing = false;
 
-  CHECK(init::InitializeGLOneOffImplementation(
-      impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing));
+  CHECK(init::InitializeGLOneOffImplementation(impl, fallback_to_software_gl,
+                                               gpu_service_logging,
+                                               disable_gl_drawing, true));
 }
 
 // static
diff --git a/ui/gl/test/gl_surface_test_support.h b/ui/gl/test/gl_surface_test_support.h
index 2fa64aea..747ffd1c 100644
--- a/ui/gl/test/gl_surface_test_support.h
+++ b/ui/gl/test/gl_surface_test_support.h
@@ -12,6 +12,7 @@
 class GLSurfaceTestSupport {
  public:
   static void InitializeOneOff();
+  static void InitializeNoExtensionsOneOff();
   static void InitializeOneOffImplementation(GLImplementation impl,
                                              bool fallback_to_osmesa);
   static void InitializeOneOffWithMockBindings();
diff --git a/ui/message_center/views/message_center_button_bar.cc b/ui/message_center/views/message_center_button_bar.cc
index 95500a5..116d3b84 100644
--- a/ui/message_center/views/message_center_button_bar.cc
+++ b/ui/message_center/views/message_center_button_bar.cc
@@ -159,8 +159,7 @@
 }
 
 void MessageCenterButtonBar::ViewVisibilityChanged() {
-  views::GridLayout* layout = new views::GridLayout(this);
-  SetLayoutManager(layout);
+  views::GridLayout* layout = views::GridLayout::CreateAndInstall(this);
   views::ColumnSet* column = layout->AddColumnSet(0);
   constexpr int kFooterLeftMargin = 4;
   column->AddPaddingColumn(0, kFooterLeftMargin);
diff --git a/ui/message_center/views/notifier_settings_view.cc b/ui/message_center/views/notifier_settings_view.cc
index 639c334..f527556 100644
--- a/ui/message_center/views/notifier_settings_view.cc
+++ b/ui/message_center/views/notifier_settings_view.cc
@@ -362,8 +362,7 @@
   using views::ColumnSet;
   using views::GridLayout;
 
-  GridLayout* layout = new GridLayout(this);
-  SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
   ColumnSet* cs = layout->AddColumnSet(0);
   // Add a column for the checkbox.
   cs->AddPaddingColumn(0, kInnateCheckboxRightPadding);
diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc
index df8d0cf..531e121 100644
--- a/ui/views/color_chooser/color_chooser_view.cc
+++ b/ui/views/color_chooser/color_chooser_view.cc
@@ -377,8 +377,7 @@
   AddChildView(container);
 
   View* container2 = new View();
-  GridLayout* layout = new GridLayout(container2);
-  container2->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container2);
   ColumnSet* columns = layout->AddColumnSet(0);
   columns->AddColumn(
       GridLayout::LEADING, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0);
diff --git a/ui/views/controls/message_box_view.cc b/ui/views/controls/message_box_view.cc
index 2445444..af7b8062 100644
--- a/ui/views/controls/message_box_view.cc
+++ b/ui/views/controls/message_box_view.cc
@@ -14,6 +14,7 @@
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/link.h"
@@ -178,6 +179,9 @@
 // MessageBoxView, private:
 
 void MessageBoxView::Init(const InitParams& params) {
+  SetBorder(CreateEmptyBorder(
+      LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_CONTENTS)));
+
   if (params.options & DETECT_DIRECTIONALITY) {
     std::vector<base::string16> texts;
     SplitStringIntoParagraphs(params.message, &texts);
@@ -213,8 +217,7 @@
 
 void MessageBoxView::ResetLayoutManager() {
   // Initialize the Grid Layout Manager used for this dialog box.
-  GridLayout* layout = GridLayout::CreatePanel(this);
-  SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(this);
 
   // Add the column set for the message displayed at the top of the dialog box.
   const int message_column_view_set_id = 0;
diff --git a/ui/views/examples/button_sticker_sheet.cc b/ui/views/examples/button_sticker_sheet.cc
index 9b32de4f..eb804408 100644
--- a/ui/views/examples/button_sticker_sheet.cc
+++ b/ui/views/examples/button_sticker_sheet.cc
@@ -31,7 +31,7 @@
   const GridLayout::SizeType kColumnUsesFixedSize = GridLayout::FIXED;
   const int kColumnWidth = 96;
 
-  GridLayout* layout = new GridLayout(host);
+  GridLayout* layout = GridLayout::CreateAndInstall(host);
   ColumnSet* columns = layout->AddColumnSet(kStretchyGridColumnSetId);
   for (int i = 0; i < ncols; ++i) {
     if (i != 0)
@@ -89,7 +89,6 @@
 
 void ButtonStickerSheet::CreateExampleView(View* container) {
   GridLayout* layout = MakeStretchyGridLayout(container, 3);
-  container->SetLayoutManager(layout);
 
   if (!ui::MaterialDesignController::IsSecondaryUiMaterial()) {
     const char* kNeedsMdWarning =
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc
index 2318b01..f5f348f 100644
--- a/ui/views/examples/dialog_example.cc
+++ b/ui/views/examples/dialog_example.cc
@@ -133,8 +133,7 @@
   views::LayoutProvider* provider = views::LayoutProvider::Get();
   const int horizontal_spacing =
       provider->GetDistanceMetric(views::DISTANCE_RELATED_BUTTON_HORIZONTAL);
-  GridLayout* layout = GridLayout::CreatePanel(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
   ColumnSet* column_set = layout->AddColumnSet(kFieldsColumnId);
   column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, kFixed,
                         GridLayout::USE_PREF, 0, 0);
diff --git a/ui/views/examples/examples_window.cc b/ui/views/examples/examples_window.cc
index ae5e2ab..a81925f 100644
--- a/ui/views/examples/examples_window.cc
+++ b/ui/views/examples/examples_window.cc
@@ -143,8 +143,7 @@
     combobox_->ModelChanged();
 
     SetBackground(CreateStandardPanelBackground());
-    GridLayout* layout = new GridLayout(this);
-    SetLayoutManager(layout);
+    GridLayout* layout = GridLayout::CreateAndInstall(this);
     ColumnSet* column_set = layout->AddColumnSet(0);
     column_set->AddPaddingColumn(0, 5);
     column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
diff --git a/ui/views/examples/label_example.cc b/ui/views/examples/label_example.cc
index 3244135..dbc8932 100644
--- a/ui/views/examples/label_example.cc
+++ b/ui/views/examples/label_example.cc
@@ -163,8 +163,7 @@
   View* control_container = new View();
   control_container->SetBorder(CreateSolidBorder(2, SK_ColorGRAY));
   control_container->SetBackground(CreateSolidBackground(SK_ColorLTGRAY));
-  GridLayout* layout = GridLayout::CreatePanel(control_container);
-  control_container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(control_container);
 
   ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
diff --git a/ui/views/examples/message_box_example.cc b/ui/views/examples/message_box_example.cc
index cb9e924..3327ff9 100644
--- a/ui/views/examples/message_box_example.cc
+++ b/ui/views/examples/message_box_example.cc
@@ -27,8 +27,7 @@
   status_ = new LabelButton(this, ASCIIToUTF16("Show Status"));
   toggle_ = new LabelButton(this, ASCIIToUTF16("Toggle Checkbox"));
 
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   message_box_view_->SetCheckBoxLabel(ASCIIToUTF16("Check Box"));
 
diff --git a/ui/views/examples/multiline_example.cc b/ui/views/examples/multiline_example.cc
index c92800f..e4d2461 100644
--- a/ui/views/examples/multiline_example.cc
+++ b/ui/views/examples/multiline_example.cc
@@ -159,8 +159,7 @@
   textfield_->set_controller(this);
   textfield_->SetText(kTestString);
 
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
diff --git a/ui/views/examples/progress_bar_example.cc b/ui/views/examples/progress_bar_example.cc
index 4cb75a15..c9d82665 100644
--- a/ui/views/examples/progress_bar_example.cc
+++ b/ui/views/examples/progress_bar_example.cc
@@ -36,8 +36,7 @@
 }
 
 void ProgressBarExample::CreateExampleView(View* container) {
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
diff --git a/ui/views/examples/radio_button_example.cc b/ui/views/examples/radio_button_example.cc
index 38072ef4..e0daa38f 100644
--- a/ui/views/examples/radio_button_example.cc
+++ b/ui/views/examples/radio_button_example.cc
@@ -38,8 +38,7 @@
     radio_buttons_[i]->set_listener(this);
   }
 
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL,
diff --git a/ui/views/examples/scroll_view_example.cc b/ui/views/examples/scroll_view_example.cc
index 39ffd8fb..ef6ff41 100644
--- a/ui/views/examples/scroll_view_example.cc
+++ b/ui/views/examples/scroll_view_example.cc
@@ -87,8 +87,7 @@
   scrollable_->SetBounds(0, 0, 1000, 100);
   scrollable_->SetColor(SK_ColorYELLOW, SK_ColorCYAN);
 
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   // Add scroll view.
   ColumnSet* column_set = layout->AddColumnSet(0);
diff --git a/ui/views/examples/tabbed_pane_example.cc b/ui/views/examples/tabbed_pane_example.cc
index 524fec6c9..2623c10b 100644
--- a/ui/views/examples/tabbed_pane_example.cc
+++ b/ui/views/examples/tabbed_pane_example.cc
@@ -27,8 +27,7 @@
   add_at_ = new LabelButton(this, ASCIIToUTF16("Add At 1"));
   select_at_ = new LabelButton(this, ASCIIToUTF16("Select At 1"));
 
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   const int tabbed_pane_column = 0;
   ColumnSet* column_set = layout->AddColumnSet(tabbed_pane_column);
diff --git a/ui/views/examples/table_example.cc b/ui/views/examples/table_example.cc
index 041270a..f3bd426 100644
--- a/ui/views/examples/table_example.cc
+++ b/ui/views/examples/table_example.cc
@@ -58,8 +58,7 @@
   column4_visible_checkbox_->SetChecked(true);
   column4_visible_checkbox_->set_listener(this);
 
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   std::vector<ui::TableColumn> columns;
   columns.push_back(TestTableColumn(0, "Fruit"));
diff --git a/ui/views/examples/text_example.cc b/ui/views/examples/text_example.cc
index 6869b076..37b1ef11 100644
--- a/ui/views/examples/text_example.cc
+++ b/ui/views/examples/text_example.cc
@@ -160,8 +160,7 @@
 void TextExample::CreateExampleView(View* container) {
   text_view_ = new TextExampleView;
   text_view_->SetBorder(CreateSolidBorder(1, SK_ColorGRAY));
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
   layout->AddPaddingRow(0, 8);
 
   ColumnSet* column_set = layout->AddColumnSet(0);
diff --git a/ui/views/examples/textfield_example.cc b/ui/views/examples/textfield_example.cc
index 6f3c5f2..e166ac0 100644
--- a/ui/views/examples/textfield_example.cc
+++ b/ui/views/examples/textfield_example.cc
@@ -61,8 +61,7 @@
   name_->set_controller(this);
   password_->set_controller(this);
 
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
diff --git a/ui/views/examples/tree_view_example.cc b/ui/views/examples/tree_view_example.cc
index 8e0f6a1..4ecdd6d3 100644
--- a/ui/views/examples/tree_view_example.cc
+++ b/ui/views/examples/tree_view_example.cc
@@ -54,8 +54,7 @@
   change_title_->SetFocusForPlatform();
   change_title_->set_request_focus_on_press(true);
 
-  GridLayout* layout = new GridLayout(container);
-  container->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(container);
 
   const int tree_view_column = 0;
   ColumnSet* column_set = layout->AddColumnSet(tree_view_column);
diff --git a/ui/views/layout/grid_layout.cc b/ui/views/layout/grid_layout.cc
index eff6527..0775b73 100644
--- a/ui/views/layout/grid_layout.cc
+++ b/ui/views/layout/grid_layout.cc
@@ -632,29 +632,16 @@
 
 // GridLayout -------------------------------------------------------------
 
-GridLayout::GridLayout(View* host)
-    : host_(host),
-      calculated_master_columns_(false),
-      remaining_row_span_(0),
-      current_row_(-1),
-      next_column_(0),
-      current_row_col_set_(nullptr),
-      adding_view_(false) {
-  DCHECK(host);
+// static
+GridLayout* GridLayout::CreateAndInstall(View* host) {
+  GridLayout* result = new GridLayout(host);
+  host->SetLayoutManager(result);
+  return result;
 }
 
 GridLayout::~GridLayout() {
 }
 
-// static
-GridLayout* GridLayout::CreatePanel(View* host) {
-  GridLayout* layout = new GridLayout(host);
-  host->SetBorder(CreateEmptyBorder(
-      LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_CONTENTS)));
-  host->SetLayoutManager(layout);
-  return layout;
-}
-
 ColumnSet* GridLayout::AddColumnSet(int id) {
   DCHECK(GetColumnSet(id) == nullptr);
   column_sets_.push_back(base::WrapUnique(new ColumnSet(id)));
@@ -810,6 +797,17 @@
   return pref.height();
 }
 
+GridLayout::GridLayout(View* host)
+    : host_(host),
+      calculated_master_columns_(false),
+      remaining_row_span_(0),
+      current_row_(-1),
+      next_column_(0),
+      current_row_col_set_(nullptr),
+      adding_view_(false) {
+  DCHECK(host);
+}
+
 void GridLayout::SizeRowsAndColumns(bool layout, int width, int height,
                                     gfx::Size* pref) const {
   // Protect against clients asking for metrics during the addition of a View.
diff --git a/ui/views/layout/grid_layout.h b/ui/views/layout/grid_layout.h
index 66d9f65a..a2521d4 100644
--- a/ui/views/layout/grid_layout.h
+++ b/ui/views/layout/grid_layout.h
@@ -100,12 +100,10 @@
     USE_PREF
   };
 
-  explicit GridLayout(View* host);
-  ~GridLayout() override;
+  // Creates a new GridLayout and installs it as the LayoutManager for |host|.
+  static GridLayout* CreateAndInstall(View* host);
 
-  // Creates a GridLayout, assigns it as the LayoutManager of |host|, and gives
-  // it a INSETS_PANEL-sized padding border.
-  static GridLayout* CreatePanel(View* host);
+  ~GridLayout() override;
 
   // Creates a new column set with the specified id and returns it.
   // The id is later used when starting a new row.
@@ -180,6 +178,8 @@
   void set_minimum_size(const gfx::Size& size) { minimum_size_ = size; }
 
  private:
+  explicit GridLayout(View* host);
+
   // As both Layout and GetPreferredSize need to do nearly the same thing,
   // they both call into this method. This sizes the Columns/Rows as
   // appropriate. If layout is true, width/height give the width/height the
diff --git a/ui/views/layout/grid_layout_unittest.cc b/ui/views/layout/grid_layout_unittest.cc
index ac58f08e..686eefb 100644
--- a/ui/views/layout/grid_layout_unittest.cc
+++ b/ui/views/layout/grid_layout_unittest.cc
@@ -78,50 +78,54 @@
 
 class GridLayoutTest : public testing::Test {
  public:
-  GridLayoutTest() : layout(&host) {}
+  GridLayoutTest() : layout_(GridLayout::CreateAndInstall(&host_)) {}
 
   void RemoveAll() {
-    for (int i = host.child_count() - 1; i >= 0; i--)
-      host.RemoveChildView(host.child_at(i));
+    for (int i = host_.child_count() - 1; i >= 0; i--)
+      host_.RemoveChildView(host_.child_at(i));
   }
 
-  void GetPreferredSize() {
-    pref = layout.GetPreferredSize(&host);
-  }
+  gfx::Size GetPreferredSize() { return layout_->GetPreferredSize(&host_); }
 
-  gfx::Size pref;
-  gfx::Rect bounds;
-  View host;
-  GridLayout layout;
+  View& host() { return host_; }
+  GridLayout* layout() { return layout_; }
+
+ private:
+  View host_;
+  GridLayout* layout_;
 };
 
 class GridLayoutAlignmentTest : public testing::Test {
  public:
-  GridLayoutAlignmentTest() : layout(&host) {
-    v1.SetPreferredSize(gfx::Size(10, 20));
+  GridLayoutAlignmentTest() : layout_(GridLayout::CreateAndInstall(&host_)) {
+    v1_.SetPreferredSize(gfx::Size(10, 20));
   }
 
   void RemoveAll() {
-    for (int i = host.child_count() - 1; i >= 0; i--)
-      host.RemoveChildView(host.child_at(i));
+    for (int i = host_.child_count() - 1; i >= 0; i--)
+      host_.RemoveChildView(host_.child_at(i));
   }
 
   void TestAlignment(GridLayout::Alignment alignment, gfx::Rect* bounds) {
-    ColumnSet* c1 = layout.AddColumnSet(0);
+    ColumnSet* c1 = layout_->AddColumnSet(0);
     c1->AddColumn(alignment, alignment, 1, GridLayout::USE_PREF, 0, 0);
-    layout.StartRow(1, 0);
-    layout.AddView(&v1);
-    gfx::Size pref = layout.GetPreferredSize(&host);
+    layout_->StartRow(1, 0);
+    layout_->AddView(&v1_);
+    gfx::Size pref = layout_->GetPreferredSize(&host_);
     EXPECT_EQ(gfx::Size(10, 20), pref);
-    host.SetBounds(0, 0, 100, 100);
-    layout.Layout(&host);
-    *bounds = v1.bounds();
+    host_.SetBounds(0, 0, 100, 100);
+    layout_->Layout(&host_);
+    *bounds = v1_.bounds();
     RemoveAll();
   }
 
-  View host;
-  View v1;
-  GridLayout layout;
+  View& host() { return host_; }
+  GridLayout* layout() { return layout_; }
+
+ private:
+  View host_;
+  View v1_;
+  GridLayout* layout_;
 };
 
 TEST_F(GridLayoutAlignmentTest, Fill) {
@@ -153,20 +157,20 @@
   v1.SetPreferredSize(gfx::Size(10, 20));
   View v2;
   v2.SetPreferredSize(gfx::Size(20, 20));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
-  layout.AddView(&v2);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
+  layout()->AddView(&v2);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(30, 20), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 10, 20, &v1);
   ExpectViewBoundsEquals(10, 0, 20, 20, &v2);
 
@@ -181,7 +185,7 @@
   v2.SetPreferredSize(gfx::Size(20, 20));
   View v3;
   v3.SetPreferredSize(gfx::Size(0, 20));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
 
   // Fill widths.
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF,
@@ -191,29 +195,30 @@
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF,
                 0, 0);
 
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
-  layout.AddView(&v2);
-  layout.AddView(&v3);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
+  layout()->AddView(&v2);
+  layout()->AddView(&v3);
 
   // Link all the columns.
   c1->LinkColumnSizes(0, 1, 2, -1);
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
 
   // |v1| and |v3| should obtain the same width as |v2|, since |v2| is largest.
+  pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(20 + 20 + 20, 20), pref);
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 20, 20, &v1);
   ExpectViewBoundsEquals(20, 0, 20, 20, &v2);
   ExpectViewBoundsEquals(40, 0, 20, 20, &v3);
 
   // If the limit is zero, behaves as though the columns are not linked.
   c1->set_linked_column_size_limit(0);
-  GetPreferredSize();
+  pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(10 + 20 + 0, 20), pref);
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 10, 20, &v1);
   ExpectViewBoundsEquals(10, 0, 20, 20, &v2);
   ExpectViewBoundsEquals(30, 0, 0, 20, &v3);
@@ -221,12 +226,12 @@
   // Set a size limit.
   c1->set_linked_column_size_limit(40);
   v1.SetPreferredSize(gfx::Size(35, 20));
-  GetPreferredSize();
 
   // |v1| now dominates, but it is still below the limit.
+  pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(35 + 35 + 35, 20), pref);
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 35, 20, &v1);
   ExpectViewBoundsEquals(35, 0, 35, 20, &v2);
   ExpectViewBoundsEquals(70, 0, 35, 20, &v3);
@@ -234,10 +239,10 @@
   // Go over the limit. |v1| shouldn't influence size at all, but the others
   // should still be linked to the next largest width.
   v1.SetPreferredSize(gfx::Size(45, 20));
-  GetPreferredSize();
+  pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(45 + 20 + 20, 20), pref);
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 45, 20, &v1);
   ExpectViewBoundsEquals(45, 0, 20, 20, &v2);
   ExpectViewBoundsEquals(65, 0, 20, 20, &v3);
@@ -250,21 +255,21 @@
   v1.SetPreferredSize(gfx::Size(100, 20));
   View v2;
   v2.SetPreferredSize(gfx::Size(10, 40));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1, 2, 1);
-  layout.StartRow(0, 0);
-  layout.AddView(&v2);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1, 2, 1);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v2);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(100, 60), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
   ExpectViewBoundsEquals(0, 20, 10, 40, &v2);
 
@@ -276,22 +281,22 @@
   v1.SetPreferredSize(gfx::Size(100, 20));
   View v2;
   v2.SetPreferredSize(gfx::Size(10, 20));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1, 2, 1);
-  layout.StartRow(0, 0);
-  layout.SkipColumns(1);
-  layout.AddView(&v2);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1, 2, 1);
+  layout()->StartRow(0, 0);
+  layout()->SkipColumns(1);
+  layout()->AddView(&v2);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(100, 40), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
   ExpectViewBoundsEquals(90, 20, 10, 20, &v2);
 
@@ -305,22 +310,22 @@
   v2.SetPreferredSize(gfx::Size(10, 20));
   View v3;
   v3.SetPreferredSize(gfx::Size(10, 20));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1, 2, 1);
-  layout.StartRow(0, 0);
-  layout.AddView(&v2);
-  layout.AddView(&v3);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1, 2, 1);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v2);
+  layout()->AddView(&v3);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(100, 40), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
   ExpectViewBoundsEquals(0, 20, 10, 20, &v2);
   ExpectViewBoundsEquals(50, 20, 10, 20, &v3);
@@ -330,7 +335,7 @@
 
 
 TEST_F(GridLayoutTest, ColSpan4) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,
                  GridLayout::USE_PREF, 0, 0);
@@ -343,17 +348,17 @@
   v2.SetPreferredSize(gfx::Size(10, 10));
   View v3;
   v3.SetPreferredSize(gfx::Size(25, 20));
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
-  layout.AddView(&v2);
-  layout.StartRow(0, 0);
-  layout.AddView(&v3, 2, 1);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
+  layout()->AddView(&v2);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v3, 2, 1);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(25, 30), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 10, 10, &v1);
   ExpectViewBoundsEquals(12, 0, 10, 10, &v2);
   ExpectViewBoundsEquals(0, 10, 25, 20, &v3);
@@ -364,7 +369,7 @@
 // Verifies the sizing of a view that doesn't start in the first column
 // and has a column span > 1 (crbug.com/254092).
 TEST_F(GridLayoutTest, ColSpanStartSecondColumn) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
                  GridLayout::USE_PREF, 0, 0);
@@ -378,15 +383,15 @@
   View v2;
   v2.SetPreferredSize(gfx::Size(20, 10));
 
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
-  layout.AddView(&v2, 2, 1);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
+  layout()->AddView(&v2, 2, 1);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(30, 10), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 10, 10, &v1);
   ExpectViewBoundsEquals(10, 0, 20, 10, &v2);
 
@@ -398,21 +403,21 @@
   v1.SetPreferredSize(gfx::Size(50, 20));
   View v2;
   v2.SetPreferredSize(gfx::Size(10, 10));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
   c1->LinkColumnSizes(0, 1, -1);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
-  layout.AddView(&v2);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
+  layout()->AddView(&v2);
 
-  gfx::Size pref = layout.GetPreferredSize(&host);
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(100, 20), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 50, 20, &v1);
   ExpectViewBoundsEquals(50, 0, 10, 10, &v2);
 
@@ -424,17 +429,17 @@
   v1.SetPreferredSize(gfx::Size(50, 20));
   View v2;
   v2.SetPreferredSize(gfx::Size(10, 10));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
-  layout.AddView(&v2);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
+  layout()->AddView(&v2);
 
-  host.SetBounds(0, 0, 110, 20);
-  layout.Layout(&host);
+  host().SetBounds(0, 0, 110, 20);
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 100, 20, &v1);
   ExpectViewBoundsEquals(100, 0, 10, 10, &v2);
 
@@ -446,17 +451,17 @@
   v1.SetPreferredSize(gfx::Size(50, 20));
   View v2;
   v2.SetPreferredSize(gfx::Size(10, 10));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
-  layout.AddView(&v2);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
+  layout()->AddView(&v2);
 
-  host.SetBounds(0, 0, 120, 20);
-  layout.Layout(&host);
+  host().SetBounds(0, 0, 120, 20);
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 80, 20, &v1);
   ExpectViewBoundsEquals(110, 0, 10, 10, &v2);
 
@@ -472,20 +477,20 @@
   v2.SetPreferredSize(gfx::Size(10, 10));
   View v3;
   v3.SetPreferredSize(gfx::Size(10, 10));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::FILL, GridLayout::LEADING,
                 1, GridLayout::USE_PREF, 0, 0);
   c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
-  layout.AddView(&v2);
-  layout.AddView(&v3);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
+  layout()->AddView(&v2);
+  layout()->AddView(&v3);
 
-  host.SetBounds(0, 0, 31, 10);
-  layout.Layout(&host);
+  host().SetBounds(0, 0, 31, 10);
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 10, 10, &v1);
   ExpectViewBoundsEquals(10, 0, 11, 10, &v2);
   ExpectViewBoundsEquals(21, 0, 10, 10, &v3);
@@ -498,19 +503,19 @@
   v1.SetPreferredSize(gfx::Size(50, 20));
   View v2;
   v2.SetPreferredSize(gfx::Size(10, 10));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::FILL, GridLayout::FILL,
                 1, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(1, 0);
-  layout.AddView(&v1);
-  layout.StartRow(0, 0);
-  layout.AddView(&v2);
+  layout()->StartRow(1, 0);
+  layout()->AddView(&v1);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v2);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(50, 30), pref);
 
-  host.SetBounds(0, 0, 50, 100);
-  layout.Layout(&host);
+  host().SetBounds(0, 0, 50, 100);
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 50, 90, &v1);
   ExpectViewBoundsEquals(0, 90, 50, 10, &v2);
 
@@ -518,29 +523,29 @@
 }
 
 TEST_F(GridLayoutTest, Border) {
-  host.SetBorder(CreateEmptyBorder(1, 2, 3, 4));
+  host().SetBorder(CreateEmptyBorder(1, 2, 3, 4));
   View v1;
   v1.SetPreferredSize(gfx::Size(10, 20));
-  ColumnSet* c1 = layout.AddColumnSet(0);
+  ColumnSet* c1 = layout()->AddColumnSet(0);
   c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                 0, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(16, 24), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(2, 1, 10, 20, &v1);
 
   RemoveAll();
 }
 
 TEST_F(GridLayoutTest, FixedSize) {
-  host.SetBorder(CreateEmptyBorder(2, 2, 2, 2));
+  host().SetBorder(CreateEmptyBorder(2, 2, 2, 2));
 
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   int column_count = 4;
   int title_width = 100;
@@ -558,17 +563,17 @@
   }
 
   for (int row = 0; row < row_count; ++row) {
-    layout.StartRow(0, 0);
+    layout()->StartRow(0, 0);
     for (int col = 0; col < column_count; ++col) {
-      layout.AddView(CreateSizedView(gfx::Size(pref_width, pref_height)));
+      layout()->AddView(CreateSizedView(gfx::Size(pref_width, pref_height)));
     }
   }
 
-  layout.Layout(&host);
+  layout()->Layout(&host());
 
   for (int i = 0; i < column_count; ++i) {
     for (int row = 0; row < row_count; ++row) {
-      View* view = host.child_at(row * column_count + i);
+      View* view = host().child_at(row * column_count + i);
       ExpectViewBoundsEquals(
           2 + title_width * i + (title_width - pref_width) / 2,
           2 + pref_height * row,
@@ -577,13 +582,13 @@
     }
   }
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(column_count * title_width + 4,
                       row_count * pref_height + 4), pref);
 }
 
 TEST_F(GridLayoutTest, RowSpanWithPaddingRow) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   set->AddColumn(GridLayout::CENTER,
                  GridLayout::CENTER,
@@ -592,13 +597,13 @@
                  10,
                  10);
 
-  layout.StartRow(0, 0);
-  layout.AddView(CreateSizedView(gfx::Size(10, 10)), 1, 2);
-  layout.AddPaddingRow(0, 10);
+  layout()->StartRow(0, 0);
+  layout()->AddView(CreateSizedView(gfx::Size(10, 10)), 1, 2);
+  layout()->AddPaddingRow(0, 10);
 }
 
 TEST_F(GridLayoutTest, RowSpan) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   set->AddColumn(GridLayout::LEADING,
                  GridLayout::LEADING,
@@ -613,112 +618,114 @@
                  0,
                  0);
 
-  layout.StartRow(0, 0);
-  layout.AddView(CreateSizedView(gfx::Size(20, 10)));
-  layout.AddView(CreateSizedView(gfx::Size(20, 40)), 1, 2);
-  layout.StartRow(1, 0);
+  layout()->StartRow(0, 0);
+  layout()->AddView(CreateSizedView(gfx::Size(20, 10)));
+  layout()->AddView(CreateSizedView(gfx::Size(20, 40)), 1, 2);
+  layout()->StartRow(1, 0);
   View* s3 = CreateSizedView(gfx::Size(20, 10));
-  layout.AddView(s3);
+  layout()->AddView(s3);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(40, 40), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 10, 20, 10, s3);
 }
 
 TEST_F(GridLayoutTest, RowSpan2) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                  0, GridLayout::USE_PREF, 0, 0);
   set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                  0,GridLayout::USE_PREF, 0, 0);
 
-  layout.StartRow(0, 0);
-  layout.AddView(CreateSizedView(gfx::Size(20, 20)));
+  layout()->StartRow(0, 0);
+  layout()->AddView(CreateSizedView(gfx::Size(20, 20)));
   View* s3 = CreateSizedView(gfx::Size(64, 64));
-  layout.AddView(s3, 1, 3);
+  layout()->AddView(s3, 1, 3);
 
-  layout.AddPaddingRow(0, 10);
+  layout()->AddPaddingRow(0, 10);
 
-  layout.StartRow(0, 0);
-  layout.AddView(CreateSizedView(gfx::Size(10, 20)));
+  layout()->StartRow(0, 0);
+  layout()->AddView(CreateSizedView(gfx::Size(10, 20)));
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(84, 64), pref);
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(20, 0, 64, 64, s3);
 }
 
 TEST_F(GridLayoutTest, FixedViewWidth) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                  0, GridLayout::USE_PREF, 0, 0);
   set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                  0,GridLayout::USE_PREF, 0, 0);
 
-  layout.StartRow(0, 0);
+  layout()->StartRow(0, 0);
   View* view = CreateSizedView(gfx::Size(30, 40));
-  layout.AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 10, 0);
+  layout()->AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 10,
+                    0);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(10, pref.width());
   EXPECT_EQ(40, pref.height());
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 10, 40, view);
 }
 
 TEST_F(GridLayoutTest, FixedViewHeight) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                  0, GridLayout::USE_PREF, 0, 0);
   set->AddColumn(GridLayout::LEADING, GridLayout::LEADING,
                  0,GridLayout::USE_PREF, 0, 0);
 
-  layout.StartRow(0, 0);
+  layout()->StartRow(0, 0);
   View* view = CreateSizedView(gfx::Size(30, 40));
-  layout.AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 0, 10);
+  layout()->AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 0,
+                    10);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(30, pref.width());
   EXPECT_EQ(10, pref.height());
 
-  host.SetBounds(0, 0, pref.width(), pref.height());
-  layout.Layout(&host);
+  host().SetBounds(0, 0, pref.width(), pref.height());
+  layout()->Layout(&host());
   ExpectViewBoundsEquals(0, 0, 30, 10, view);
 }
 
 // Make sure that for views that span columns the underlying columns are resized
 // based on the resize percent of the column.
 TEST_F(GridLayoutTest, ColumnSpanResizing) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
 
   set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
                  2, GridLayout::USE_PREF, 0, 0);
   set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
                  4, GridLayout::USE_PREF, 0, 0);
 
-  layout.StartRow(0, 0);
+  layout()->StartRow(0, 0);
   // span_view spans two columns and is twice as big the views added below.
   View* span_view = CreateSizedView(gfx::Size(12, 40));
-  layout.AddView(span_view, 2, 1, GridLayout::LEADING, GridLayout::LEADING);
+  layout()->AddView(span_view, 2, 1, GridLayout::LEADING, GridLayout::LEADING);
 
-  layout.StartRow(0, 0);
+  layout()->StartRow(0, 0);
   View* view1 = CreateSizedView(gfx::Size(2, 40));
   View* view2 = CreateSizedView(gfx::Size(4, 40));
-  layout.AddView(view1);
-  layout.AddView(view2);
+  layout()->AddView(view1);
+  layout()->AddView(view2);
 
-  host.SetBounds(0, 0, 12, 80);
-  layout.Layout(&host);
+  host().SetBounds(0, 0, 12, 80);
+  layout()->Layout(&host());
 
   ExpectViewBoundsEquals(0, 0, 12, 40, span_view);
 
@@ -736,55 +743,55 @@
 // there is additional space in the case we have column sets of different
 // preferred sizes.
 TEST_F(GridLayoutTest, ColumnResizingOnGetPreferredSize) {
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
   set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
                  1, GridLayout::USE_PREF, 0, 0);
 
-  set = layout.AddColumnSet(1);
+  set = layout()->AddColumnSet(1);
   set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
                  1, GridLayout::USE_PREF, 0, 0);
 
-  set = layout.AddColumnSet(2);
+  set = layout()->AddColumnSet(2);
   set->AddColumn(GridLayout::FILL, GridLayout::CENTER,
                  1, GridLayout::USE_PREF, 0, 0);
 
   // Make a row containing a flexible view that trades width for height.
-  layout.StartRow(0, 0);
+  layout()->StartRow(0, 0);
   View* view1 = new FlexibleView(100);
-  layout.AddView(view1, 1, 1, GridLayout::FILL, GridLayout::LEADING);
+  layout()->AddView(view1, 1, 1, GridLayout::FILL, GridLayout::LEADING);
 
   // The second row contains a view of fixed size that will enforce a column
   // width of 20 pixels.
-  layout.StartRow(0, 1);
+  layout()->StartRow(0, 1);
   View* view2 = CreateSizedView(gfx::Size(20, 20));
-  layout.AddView(view2, 1, 1, GridLayout::FILL, GridLayout::LEADING);
+  layout()->AddView(view2, 1, 1, GridLayout::FILL, GridLayout::LEADING);
 
   // Add another flexible view in row three in order to ensure column set
   // ordering doesn't influence sizing behaviour.
-  layout.StartRow(0, 2);
+  layout()->StartRow(0, 2);
   View* view3 = new FlexibleView(40);
-  layout.AddView(view3, 1, 1, GridLayout::FILL, GridLayout::LEADING);
+  layout()->AddView(view3, 1, 1, GridLayout::FILL, GridLayout::LEADING);
 
   // We expect a height of 50: 30 from the variable width view in the first row
   // plus 20 from the statically sized view in the second row. The flexible
   // view in the third row should contribute no height.
-  EXPECT_EQ(gfx::Size(20, 50), layout.GetPreferredSize(&host));
+  EXPECT_EQ(gfx::Size(20, 50), GetPreferredSize());
 }
 
 TEST_F(GridLayoutTest, MinimumPreferredSize) {
   View v1;
   v1.SetPreferredSize(gfx::Size(10, 20));
-  ColumnSet* set = layout.AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
   set->AddColumn(GridLayout::FILL, GridLayout::FILL,
                  0, GridLayout::USE_PREF, 0, 0);
-  layout.StartRow(0, 0);
-  layout.AddView(&v1);
+  layout()->StartRow(0, 0);
+  layout()->AddView(&v1);
 
-  GetPreferredSize();
+  gfx::Size pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(10, 20), pref);
 
-  layout.set_minimum_size(gfx::Size(40, 40));
-  GetPreferredSize();
+  layout()->set_minimum_size(gfx::Size(40, 40));
+  pref = GetPreferredSize();
   EXPECT_EQ(gfx::Size(40, 40), pref);
 
   RemoveAll();
@@ -801,22 +808,18 @@
   if (PlatformTestHelper::IsMus())
     return;
 
-  // Don't use the |layout| data member from the test harness, otherwise
-  // SetLayoutManager() can take not take ownership.
-  GridLayout* grid_layout = new GridLayout(&host);
-  host.SetLayoutManager(grid_layout);
-  ColumnSet* set = grid_layout->AddColumnSet(0);
+  ColumnSet* set = layout()->AddColumnSet(0);
   set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0,
                  0);
-  grid_layout->StartRow(0, 0);
+  layout()->StartRow(0, 0);
   LayoutOnAddView view;
-  EXPECT_DCHECK_DEATH(grid_layout->AddView(&view));
+  EXPECT_DCHECK_DEATH(layout()->AddView(&view));
   // Death tests use fork(), so nothing should be added here.
   EXPECT_FALSE(view.parent());
 
   // If the View has nothing to change, adding should succeed.
   view.set_target_size(view.GetPreferredSize());
-  grid_layout->AddView(&view);
+  layout()->AddView(&view);
   EXPECT_TRUE(view.parent());
 
   RemoveAll();
diff --git a/ui/views/win/fullscreen_handler.cc b/ui/views/win/fullscreen_handler.cc
index 6cbe30a..7f13e846 100644
--- a/ui/views/win/fullscreen_handler.cc
+++ b/ui/views/win/fullscreen_handler.cc
@@ -29,6 +29,24 @@
   SetFullscreenImpl(fullscreen);
 }
 
+void FullscreenHandler::MarkFullscreen(bool fullscreen) {
+  if (!task_bar_list_) {
+    HRESULT hr =
+        ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,
+                           IID_PPV_ARGS(&task_bar_list_));
+    if (SUCCEEDED(hr) && FAILED(task_bar_list_->HrInit()))
+      task_bar_list_ = nullptr;
+  }
+
+  // As per MSDN marking the window as fullscreen should ensure that the
+  // taskbar is moved to the bottom of the Z-order when the fullscreen window
+  // is activated. If the window is not fullscreen, the Shell falls back to
+  // heuristics to determine how the window should be treated, which means
+  // that it could still consider the window as fullscreen. :(
+  if (task_bar_list_)
+    task_bar_list_->MarkFullscreenWindow(hwnd_, !!fullscreen);
+}
+
 gfx::Rect FullscreenHandler::GetRestoreBounds() const {
   return gfx::Rect(saved_window_info_.window_rect);
 }
@@ -86,21 +104,7 @@
                  SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
   }
 
-  if (!task_bar_list_) {
-    HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList, NULL,
-                                    CLSCTX_INPROC_SERVER,
-                                    IID_PPV_ARGS(&task_bar_list_));
-    if (SUCCEEDED(hr) && FAILED(task_bar_list_->HrInit()))
-      task_bar_list_ = nullptr;
-  }
-
-  // As per MSDN marking the window as fullscreen should ensure that the
-  // taskbar is moved to the bottom of the Z-order when the fullscreen window
-  // is activated. If the window is not fullscreen, the Shell falls back to
-  // heuristics to determine how the window should be treated, which means
-  // that it could still consider the window as fullscreen. :(
-  if (task_bar_list_)
-    task_bar_list_->MarkFullscreenWindow(hwnd_, !!fullscreen);
+  MarkFullscreen(fullscreen);
 }
 
 }  // namespace views
diff --git a/ui/views/win/fullscreen_handler.h b/ui/views/win/fullscreen_handler.h
index 772d275..62eb9b4 100644
--- a/ui/views/win/fullscreen_handler.h
+++ b/ui/views/win/fullscreen_handler.h
@@ -27,6 +27,9 @@
 
   void SetFullscreen(bool fullscreen);
 
+  // Informs the taskbar whether the window is a fullscreen window.
+  void MarkFullscreen(bool fullscreen);
+
   gfx::Rect GetRestoreBounds() const;
 
   bool fullscreen() const { return fullscreen_; }
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 451a5c06..b611fa18 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -1139,6 +1139,11 @@
     GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY),
                    &monitor_info);
     SetBoundsInternal(gfx::Rect(monitor_info.rcMonitor), false);
+    // Inform the taskbar that this window is now a fullscreen window so it go
+    // behind the window in the Z-Order. The taskbar heuristics to detect
+    // fullscreen windows are not reliable. Marking it explicitly seems to work
+    // around these problems.
+    fullscreen_handler()->MarkFullscreen(true);
     background_fullscreen_hack_ = false;
   } else {
     // If the window becoming active has a fullscreen window on the same
@@ -3005,6 +3010,11 @@
   shrunk_rect.set_height(shrunk_rect.height() - 1);
   background_fullscreen_hack_ = true;
   SetBoundsInternal(shrunk_rect, false);
+  // Inform the taskbar that this window is no longer a fullscreen window so it
+  // can bring itself to the top of the Z-Order. The taskbar heuristics to
+  // detect fullscreen windows are not reliable. Marking it explicitly seems to
+  // work around these problems.
+  fullscreen_handler()->MarkFullscreen(false);
 }
 
 void HWNDMessageHandler::DestroyAXSystemCaret() {
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc
index 256ba5dc..7cd77e42 100644
--- a/ui/views/window/dialog_client_view.cc
+++ b/ui/views/window/dialog_client_view.cc
@@ -326,14 +326,14 @@
 
 void DialogClientView::SetupLayout() {
   base::AutoReset<bool> auto_reset(&adding_or_removing_views_, true);
-  GridLayout* layout = new GridLayout(button_row_container_);
-  layout->set_minimum_size(minimum_size_);
   FocusManager* focus_manager = GetFocusManager();
   ViewTracker view_tracker(focus_manager->GetFocusedView());
 
   // Clobber any existing LayoutManager since it has weak references to child
   // Views which may be removed by SetupViews().
-  button_row_container_->SetLayoutManager(layout);
+  GridLayout* layout = GridLayout::CreateAndInstall(button_row_container_);
+  layout->set_minimum_size(minimum_size_);
+
   SetupViews();
   const std::array<View*, kNumButtons> views = GetButtonRowViews();