diff --git a/DEPS b/DEPS
index 4e7d417..35decb4 100644
--- a/DEPS
+++ b/DEPS
@@ -308,7 +308,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1413d75bc71561db4dd2f9085650c6b58a77161f',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2a1ca3f68bc97c69537d46890348bfc79483ed60',
 
   # DevTools node modules. Used on Linux buildbots only.
   'src/third_party/devtools-node-modules': {
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 62449fd..d682d8434 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -102,6 +102,8 @@
     "display/display_configuration_controller.h",
     "display/display_error_observer_chromeos.cc",
     "display/display_error_observer_chromeos.h",
+    "display/display_move_window_util.cc",
+    "display/display_move_window_util.h",
     "display/display_util.cc",
     "display/display_util.h",
     "display/event_transformation_handler.cc",
@@ -1254,6 +1256,7 @@
     "display/display_configuration_controller_unittest.cc",
     "display/display_error_observer_chromeos_unittest.cc",
     "display/display_manager_unittest.cc",
+    "display/display_move_window_util_unittest.cc",
     "display/display_util_unittest.cc",
     "display/extended_mouse_warp_controller_unittest.cc",
     "display/mirror_window_controller_unittest.cc",
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 89225e0..e6d29be5 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -13,6 +13,7 @@
 #include "ash/accessibility/accessibility_delegate.h"
 #include "ash/accessibility_types.h"
 #include "ash/display/display_configuration_controller.h"
+#include "ash/display/display_move_window_util.h"
 #include "ash/focus_cycler.h"
 #include "ash/ime/ime_controller.h"
 #include "ash/ime/ime_switch_type.h"
@@ -257,6 +258,22 @@
   Shell::Get()->media_controller()->HandleMediaPrevTrack();
 }
 
+void HandleMoveWindowBetweenDisplays(const ui::Accelerator& accelerator) {
+  ui::KeyboardCode key_code = accelerator.key_code();
+  DisplayMoveWindowDirection direction;
+  if (key_code == ui::VKEY_LEFT) {
+    direction = DisplayMoveWindowDirection::kLeft;
+  } else if (key_code == ui::VKEY_UP) {
+    direction = DisplayMoveWindowDirection::kAbove;
+  } else if (key_code == ui::VKEY_RIGHT) {
+    direction = DisplayMoveWindowDirection::kRight;
+  } else {
+    DCHECK(key_code == ui::VKEY_DOWN);
+    direction = DisplayMoveWindowDirection::kBelow;
+  }
+  HandleMoveWindowToDisplay(direction);
+}
+
 void HandleToggleMirrorMode() {
   base::RecordAction(UserMetricsAction("Accel_Toggle_Mirror_Mode"));
   bool mirror = !Shell::Get()->display_manager()->IsInMirrorMode();
@@ -613,6 +630,14 @@
   Shell::Get()->session_controller()->LockScreen();
 }
 
+bool CanHandleMoveWindowBetweenDisplays() {
+  display::DisplayManager* display_manager = Shell::Get()->display_manager();
+  // Accelerators to move window between displays on unified desktop mode and
+  // mirror mode is disabled.
+  return !display_manager->IsInUnifiedMode() &&
+         !display_manager->IsInMirrorMode();
+}
+
 PaletteTray* GetPaletteTray() {
   return Shelf::ForWindow(Shell::GetRootWindowForNewWindows())
       ->GetStatusAreaWidget()
@@ -1073,6 +1098,11 @@
       return CanHandleDisableCapsLock(previous_accelerator);
     case LOCK_SCREEN:
       return CanHandleLock();
+    case MOVE_WINDOW_TO_ABOVE_DISPLAY:
+    case MOVE_WINDOW_TO_BELOW_DISPLAY:
+    case MOVE_WINDOW_TO_LEFT_DISPLAY:
+    case MOVE_WINDOW_TO_RIGHT_DISPLAY:
+      return CanHandleMoveWindowBetweenDisplays();
     case NEW_INCOGNITO_WINDOW:
       return CanHandleNewIncognitoWindow();
     case NEXT_IME:
@@ -1287,6 +1317,12 @@
     case MEDIA_PREV_TRACK:
       HandleMediaPrevTrack();
       break;
+    case MOVE_WINDOW_TO_ABOVE_DISPLAY:
+    case MOVE_WINDOW_TO_BELOW_DISPLAY:
+    case MOVE_WINDOW_TO_LEFT_DISPLAY:
+    case MOVE_WINDOW_TO_RIGHT_DISPLAY:
+      HandleMoveWindowBetweenDisplays(accelerator);
+      break;
     case NEW_INCOGNITO_WINDOW:
       HandleNewIncognitoWindow();
       break;
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index c53d39b..f1fad7e 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -156,6 +156,16 @@
     {true, ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN, FOCUS_NEXT_PANE},
     {true, ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN, FOCUS_PREVIOUS_PANE},
 
+    // Window movement between displays shortcuts.
+    {true, ui::VKEY_UP, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
+     MOVE_WINDOW_TO_ABOVE_DISPLAY},
+    {true, ui::VKEY_DOWN, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
+     MOVE_WINDOW_TO_BELOW_DISPLAY},
+    {true, ui::VKEY_LEFT, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
+     MOVE_WINDOW_TO_LEFT_DISPLAY},
+    {true, ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN,
+     MOVE_WINDOW_TO_RIGHT_DISPLAY},
+
     // Media Player shortcuts.
     {true, ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE, MEDIA_NEXT_TRACK},
     {true, ui::VKEY_MEDIA_PLAY_PAUSE, ui::EF_NONE, MEDIA_PLAY_PAUSE},
diff --git a/ash/accelerators/accelerator_table.h b/ash/accelerators/accelerator_table.h
index d82b970..9fa6d638 100644
--- a/ash/accelerators/accelerator_table.h
+++ b/ash/accelerators/accelerator_table.h
@@ -95,6 +95,10 @@
   MEDIA_NEXT_TRACK,
   MEDIA_PLAY_PAUSE,
   MEDIA_PREV_TRACK,
+  MOVE_WINDOW_TO_ABOVE_DISPLAY,
+  MOVE_WINDOW_TO_BELOW_DISPLAY,
+  MOVE_WINDOW_TO_LEFT_DISPLAY,
+  MOVE_WINDOW_TO_RIGHT_DISPLAY,
   NEW_INCOGNITO_WINDOW,
   NEW_TAB,
   NEW_WINDOW,
diff --git a/ash/accelerators/accelerator_table_unittest.cc b/ash/accelerators/accelerator_table_unittest.cc
index 63f2a9b..f9f2a17 100644
--- a/ash/accelerators/accelerator_table_unittest.cc
+++ b/ash/accelerators/accelerator_table_unittest.cc
@@ -19,8 +19,10 @@
 constexpr int kNonSearchAcceleratorsNum = 93;
 // The hash of non-Search-based accelerators as of 2017-10-26.
 // See HashAcceleratorData().
+// TODO: adding Search-based accelerators should not update this hash
+// (crbug.com/778432).
 constexpr char kNonSearchAcceleratorsHash[] =
-    "f49b50d5990e0d2af99160176c6215f7";
+    "ccfca1c73d60f4837cef74800374a1c9";
 
 struct Cmp {
   bool operator()(const AcceleratorData& lhs, const AcceleratorData& rhs) {
diff --git a/ash/display/display_move_window_util.cc b/ash/display/display_move_window_util.cc
new file mode 100644
index 0000000..eae60ba
--- /dev/null
+++ b/ash/display/display_move_window_util.cc
@@ -0,0 +1,148 @@
+// 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 "ash/display/display_move_window_util.h"
+
+#include <stdint.h>
+#include <array>
+
+#include "ash/wm/window_util.h"
+#include "ui/aura/window.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/display/types/display_constants.h"
+
+namespace ash {
+
+namespace {
+
+// Calculate the vertical offset between two displays' center points.
+int GetVerticalOffset(const display::Display& display1,
+                      const display::Display& display2) {
+  DCHECK(display1.is_valid());
+  DCHECK(display2.is_valid());
+  return std::abs(display1.bounds().CenterPoint().y() -
+                  display2.bounds().CenterPoint().y());
+}
+
+// Calculate the horizontal offset between two displays' center points.
+int GetHorizontalOffset(const display::Display& display1,
+                        const display::Display& display2) {
+  DCHECK(display1.is_valid());
+  DCHECK(display2.is_valid());
+  return std::abs(display1.bounds().CenterPoint().x() -
+                  display2.bounds().CenterPoint().x());
+}
+
+// Returns true if |candidate_display| is not completely off the bounds of
+// |original_display| in the moving direction or cycled direction.
+bool IsWithinBounds(const display::Display& candidate_display,
+                    const display::Display& origin_display,
+                    DisplayMoveWindowDirection direction) {
+  const gfx::Rect& candidate_bounds = candidate_display.bounds();
+  const gfx::Rect& origin_bounds = origin_display.bounds();
+  if (direction == DisplayMoveWindowDirection::kLeft ||
+      direction == DisplayMoveWindowDirection::kRight) {
+    return GetHorizontalOffset(candidate_display, origin_display) <
+           (candidate_bounds.width() + origin_bounds.width()) / 2;
+  }
+
+  return GetVerticalOffset(candidate_display, origin_display) <
+         (candidate_bounds.height() + origin_bounds.height()) / 2;
+}
+
+// Gets the space between displays. The calculation is based on |direction|.
+// Non-negative value indicates |kInMovementDirection| and negative value
+// indicates |kInCycledDirection|.
+int GetSpaceBetweenDisplays(const display::Display& candidate_display,
+                            const display::Display& origin_display,
+                            DisplayMoveWindowDirection direction) {
+  switch (direction) {
+    case DisplayMoveWindowDirection::kAbove:
+      return origin_display.bounds().y() - candidate_display.bounds().bottom();
+    case DisplayMoveWindowDirection::kBelow:
+      return candidate_display.bounds().y() - origin_display.bounds().bottom();
+    case DisplayMoveWindowDirection::kLeft:
+      return origin_display.bounds().x() - candidate_display.bounds().right();
+    case DisplayMoveWindowDirection::kRight:
+      return candidate_display.bounds().x() - origin_display.bounds().right();
+  }
+  NOTREACHED();
+  return 0;
+}
+
+// Gets the orthogonal offset between displays, which is vertical offset for
+// left/right, horizontal offset for above/below.
+int GetOrthogonalOffsetBetweenDisplays(
+    const display::Display& candidate_display,
+    const display::Display& origin_display,
+    DisplayMoveWindowDirection direction) {
+  if (direction == DisplayMoveWindowDirection::kLeft ||
+      direction == DisplayMoveWindowDirection::kRight) {
+    return GetVerticalOffset(candidate_display, origin_display);
+  }
+  return GetHorizontalOffset(candidate_display, origin_display);
+}
+
+// Gets the display id of next display of |origin_display| on the moving
+// |direction|. Cycled direction result is returned if the moving |direction|
+// doesn't contain eligible candidate displays.
+int64_t GetNextDisplay(const display::Display& origin_display,
+                       DisplayMoveWindowDirection direction) {
+  // Saves the next display found in the specified movement direction.
+  display::Display next_movement_display;
+  // Saves the next display found in the cycled movement direction.
+  display::Display next_cycled_display;
+  for (const auto& candidate_display :
+       display::Screen::GetScreen()->GetAllDisplays()) {
+    if (candidate_display.id() == origin_display.id())
+      continue;
+    // Ignore the candidate display if |IsWithinBounds| returns true. This
+    // ensures that |candidate_space| calculated below represents the real
+    // layout apart distance.
+    if (IsWithinBounds(candidate_display, origin_display, direction))
+      continue;
+    const int candidate_space =
+        GetSpaceBetweenDisplays(candidate_display, origin_display, direction);
+    display::Display& next_display =
+        candidate_space >= 0 ? next_movement_display : next_cycled_display;
+
+    if (!next_display.is_valid()) {
+      next_display = candidate_display;
+      continue;
+    }
+
+    const int current_space =
+        GetSpaceBetweenDisplays(next_display, origin_display, direction);
+    if (candidate_space < current_space) {
+      next_display = candidate_display;
+    } else if (candidate_space == current_space) {
+      const int candidate_orthogonal_offset =
+          GetOrthogonalOffsetBetweenDisplays(candidate_display, origin_display,
+                                             direction);
+      const int current_orthogonal_offset = GetOrthogonalOffsetBetweenDisplays(
+          next_display, origin_display, direction);
+      if (candidate_orthogonal_offset < current_orthogonal_offset)
+        next_display = candidate_display;
+    }
+  }
+
+  return next_movement_display.is_valid() ? next_movement_display.id()
+                                          : next_cycled_display.id();
+}
+
+}  // namespace
+
+void HandleMoveWindowToDisplay(DisplayMoveWindowDirection direction) {
+  aura::Window* window = wm::GetActiveWindow();
+  if (!window)
+    return;
+
+  display::Display origin_display =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window);
+  int64_t dest_display_id = GetNextDisplay(origin_display, direction);
+  wm::MoveWindowToDisplay(window, dest_display_id);
+}
+
+}  // namespace ash
diff --git a/ash/display/display_move_window_util.h b/ash/display/display_move_window_util.h
new file mode 100644
index 0000000..4aa695be
--- /dev/null
+++ b/ash/display/display_move_window_util.h
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_DISPLAY_DISPLAY_MOVE_WINDOW_UTIL_H_
+#define ASH_DISPLAY_DISPLAY_MOVE_WINDOW_UTIL_H_
+
+#include "ash/ash_export.h"
+
+namespace ash {
+
+enum class DisplayMoveWindowDirection { kAbove, kBelow, kLeft, kRight };
+
+// Handles moving current active window from its display to another display
+// specified by |Direction|.
+ASH_EXPORT void HandleMoveWindowToDisplay(DisplayMoveWindowDirection direction);
+
+}  // namespace ash
+
+#endif  // ASH_DISPLAY_DISPLAY_MOVE_WINDOW_UTIL_H_
diff --git a/ash/display/display_move_window_util_unittest.cc b/ash/display/display_move_window_util_unittest.cc
new file mode 100644
index 0000000..a25dfef6
--- /dev/null
+++ b/ash/display/display_move_window_util_unittest.cc
@@ -0,0 +1,216 @@
+// 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 "ash/display/display_move_window_util.h"
+
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/window_util.h"
+#include "base/macros.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/display/display.h"
+#include "ui/display/display_layout.h"
+#include "ui/display/display_layout_builder.h"
+#include "ui/display/manager/display_layout_store.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/screen.h"
+#include "ui/display/test/display_manager_test_api.h"
+
+namespace ash {
+
+using DisplayMoveWindowUtilTest = AshTestBase;
+
+// Tests that window bounds are not changing after moving to another display. It
+// is added as a child window to another root window with the same origin.
+TEST_F(DisplayMoveWindowUtilTest, WindowBounds) {
+  display::Screen* screen = display::Screen::GetScreen();
+  int64_t primary_id = screen->GetPrimaryDisplay().id();
+  display::DisplayIdList list =
+      display::test::CreateDisplayIdList2(primary_id, primary_id + 1);
+  // Layout: [p][1]
+  display::DisplayLayoutBuilder builder(primary_id);
+  builder.AddDisplayPlacement(list[1], primary_id,
+                              display::DisplayPlacement::RIGHT, 0);
+  display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
+      list, builder.Build());
+  UpdateDisplay("400x300,400x300");
+  EXPECT_EQ(2U, display_manager()->GetNumDisplays());
+  EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
+            display_manager()->GetDisplayAt(0).bounds());
+  EXPECT_EQ(gfx::Rect(400, 0, 400, 300),
+            display_manager()->GetDisplayAt(1).bounds());
+
+  aura::Window* window =
+      CreateTestWindowInShellWithBounds(gfx::Rect(10, 20, 200, 100));
+  wm::ActivateWindow(window);
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kRight);
+  EXPECT_EQ(gfx::Rect(410, 20, 200, 100), window->GetBoundsInScreen());
+}
+
+// A horizontal layout for three displays. They are perfectly horizontally
+// aligned with no vertical offset.
+TEST_F(DisplayMoveWindowUtilTest, ThreeDisplaysHorizontalLayout) {
+  display::Screen* screen = display::Screen::GetScreen();
+  int64_t primary_id = screen->GetPrimaryDisplay().id();
+  display::DisplayIdList list = display::test::CreateDisplayIdListN(
+      3, primary_id, primary_id + 1, primary_id + 2);
+  // Layout: [p][1][2]
+  display::DisplayLayoutBuilder builder(primary_id);
+  builder.AddDisplayPlacement(list[1], primary_id,
+                              display::DisplayPlacement::RIGHT, 0);
+  builder.AddDisplayPlacement(list[2], list[1],
+                              display::DisplayPlacement::RIGHT, 0);
+  display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
+      list, builder.Build());
+  UpdateDisplay("400x300,400x300,400x300");
+  EXPECT_EQ(3U, display_manager()->GetNumDisplays());
+  EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
+            display_manager()->GetDisplayAt(0).bounds());
+  EXPECT_EQ(gfx::Rect(400, 0, 400, 300),
+            display_manager()->GetDisplayAt(1).bounds());
+  EXPECT_EQ(gfx::Rect(800, 0, 400, 300),
+            display_manager()->GetDisplayAt(2).bounds());
+
+  aura::Window* window =
+      CreateTestWindowInShellWithBounds(gfx::Rect(10, 20, 200, 100));
+  wm::ActivateWindow(window);
+  ASSERT_EQ(list[0], screen->GetDisplayNearestWindow(window).id());
+
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kLeft);
+  EXPECT_EQ(list[2], screen->GetDisplayNearestWindow(window).id());
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kLeft);
+  EXPECT_EQ(list[1], screen->GetDisplayNearestWindow(window).id());
+
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kRight);
+  EXPECT_EQ(list[2], screen->GetDisplayNearestWindow(window).id());
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kRight);
+  EXPECT_EQ(list[0], screen->GetDisplayNearestWindow(window).id());
+
+  // No-op for above/below movement since no display is in the above/below of
+  // primary display.
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kAbove);
+  EXPECT_EQ(list[0], screen->GetDisplayNearestWindow(window).id());
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kBelow);
+  EXPECT_EQ(list[0], screen->GetDisplayNearestWindow(window).id());
+}
+
+// A vertical layout for three displays. They are laid out with horizontal
+// offset.
+TEST_F(DisplayMoveWindowUtilTest, ThreeDisplaysVerticalLayoutWithOffset) {
+  display::Screen* screen = display::Screen::GetScreen();
+  int64_t primary_id = screen->GetPrimaryDisplay().id();
+  display::DisplayIdList list = display::test::CreateDisplayIdListN(
+      3, primary_id, primary_id + 1, primary_id + 2);
+  // Layout: [P]
+  //          [2]
+  //           [1]
+  // Note that display [1] is completely in the right of display [p].
+  display::DisplayLayoutBuilder builder(primary_id);
+  builder.AddDisplayPlacement(list[1], list[2],
+                              display::DisplayPlacement::BOTTOM, 200);
+  builder.AddDisplayPlacement(list[2], primary_id,
+                              display::DisplayPlacement::BOTTOM, 200);
+  display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
+      list, builder.Build());
+  UpdateDisplay("400x300,400x300,400x300");
+  EXPECT_EQ(3U, display_manager()->GetNumDisplays());
+  EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
+            display_manager()->GetDisplayAt(0).bounds());
+  EXPECT_EQ(gfx::Rect(200, 300, 400, 300),
+            display_manager()->GetDisplayAt(2).bounds());
+  EXPECT_EQ(gfx::Rect(400, 600, 400, 300),
+            display_manager()->GetDisplayAt(1).bounds());
+
+  aura::Window* window =
+      CreateTestWindowInShellWithBounds(gfx::Rect(10, 20, 200, 100));
+  wm::ActivateWindow(window);
+  ASSERT_EQ(list[0], screen->GetDisplayNearestWindow(window).id());
+
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kAbove);
+  EXPECT_EQ(list[1], screen->GetDisplayNearestWindow(window).id());
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kAbove);
+  EXPECT_EQ(list[2], screen->GetDisplayNearestWindow(window).id());
+
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kBelow);
+  EXPECT_EQ(list[1], screen->GetDisplayNearestWindow(window).id());
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kBelow);
+  EXPECT_EQ(list[0], screen->GetDisplayNearestWindow(window).id());
+
+  // Left/Right is able to be applied between display [p] and [1].
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kRight);
+  EXPECT_EQ(list[1], screen->GetDisplayNearestWindow(window).id());
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kLeft);
+  EXPECT_EQ(list[0], screen->GetDisplayNearestWindow(window).id());
+}
+
+// A more than three displays layout to verify the select closest strategy.
+TEST_F(DisplayMoveWindowUtilTest, SelectClosestCandidate) {
+  display::Screen* screen = display::Screen::GetScreen();
+  int64_t primary_id = screen->GetPrimaryDisplay().id();
+  // Layout:
+  // +--------+
+  // | 5      |----+
+  // +---+----+ 3  |
+  //     | 4  |--+----+----+
+  //     +----+  | P  | 1  |
+  //             +----+----+
+  //                  | 2  |
+  //                  +----+
+  display::DisplayIdList list = display::test::CreateDisplayIdListN(
+      6, primary_id, primary_id + 1, primary_id + 2, primary_id + 3,
+      primary_id + 4, primary_id + 5);
+  display::DisplayLayoutBuilder builder(primary_id);
+  builder.AddDisplayPlacement(list[1], primary_id,
+                              display::DisplayPlacement::RIGHT, 0);
+  builder.AddDisplayPlacement(list[2], list[1],
+                              display::DisplayPlacement::BOTTOM, 0);
+  builder.AddDisplayPlacement(list[3], primary_id,
+                              display::DisplayPlacement::TOP, -200);
+  builder.AddDisplayPlacement(list[4], list[3], display::DisplayPlacement::LEFT,
+                              200);
+  builder.AddDisplayPlacement(list[5], list[3], display::DisplayPlacement::LEFT,
+                              -100);
+  display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
+      list, builder.Build());
+  UpdateDisplay("400x300,400x300,400x300,400x300,400x300,800x300");
+  EXPECT_EQ(6U, display_manager()->GetNumDisplays());
+  EXPECT_EQ(gfx::Rect(0, 0, 400, 300),
+            display_manager()->GetDisplayAt(0).bounds());
+  EXPECT_EQ(gfx::Rect(400, 0, 400, 300),
+            display_manager()->GetDisplayAt(1).bounds());
+  EXPECT_EQ(gfx::Rect(400, 300, 400, 300),
+            display_manager()->GetDisplayAt(2).bounds());
+  EXPECT_EQ(gfx::Rect(-200, -300, 400, 300),
+            display_manager()->GetDisplayAt(3).bounds());
+  EXPECT_EQ(gfx::Rect(-600, -100, 400, 300),
+            display_manager()->GetDisplayAt(4).bounds());
+  EXPECT_EQ(gfx::Rect(-1000, -400, 800, 300),
+            display_manager()->GetDisplayAt(5).bounds());
+
+  aura::Window* window =
+      CreateTestWindowInShellWithBounds(gfx::Rect(10, 20, 200, 100));
+  wm::ActivateWindow(window);
+  ASSERT_EQ(list[0], screen->GetDisplayNearestWindow(window).id());
+
+  // Moving window to left display, we have [4] and [5] candidate displays in
+  // the left direction. They have the same left space but [4] is selected since
+  // it is vertically closer to display [p].
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kLeft);
+  EXPECT_EQ(list[4], screen->GetDisplayNearestWindow(window).id());
+
+  // Moving window to left display, we don't have candidate displays in the left
+  // direction. [1][2][3][p] are candidate displays in the cycled direction.
+  // [1][2] have the same left space but [1] is selected since it is vertically
+  // closer to display [4].
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kLeft);
+  EXPECT_EQ(list[1], screen->GetDisplayNearestWindow(window).id());
+
+  // Moving window to right display, we don't have candidate displays in the
+  // right direction. [p][3][4][5] are candidate displays in the cycled
+  // direction. [5] has the furthest right space so it is selected.
+  HandleMoveWindowToDisplay(DisplayMoveWindowDirection::kRight);
+  EXPECT_EQ(list[5], screen->GetDisplayNearestWindow(window).id());
+}
+
+}  // namespace ash
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsTabModelSelector.java b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsTabModelSelector.java
index 49df28d..b9cd81f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsTabModelSelector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsTabModelSelector.java
@@ -236,4 +236,12 @@
         mTabSaver.destroy();
         sInstance = null;
     }
+
+    /**
+     * Add a {@link TabPersistentStoreObserver} to {@link TabPersistentStore}.
+     * @param observer The observer to add.
+     */
+    public void addTabPersistentStoreObserver(TabPersistentStoreObserver observer) {
+        mTabSaver.addObserver(observer);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
index 58d50d3..40363fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorImpl.java
@@ -416,4 +416,12 @@
     public TabPersistentStore getTabPersistentStoreForTesting() {
         return mTabSaver;
     }
+
+    /**
+     * Add a {@link TabPersistentStoreObserver} to {@link TabPersistentStore}.
+     * @param observer The observer to add.
+     */
+    public void addTabPersistentStoreObserver(TabPersistentStoreObserver observer) {
+        mTabSaver.addObserver(observer);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index b8f21c4..9c6b38f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -19,6 +19,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.ObserverList;
 import org.chromium.base.StreamUtil;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
@@ -132,8 +133,9 @@
         /**
          * Called when the metadata file has been saved out asynchronously.
          * This currently does not get called when the metadata file is saved out on the UI thread.
+         * @param modelSelectorMetadata The saved metadata of current tab model selector.
          */
-        public void onMetadataSavedAsynchronously() {}
+        public void onMetadataSavedAsynchronously(TabModelSelectorMetadata modelSelectorMetadata) {}
     }
 
     /** Stores information about a TabModel. */
@@ -162,7 +164,7 @@
     private final TabPersistencePolicy mPersistencePolicy;
     private final TabModelSelector mTabModelSelector;
     private final TabCreatorManager mTabCreatorManager;
-    private TabPersistentStoreObserver mObserver;
+    private ObserverList<TabPersistentStoreObserver> mObservers;
 
     private final Deque<Tab> mTabsToSave;
     private final Deque<TabRestoreDetails> mTabsToRestore;
@@ -212,7 +214,8 @@
         mTabsToSave = new ArrayDeque<>();
         mTabsToRestore = new ArrayDeque<>();
         mTabIdsToRestore = new HashSet<>();
-        mObserver = observer;
+        mObservers = new ObserverList<>();
+        mObservers.addObserver(observer);
         mPreferences = ContextUtils.getAppSharedPreferences();
 
         mPrefetchTabListToMergeTasks = new ArrayList<>();
@@ -280,7 +283,7 @@
             // it looked when the SaveListTask was first created.
             if (mSaveListTask != null) mSaveListTask.cancel(true);
             try {
-                saveListToFile(serializeTabMetadata());
+                saveListToFile(serializeTabMetadata().listData);
             } catch (IOException e) {
                 Log.w(TAG, "Error while saving tabs state; will attempt to continue...", e);
             }
@@ -414,7 +417,9 @@
         }
 
         mPersistencePolicy.notifyStateLoaded(mTabsToRestore.size());
-        if (mObserver != null) mObserver.onInitialized(mTabsToRestore.size());
+        for (TabPersistentStoreObserver observer : mObservers) {
+            observer.onInitialized(mTabsToRestore.size());
+        }
     }
 
     /**
@@ -795,7 +800,7 @@
         // done as part of the standard tab removal process.
     }
 
-    private byte[] serializeTabMetadata() throws IOException {
+    private TabModelSelectorMetadata serializeTabMetadata() throws IOException {
         List<TabRestoreDetails> tabsToRestore = new ArrayList<>();
 
         // The metadata file may be being written out before all of the Tabs have been restored.
@@ -813,10 +818,11 @@
      * and selected indices.
      * @param selector          The {@link TabModelSelector} to serialize.
      * @param tabsBeingRestored Tabs that are in the process of being restored.
-     * @return                  {@code byte[]} containing the serialized state of {@code selector}.
+     * @return                  {@link TabModelSelectorMetadata} containing the meta data and
+     * serialized state of {@code selector}.
      */
     @VisibleForTesting
-    public static byte[] serializeTabModelSelector(TabModelSelector selector,
+    public static TabModelSelectorMetadata serializeTabModelSelector(TabModelSelector selector,
             List<TabRestoreDetails> tabsBeingRestored) throws IOException {
         ThreadUtils.assertOnUiThread();
 
@@ -844,7 +850,8 @@
         ContextUtils.getAppSharedPreferences().edit().putInt(
                 PREF_ACTIVE_TAB_ID, activeTabId).apply();
 
-        return serializeMetadata(normalInfo, incognitoInfo, tabsBeingRestored);
+        byte[] listData = serializeMetadata(normalInfo, incognitoInfo, tabsBeingRestored);
+        return new TabModelSelectorMetadata(listData, normalInfo, incognitoInfo, tabsBeingRestored);
     }
 
     /**
@@ -975,8 +982,8 @@
                     mTabsToRestore.addLast(details);
                 }
 
-                if (mObserver != null) {
-                    mObserver.onDetailsRead(
+                for (TabPersistentStoreObserver observer : mObservers) {
+                    observer.onDetailsRead(
                             index, id, url, isStandardActiveIndex, isIncognitoActiveIndex);
                 }
             }
@@ -1155,43 +1162,65 @@
         }
     }
 
+    /** Stores meta data about the TabModelSelector which has been serialized to disk. */
+    public static class TabModelSelectorMetadata {
+        public final byte[] listData;
+        public final TabModelMetadata normalModelMetadata;
+        public final TabModelMetadata incognitoModelMetadata;
+        public final List<TabRestoreDetails> tabsBeingRestored;
+
+        public TabModelSelectorMetadata(byte[] listData, TabModelMetadata normalModelMetadata,
+                TabModelMetadata incognitoModelMetadata,
+                List<TabRestoreDetails> tabsBeingRestored) {
+            this.listData = listData;
+            this.normalModelMetadata = normalModelMetadata;
+            this.incognitoModelMetadata = incognitoModelMetadata;
+            this.tabsBeingRestored = tabsBeingRestored;
+        }
+    }
+
     private class SaveListTask extends AsyncTask<Void, Void, Void> {
-        byte[] mListData;
+        TabModelSelectorMetadata mMetadata;
 
         @Override
         protected void onPreExecute() {
             if (mDestroyed || isCancelled()) return;
             try {
-                mListData = serializeTabMetadata();
+                mMetadata = serializeTabMetadata();
             } catch (IOException e) {
-                mListData = null;
+                mMetadata = null;
             }
         }
 
         @Override
         protected Void doInBackground(Void... voids) {
-            if (mListData == null || isCancelled()) return null;
-            saveListToFile(mListData);
-            mListData = null;
+            if (mMetadata == null || isCancelled()) return null;
+            saveListToFile(mMetadata.listData);
             return null;
         }
 
         @Override
         protected void onPostExecute(Void v) {
-            if (mDestroyed || isCancelled()) return;
+            if (mDestroyed || isCancelled()) {
+                mMetadata = null;
+                return;
+            }
 
             if (mSaveListTask == this) {
                 mSaveListTask = null;
-                if (mObserver != null) mObserver.onMetadataSavedAsynchronously();
+                for (TabPersistentStoreObserver observer : mObservers) {
+                    observer.onMetadataSavedAsynchronously(mMetadata);
+                }
+                mMetadata = null;
             }
         }
     }
 
     private void onStateLoaded() {
-        if (mObserver != null) {
+        for (TabPersistentStoreObserver observer : mObservers) {
             // mergeState() starts an AsyncTask to call this and this calls
             // onTabStateInitialized which should be called from the UI thread.
-            ThreadUtils.runOnUiThread( () -> mObserver.onStateLoaded() );
+            ThreadUtils.runOnUiThread(() -> observer.onStateLoaded());
         }
     }
 
@@ -1228,7 +1257,7 @@
                 for (String mergedFileName : new HashSet<String>(mMergedFileNames)) {
                     deleteFileAsync(mergedFileName);
                 }
-                if (mObserver != null) mObserver.onStateMerged();
+                for (TabPersistentStoreObserver observer : mObservers) observer.onStateMerged();
             }
 
             cleanUpPersistentData();
@@ -1410,8 +1439,16 @@
     }
 
     @VisibleForTesting
-    public void setObserverForTesting(TabPersistentStoreObserver observer) {
-        mObserver = observer;
+    public void addObserver(TabPersistentStoreObserver observer) {
+        mObservers.addObserver(observer);
+    }
+
+    /**
+     * Removes a {@link TabPersistentStoreObserver}.
+     * @param observer The {@link TabPersistentStoreObserver} to remove.
+     */
+    public void removeObserver(TabPersistentStoreObserver observer) {
+        mObservers.removeObserver(observer);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 95f111c..00a8625 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -260,6 +260,12 @@
         // TODO(yusufo): Consider not initializing these for WebAPKs.
         mBrowserSessionDataProvider = new BrowserSessionDataProvider(intent);
         mTrustedWebContentProvider = new TrustedWebContentProvider();
+
+        // When turning on TalkBack on Android, hitting app switcher to bring a WebappActivity to
+        // front will speak "Web App", which is the label of WebappActivity. Therefore, we set title
+        // of the WebappActivity explicitly to make it speak the short name of the Web App.
+        setTitle(mWebappInfo.shortName());
+
         super.preInflationStartup();
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
index ce0649ed..c4e8628 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java
@@ -279,7 +279,7 @@
             public byte[] call() throws Exception {
                 TabModelSelectorImpl selectorImpl =
                         buildTestTabModelSelector(new int[] {111, 222, 333}, null);
-                return TabPersistentStore.serializeTabModelSelector(selectorImpl, null);
+                return TabPersistentStore.serializeTabModelSelector(selectorImpl, null).listData;
             }
         });
         FileOutputStream fos = null;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/RestoreMigrateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/RestoreMigrateTest.java
index 53eef43..a1c9e8e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/RestoreMigrateTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/RestoreMigrateTest.java
@@ -49,7 +49,8 @@
                 new Callable<byte[]>() {
                     @Override
                     public byte[] call() throws Exception {
-                        return TabPersistentStore.serializeTabModelSelector(selector, null);
+                        return TabPersistentStore.serializeTabModelSelector(selector, null)
+                                .listData;
                     }
                 });
         File f = TabbedModeTabPersistencePolicy.getOrCreateTabbedModeStateDirectory();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java
index 7cbf6d6b..1ead798 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabModelMergingTest.java
@@ -322,7 +322,7 @@
         MockTabPersistentStoreObserver mockObserver = new MockTabPersistentStoreObserver();
         TabModelSelectorImpl tabModelSelector =
                 (TabModelSelectorImpl) mActivity2.getTabModelSelector();
-        tabModelSelector.getTabPersistentStoreForTesting().setObserverForTesting(mockObserver);
+        tabModelSelector.getTabPersistentStoreForTesting().addObserver(mockObserver);
 
         // Merge tabs into ChromeTabbedActivity2. Wait for the merge to finish, ensuring the
         // tab metadata file for ChromeTabbedActivity gets deleted before attempting to merge
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
index 65ec5db..4d6a39e6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreTest.java
@@ -37,6 +37,7 @@
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
+import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabModelSelectorMetadata;
 import org.chromium.chrome.browser.tabmodel.TabPersistentStore.TabPersistentStoreObserver;
 import org.chromium.chrome.browser.tabmodel.TestTabModelDirectory.TabModelMetaDataInfo;
 import org.chromium.chrome.browser.test.ChromeBrowserTestRule;
@@ -259,7 +260,7 @@
         }
 
         @Override
-        public void onMetadataSavedAsynchronously() {
+        public void onMetadataSavedAsynchronously(TabModelSelectorMetadata metadata) {
             listWrittenCallback.notifyCalled();
         }
     }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 6b9fdc5..9994a99 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -3379,6 +3379,18 @@
   <message name="IDS_KEYBOARD_OVERLAY_BRIGHT_UP_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'bright up' key that turns the screen's brightness up.">
     bright up
   </message>
+  <message name="IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_ABOVE_DISPLAY" desc="The text in the keyboard overlay to explain the shortcut (move active window to above display).">
+    Window up
+  </message>
+  <message name="IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_BELOW_DISPLAY" desc="The text in the keyboard overlay to explain the shortcut (move active window to below display).">
+    Window down
+  </message>
+  <message name="IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_LEFT_DISPLAY" desc="The text in the keyboard overlay to explain the shortcut (move active window to left display).">
+    Window left
+  </message>
+  <message name="IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_RIGHT_DISPLAY" desc="The text in the keyboard overlay to explain the shortcut (move active window to right display).">
+    Window right
+  </message>
   <message name="IDS_KEYBOARD_OVERLAY_MUTE_KEY_LABEL" desc="The text in the keyboard overlay to show the label of the 'mute' key that mutes the system's sound.">
     mute
   </message>
diff --git a/chrome/browser/chromeos/events/event_rewriter_unittest.cc b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
index e2e53566..3b298e5 100644
--- a/chrome/browser/chromeos/events/event_rewriter_unittest.cc
+++ b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
@@ -1365,19 +1365,7 @@
         ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::DomKey::ARROW_DOWN},
        {ui::VKEY_END, ui::DomCode::END, ui::EF_NONE, ui::DomKey::END}},
 
-      // Search+Alt+Up -> Alt+Up
-      {ui::ET_KEY_PRESSED,
-       {ui::VKEY_UP, ui::DomCode::ARROW_UP,
-        ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ARROW_UP},
-       {ui::VKEY_UP, ui::DomCode::ARROW_UP, ui::EF_ALT_DOWN,
-        ui::DomKey::ARROW_UP}},
-      // Search+Alt+Down -> Alt+Down
-      {ui::ET_KEY_PRESSED,
-       {ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN,
-        ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN, ui::DomKey::ARROW_DOWN},
-       {ui::VKEY_DOWN, ui::DomCode::ARROW_DOWN, ui::EF_ALT_DOWN,
-        ui::DomKey::ARROW_DOWN}},
-      // Search+Ctrl+Alt+Up -> Search+Ctrl+Alt+Up
+      // Search+Ctrl+Alt+Up -> Ctrl+Alt+Up
       {ui::ET_KEY_PRESSED,
        {ui::VKEY_UP, ui::DomCode::ARROW_UP,
         ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
diff --git a/chrome/browser/previews/previews_browsertest.cc b/chrome/browser/previews/previews_browsertest.cc
index ce4bbe9..afda20443 100644
--- a/chrome/browser/previews/previews_browsertest.cc
+++ b/chrome/browser/previews/previews_browsertest.cc
@@ -55,7 +55,6 @@
     }
   }
 
-  base::test::ScopedFeatureList scoped_feature_list_;
   net::EmbeddedTestServer https_server_;
   GURL test_url_;
   bool noscript_css_requested_;
@@ -76,14 +75,15 @@
 // This test class enables NoScriptPreviews via command line addition.
 class PreviewsNoScriptBrowserTest : public PreviewsBrowserTest {
  public:
-  PreviewsNoScriptBrowserTest() {}
+  PreviewsNoScriptBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        previews::features::kNoScriptPreviews);
+  }
 
   ~PreviewsNoScriptBrowserTest() override {}
 
-  void SetUpCommandLine(base::CommandLine* cmd) override {
-    PreviewsBrowserTest::SetUpCommandLine(cmd);
-    cmd->AppendSwitchASCII("enable-features", "NoScriptPreviews");
-  }
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Previews InfoBar (which this test triggers) does not work on Mac.
diff --git a/chrome/browser/resources/chromeos/keyboard_overlay_data.js b/chrome/browser/resources/chromeos/keyboard_overlay_data.js
index d064083e..98afa85 100644
--- a/chrome/browser/resources/chromeos/keyboard_overlay_data.js
+++ b/chrome/browser/resources/chromeos/keyboard_overlay_data.js
@@ -4180,6 +4180,7 @@
     'd<>CTRL': 'keyboardOverlayBookmarkCurrentPage',
     'd<>CTRL<>SHIFT': 'keyboardOverlayBookmarkAllTabs',
     'down<>ALT': 'keyboardOverlayPageDown',
+    'down<>ALT<>SEARCH': 'keyboardOverlayMoveWindowToBelowDisplay',
     'down<>ALT<>CTRL': 'keyboardOverlayEnd',
     'down<>SEARCH': 'keyboardOverlayPageDown',
     'e<>ALT': 'keyboardOverlayShowWrenchMenu',
@@ -4236,6 +4237,7 @@
     'l<>SEARCH': 'keyboardOverlayLockScreen',
     'l<>SEARCH<>SHIFT': 'keyboardOverlaySuspend',
     'left<>ALT': 'keyboardOverlayGoBack',
+    'left<>ALT<>SEARCH': 'keyboardOverlayMoveWindowToLeftDisplay',
     'left<>CTRL': 'keyboardOverlayPreviousWord',
     'left<>CTRL<>SHIFT': 'keyboardOverlaySelectWordAtATime',
     'left<>SEARCH': 'keyboardOverlayHome',
@@ -4257,6 +4259,7 @@
     'reload<>CTRL<>SHIFT': 'keyboardOverlayRotateScreen',
     'reload<>SEARCH': 'keyboardOverlayF3',
     'right<>ALT': 'keyboardOverlayGoForward',
+    'right<>ALT<>SEARCH': 'keyboardOverlayMoveWindowToRightDisplay',
     'right<>CTRL': 'keyboardOverlayNextWord',
     'right<>CTRL<>SHIFT': 'keyboardOverlaySelectWordAtATime',
     'right<>SEARCH': 'keyboardOverlayEnd',
@@ -4280,6 +4283,7 @@
     'u<>CTRL': 'keyboardOverlayViewSource',
     'u<>CTRL<>SHIFT': 'keyboardOverlayInputUnicodeCharacters',
     'up<>ALT': 'keyboardOverlayPageUp',
+    'up<>ALT<>SEARCH': 'keyboardOverlayMoveWindowToAboveDisplay',
     'up<>ALT<>CTRL': 'keyboardOverlayHome',
     'up<>SEARCH': 'keyboardOverlayPageUp',
     'v<>CTRL': 'keyboardOverlayPaste',
diff --git a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
index 762336e..5ee538c 100644
--- a/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/keyboard_overlay_ui.cc
@@ -208,6 +208,16 @@
     {"keyboardOverlayMaximizeWindow", IDS_KEYBOARD_OVERLAY_MAXIMIZE_WINDOW},
     {"keyboardOverlayMinimizeWindow", IDS_KEYBOARD_OVERLAY_MINIMIZE_WINDOW},
     {"keyboardOverlayMirrorMonitors", IDS_KEYBOARD_OVERLAY_MIRROR_MONITORS},
+    // TODO(warx): keyboard overlay name for move window between displays
+    // shortcuts need to be updated when new keyboard shortcuts helper is there.
+    {"keyboardOverlayMoveWindowToAboveDisplay",
+     IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_ABOVE_DISPLAY},
+    {"keyboardOverlayMoveWindowToBelowDisplay",
+     IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_BELOW_DISPLAY},
+    {"keyboardOverlayMoveWindowToLeftDisplay",
+     IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_LEFT_DISPLAY},
+    {"keyboardOverlayMoveWindowToRightDisplay",
+     IDS_KEYBOARD_OVERLAY_MOVE_WINDOW_TO_RIGHT_DISPLAY},
     {"keyboardOverlayNewIncognitoWindow",
      IDS_KEYBOARD_OVERLAY_NEW_INCOGNITO_WINDOW},
     {"keyboardOverlayNewTab", IDS_KEYBOARD_OVERLAY_NEW_TAB},
diff --git a/components/autofill/ios/browser/autofill_client_ios.h b/components/autofill/ios/browser/autofill_client_ios.h
index 30758ec..02da756f 100644
--- a/components/autofill/ios/browser/autofill_client_ios.h
+++ b/components/autofill/ios/browser/autofill_client_ios.h
@@ -91,6 +91,9 @@
   bool IsAutofillSupported() override;
   void ExecuteCommand(int id) override;
 
+ protected:
+  web::WebState* web_state() { return web_state_; }
+
  private:
   PrefService* pref_service_;
   PersonalDataManager* personal_data_manager_;
diff --git a/components/autofill/ios/browser/autofill_client_ios.mm b/components/autofill/ios/browser/autofill_client_ios.mm
index c6a030f..5803427 100644
--- a/components/autofill/ios/browser/autofill_client_ios.mm
+++ b/components/autofill/ios/browser/autofill_client_ios.mm
@@ -142,9 +142,7 @@
 }
 
 void AutofillClientIOS::DidInteractWithNonsecureCreditCardInput(
-    content::RenderFrameHost* rfh) {
-  web_state_->OnCreditCardInputShownOnHttp();
-}
+    content::RenderFrameHost* rfh) {}
 
 bool AutofillClientIOS::IsContextSecure() {
   // This implementation differs slightly from other platforms. Other platforms'
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index d7fb2db..54f6012 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -84,8 +84,10 @@
 }
 
 void AutofillDriverIOS::DidInteractWithCreditCardForm() {
-  if (!web::IsOriginSecure(web_state_->GetLastCommittedURL()))
-    web_state_->OnCreditCardInputShownOnHttp();
+  if (!web::IsOriginSecure(web_state_->GetLastCommittedURL())) {
+    autofill_manager_.client()->DidInteractWithNonsecureCreditCardInput(
+        nullptr);
+  }
 }
 
 void AutofillDriverIOS::RendererShouldClearFilledForm() {
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index 99effbf..decf9a18 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -356,10 +356,13 @@
     observer.OnWillChangeBookmarkNode(this, node);
 
   // The title index doesn't support changing the title, instead we remove then
-  // add it back.
-  index_->Remove(node);
+  // add it back. Only do this for URL nodes. A directory node can have its
+  // title changed but should be excluded from the index.
+  if (node->is_url())
+    index_->Remove(node);
   AsMutable(node)->SetTitle(title);
-  index_->Add(node);
+  if (node->is_url())
+    index_->Add(node);
 
   if (store_.get())
     store_->ScheduleSave();
diff --git a/components/bookmarks/browser/bookmark_model_unittest.cc b/components/bookmarks/browser/bookmark_model_unittest.cc
index a3b70780..bb5bd40 100644
--- a/components/bookmarks/browser/bookmark_model_unittest.cc
+++ b/components/bookmarks/browser/bookmark_model_unittest.cc
@@ -25,6 +25,7 @@
 #include "components/bookmarks/browser/bookmark_model_observer.h"
 #include "components/bookmarks/browser/bookmark_undo_delegate.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
+#include "components/bookmarks/browser/titled_url_match.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/bookmarks/test/test_bookmark_client.h"
 #include "components/favicon_base/favicon_callback.h"
@@ -1200,6 +1201,24 @@
   EXPECT_EQ(user, model_->GetMostRecentlyAddedUserNodeForURL(url));
 }
 
+// Verifies that renaming a bookmark folder does not add the folder node to the
+// autocomplete index. crbug.com/778266
+TEST_F(BookmarkModelTest, RenamedFolderNodeExcludedFromIndex) {
+  // Add a folder.
+  const BookmarkNode* folder =
+      model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("MyFavorites"));
+
+  // Change the folder title.
+  model_->SetTitle(folder, ASCIIToUTF16("MyBookmarks"));
+
+  // There should be no matching bookmarks.
+  std::vector<TitledUrlMatch> matches;
+  model_->GetBookmarksMatching(ASCIIToUTF16("MyB"), /*max_count = */ 1,
+                               query_parser::MatchingAlgorithm::DEFAULT,
+                               &matches);
+  EXPECT_TRUE(matches.empty());
+}
+
 TEST(BookmarkNodeTest, NodeMetaInfo) {
   GURL url;
   BookmarkNode node(url);
diff --git a/components/security_state/ios/BUILD.gn b/components/security_state/ios/BUILD.gn
new file mode 100644
index 0000000..2e052ef
--- /dev/null
+++ b/components/security_state/ios/BUILD.gn
@@ -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.
+
+source_set("ios") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "ssl_status_input_event_data.h",
+    "ssl_status_input_event_data.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/security_state/core/",
+    "//ios/web/public/",
+  ]
+}
diff --git a/components/security_state/ios/DEPS b/components/security_state/ios/DEPS
new file mode 100644
index 0000000..0eca071
--- /dev/null
+++ b/components/security_state/ios/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+components/security_state/core",
+  "+ios/web/public",
+]
diff --git a/components/security_state/ios/ssl_status_input_event_data.h b/components/security_state/ios/ssl_status_input_event_data.h
new file mode 100644
index 0000000..f21113d
--- /dev/null
+++ b/components/security_state/ios/ssl_status_input_event_data.h
@@ -0,0 +1,40 @@
+// 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 COMPONENTS_SECURITY_STATE_IOS_SSL_STATUS_INPUT_EVENT_DATA_H_
+#define COMPONENTS_SECURITY_STATE_IOS_SSL_STATUS_INPUT_EVENT_DATA_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "components/security_state/core/insecure_input_event_data.h"
+#include "ios/web/public/ssl_status.h"
+
+namespace security_state {
+
+using web::SSLStatus;
+
+// Stores flags about Input Events in the SSLStatus UserData to
+// influence the Security Level of the page.
+class SSLStatusInputEventData : public SSLStatus::UserData {
+ public:
+  SSLStatusInputEventData();
+  explicit SSLStatusInputEventData(
+      const security_state::InsecureInputEventData& initial_data);
+  ~SSLStatusInputEventData() override;
+
+  security_state::InsecureInputEventData* input_events();
+
+  // SSLStatus::UserData:
+  std::unique_ptr<SSLStatus::UserData> Clone() override;
+
+ private:
+  security_state::InsecureInputEventData data_;
+
+  DISALLOW_COPY_AND_ASSIGN(SSLStatusInputEventData);
+};
+
+}  // namespace security_state
+
+#endif  // COMPONENTS_SECURITY_STATE_IOS_SSL_STATUS_INPUT_EVENT_DATA_H_
diff --git a/components/security_state/ios/ssl_status_input_event_data.mm b/components/security_state/ios/ssl_status_input_event_data.mm
new file mode 100644
index 0000000..3b84a2f
--- /dev/null
+++ b/components/security_state/ios/ssl_status_input_event_data.mm
@@ -0,0 +1,26 @@
+// 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 "components/security_state/ios/ssl_status_input_event_data.h"
+
+namespace security_state {
+
+SSLStatusInputEventData::SSLStatusInputEventData() {}
+
+SSLStatusInputEventData::SSLStatusInputEventData(
+    const security_state::InsecureInputEventData& initial_data)
+    : data_(initial_data) {}
+
+SSLStatusInputEventData::~SSLStatusInputEventData() {}
+
+security_state::InsecureInputEventData*
+SSLStatusInputEventData::input_events() {
+  return &data_;
+}
+
+std::unique_ptr<web::SSLStatus::UserData> SSLStatusInputEventData::Clone() {
+  return base::MakeUnique<SSLStatusInputEventData>(data_);
+}
+
+}  // namespace security_state
diff --git a/components/translate/OWNERS b/components/translate/OWNERS
index 3cf2d4d1..b481dc685 100644
--- a/components/translate/OWNERS
+++ b/components/translate/OWNERS
@@ -2,11 +2,12 @@
 #   Android, CLD and language detection: andrewhayden@chromium.org
 #   Componentization and iOS: droger@chromium.org
 #   "Bubble" UI and translate internals: groby@chromium.org
-#                                        hajimehoshi@chromium.org 
+#                                        hajimehoshi@chromium.org
 #   CSP and isolated worlds: toyoshim@chromium.org
 andrewhayden@chromium.org
 droger@chromium.org
 groby@chromium.org
 hajimehoshi@chromium.org
+martis@chromium.org
 napper@chromium.org
 toyoshim@chromium.org
diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg
index e879c69..7a479a0 100644
--- a/infra/config/cq.cfg
+++ b/infra/config/cq.cfg
@@ -39,14 +39,14 @@
       # on master.tryserver.chromium.linux with these.
       name: "master.tryserver.chromium.chromiumos"
       builders { name: "chromeos-amd64-generic-rel" }
-      #builders {
-      #  name: "chromeos-daisy-rel"
-      #  experiment_percentage: 3
-      #}
-      #builders {
-      #  name: "linux-chromeos-rel"
-      #  experiment_percentage: 3
-      #}
+      builders {
+        name: "chromeos-daisy-rel"
+        experiment_percentage: 50
+      }
+      builders {
+        name: "linux-chromeos-rel"
+        experiment_percentage: 50
+      }
     }
     buckets {
       name: "master.tryserver.chromium.linux"
diff --git a/ios/chrome/browser/autofill/BUILD.gn b/ios/chrome/browser/autofill/BUILD.gn
index 4b1869af..379313f3 100644
--- a/ios/chrome/browser/autofill/BUILD.gn
+++ b/ios/chrome/browser/autofill/BUILD.gn
@@ -119,9 +119,11 @@
     "//components/autofill/ios/browser",
     "//components/infobars/core",
     "//components/keyed_service/core",
+    "//components/security_state/ios",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/infobars",
+    "//ios/chrome/browser/ssl",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/autofill",
     "//ios/chrome/browser/ui/settings:test_support",
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index 33105b22..4f3640e 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -23,10 +23,12 @@
 #include "components/infobars/core/infobar.h"
 #include "components/infobars/core/infobar_manager.h"
 #include "components/keyed_service/core/service_access_type.h"
+#include "components/security_state/ios/ssl_status_input_event_data.h"
 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
 #import "ios/chrome/browser/autofill/form_suggestion_controller.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
+#include "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
 #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h"
 #include "ios/chrome/browser/ui/settings/personal_data_manager_data_changed_observer.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
@@ -120,7 +122,7 @@
 
 // An HTML page without a card-type form.
 static NSString* kNoCreditCardFormHtml =
-    @"<h2>The rain in Spain stays <i>mainly</i> in the plain.</h2>";
+    @"<input type=\"text\" autofocus autocomplete=\"username\"></form>";
 
 // A credit card-type form with the autofocus attribute (which is detected at
 // page load).
@@ -607,48 +609,63 @@
 };
 
 // Checks that an HTTP page containing a credit card results in a navigation
-// entry with the "credit card displayed" bit set to true.
-// TODO(crbug.com/689082): disabled due to flakyness.
-TEST_F(AutofillControllerTest, DISABLED_HttpCreditCard) {
+// entry with the "credit card interaction" bit set to true.
+TEST_F(AutofillControllerTest, HttpCreditCard) {
   LoadHtml(kCreditCardAutofocusFormHtml, GURL("http://chromium.test"));
+  WaitForSuggestionRetrieval();
 
   web::SSLStatus ssl_status =
       web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL();
-  EXPECT_TRUE(ssl_status.content_status &
-              web::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP);
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl_status.user_data.get());
+  EXPECT_TRUE(input_events &&
+              input_events->input_events()->credit_card_field_edited);
 };
 
 // Checks that an HTTP page without a credit card form does not result in a
-// navigation entry with the "credit card displayed" bit set to true.
+// navigation entry with the "credit card interaction" bit set to true.
 TEST_F(AutofillControllerTest, HttpNoCreditCard) {
   LoadHtml(kNoCreditCardFormHtml, GURL("http://chromium.test"));
+  WaitForSuggestionRetrieval();
 
   web::SSLStatus ssl_status =
       web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL();
-  EXPECT_FALSE(ssl_status.content_status &
-               web::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP);
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl_status.user_data.get());
+  EXPECT_FALSE(input_events &&
+               input_events->input_events()->credit_card_field_edited);
 };
 
 // Checks that an HTTPS page containing a credit card form does not result in a
-// navigation entry with the "credit card displayed" bit set to true.
+// navigation entry with the "credit card interaction" bit set to true.
 TEST_F(AutofillControllerTest, HttpsCreditCard) {
   LoadHtml(kCreditCardAutofocusFormHtml, GURL("https://chromium.test"));
+  WaitForSuggestionRetrieval();
 
   web::SSLStatus ssl_status =
       web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL();
-  EXPECT_FALSE(ssl_status.content_status &
-               web::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP);
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl_status.user_data.get());
+  EXPECT_FALSE(input_events &&
+               input_events->input_events()->credit_card_field_edited);
 };
 
 // Checks that an HTTPS page without a credit card form does not result in a
-// navigation entry with the "credit card displayed" bit set to true.
+// navigation entry with the "credit card interaction" bit set to true.
 TEST_F(AutofillControllerTest, HttpsNoCreditCard) {
   LoadHtml(kNoCreditCardFormHtml, GURL("https://chromium.test"));
+  WaitForSuggestionRetrieval();
 
   web::SSLStatus ssl_status =
       web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL();
-  EXPECT_FALSE(ssl_status.content_status &
-               web::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP);
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl_status.user_data.get());
+  EXPECT_FALSE(input_events &&
+               input_events->input_events()->credit_card_field_edited);
 };
 
 }  // namespace
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 4da33a7..4bedc8a 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -131,6 +131,7 @@
     "//components/password_manager/core/common",
     "//components/prefs",
     "//components/prefs:test_support",
+    "//components/security_state/ios",
     "//google_apis",
     "//ios/chrome/browser/autofill",
     "//ios/chrome/browser/browser_state:test_support",
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index 6cd92ed..72f845f 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -41,6 +41,7 @@
 #import "ios/chrome/browser/passwords/js_password_manager.h"
 #import "ios/chrome/browser/passwords/notify_auto_signin_view_controller.h"
 #import "ios/chrome/browser/passwords/password_form_filler.h"
+#import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #include "ios/chrome/browser/web/tab_id_tab_helper.h"
@@ -632,7 +633,8 @@
     if (webStateObserverBridge_) {
       web::WebState* web_state = webStateObserverBridge_->web_state();
       if (web_state && !web::IsOriginSecure(web_state->GetLastCommittedURL())) {
-        web_state->OnPasswordInputShownOnHttp();
+        InsecureInputTabHelper::GetOrCreateForWebState(web_state)
+            ->DidShowPasswordFieldInInsecureContext();
       }
     }
 
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index f3d3718..772f8174 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -25,11 +25,13 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/security_state/ios/ssl_status_input_event_data.h"
 #import "ios/chrome/browser/autofill/form_input_accessory_view_controller.h"
 #import "ios/chrome/browser/autofill/form_suggestion_controller.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/passwords/js_password_manager.h"
 #import "ios/chrome/browser/passwords/password_form_filler.h"
+#import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
 #import "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/ssl_status.h"
@@ -1331,47 +1333,59 @@
 }
 
 // Tests that an HTTP page without a password field does not update the SSL
-// status to indicate DISPLAYED_PASSWORD_FIELD_ON_HTTP.
+// status to indicate |password_field_shown|.
 TEST_F(PasswordControllerTest, HTTPNoPassword) {
   LoadHtml(kHtmlWithoutPasswordForm, GURL("http://chromium.test"));
 
   web::SSLStatus ssl_status =
       web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL();
-  EXPECT_FALSE(ssl_status.content_status &
-               web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl_status.user_data.get());
+  EXPECT_FALSE(input_events &&
+               input_events->input_events()->password_field_shown);
 }
 
 // Tests that an HTTP page with a password field updates the SSL status
-// to indicate DISPLAYED_PASSWORD_FIELD_ON_HTTP.
+// to indicate |password_field_shown|.
 TEST_F(PasswordControllerTest, HTTPPassword) {
   LoadHtml(kHtmlWithPasswordForm, GURL("http://chromium.test"));
 
   web::SSLStatus ssl_status =
       web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL();
-  EXPECT_TRUE(ssl_status.content_status &
-              web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl_status.user_data.get());
+  ASSERT_TRUE(input_events);
+  EXPECT_TRUE(input_events->input_events()->password_field_shown);
 }
 
 // Tests that an HTTPS page without a password field does not update the SSL
-// status to indicate DISPLAYED_PASSWORD_FIELD_ON_HTTP.
+// status to indicate |password_field_shown|.
 TEST_F(PasswordControllerTest, HTTPSNoPassword) {
   LoadHtml(kHtmlWithoutPasswordForm, GURL("https://chromium.test"));
 
   web::SSLStatus ssl_status =
       web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL();
-  EXPECT_FALSE(ssl_status.content_status &
-               web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl_status.user_data.get());
+  EXPECT_FALSE(input_events &&
+               input_events->input_events()->password_field_shown);
 }
 
 // Tests that an HTTPS page with a password field does not update the SSL status
-// to indicate DISPLAYED_PASSWORD_FIELD_ON_HTTP.
+// to indicate |password_field_shown|.
 TEST_F(PasswordControllerTest, HTTPSPassword) {
   LoadHtml(kHtmlWithPasswordForm, GURL("https://chromium.test"));
 
   web::SSLStatus ssl_status =
       web_state()->GetNavigationManager()->GetLastCommittedItem()->GetSSL();
-  EXPECT_FALSE(ssl_status.content_status &
-               web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl_status.user_data.get());
+  EXPECT_FALSE(input_events &&
+               input_events->input_events()->password_field_shown);
 }
 
 // Checks that when the user set a focus on a field of a password form which was
diff --git a/ios/chrome/browser/ssl/BUILD.gn b/ios/chrome/browser/ssl/BUILD.gn
index 3be96a6..aa3b294 100644
--- a/ios/chrome/browser/ssl/BUILD.gn
+++ b/ios/chrome/browser/ssl/BUILD.gn
@@ -8,6 +8,8 @@
     "captive_portal_detector_tab_helper.h",
     "captive_portal_detector_tab_helper.mm",
     "captive_portal_detector_tab_helper_delegate.h",
+    "insecure_input_tab_helper.h",
+    "insecure_input_tab_helper.mm",
     "ios_captive_portal_blocking_page.h",
     "ios_captive_portal_blocking_page.mm",
     "ios_security_state_tab_helper.h",
@@ -24,6 +26,7 @@
     "//components/captive_portal",
     "//components/security_interstitials/core",
     "//components/security_state/core",
+    "//components/security_state/ios",
     "//components/strings",
     "//components/url_formatter",
     "//ios/chrome/app/strings",
@@ -55,10 +58,12 @@
   testonly = true
   sources = [
     "ios_captive_portal_blocking_page_unittest.mm",
+    "ios_security_state_tab_helper_unittest.mm",
     "ios_ssl_error_handler_unittest.mm",
   ]
   deps = [
     ":ssl",
+    "//components/security_state/core",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/web:test_support",
     "//ios/testing:ios_test_support",
diff --git a/ios/chrome/browser/ssl/insecure_input_tab_helper.h b/ios/chrome/browser/ssl/insecure_input_tab_helper.h
new file mode 100644
index 0000000..7fddf26
--- /dev/null
+++ b/ios/chrome/browser/ssl/insecure_input_tab_helper.h
@@ -0,0 +1,53 @@
+// 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_SSL_INSECURE_INPUT_TAB_HELPER_H_
+#define IOS_CHROME_BROWSER_SSL_INSECURE_INPUT_TAB_HELPER_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "ios/web/public/web_state/web_state_observer.h"
+#import "ios/web/public/web_state/web_state_user_data.h"
+
+// Observes user activity on forms and receives notifications about page content
+// from autofill and updates the page's |SSLStatusUserData| to track insecure
+// input events. Such events may change the page's Security Level.
+class InsecureInputTabHelper
+    : public web::WebStateObserver,
+      public web::WebStateUserData<InsecureInputTabHelper> {
+ public:
+  ~InsecureInputTabHelper() override;
+
+  static InsecureInputTabHelper* GetOrCreateForWebState(
+      web::WebState* web_state);
+
+  // This method should be called when a form containing a password field is
+  // parsed in a non-secure context.
+  void DidShowPasswordFieldInInsecureContext();
+
+  // This method should be called when the autofill component detects a credit
+  // card field was interacted with in a non-secure context.
+  void DidInteractWithNonsecureCreditCardInput();
+
+  // This method should be called when the user edits a field in a non-secure
+  // context.
+  void DidEditFieldInInsecureContext();
+
+ private:
+  friend class web::WebStateUserData<InsecureInputTabHelper>;
+  explicit InsecureInputTabHelper(web::WebState* web_state);
+
+  // WebStateObserver implementation.
+  void FormActivityRegistered(web::WebState* web_state,
+                              const std::string& form_name,
+                              const std::string& field_name,
+                              const std::string& type,
+                              const std::string& value,
+                              bool input_missing) override;
+
+  DISALLOW_COPY_AND_ASSIGN(InsecureInputTabHelper);
+};
+
+#endif  // IOS_CHROME_BROWSER_SSL_INSECURE_INPUT_TAB_HELPER_H_
diff --git a/ios/chrome/browser/ssl/insecure_input_tab_helper.mm b/ios/chrome/browser/ssl/insecure_input_tab_helper.mm
new file mode 100644
index 0000000..e1264bb
--- /dev/null
+++ b/ios/chrome/browser/ssl/insecure_input_tab_helper.mm
@@ -0,0 +1,128 @@
+// 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/ssl/insecure_input_tab_helper.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/memory/ptr_util.h"
+#include "components/security_state/ios/ssl_status_input_event_data.h"
+#import "ios/web/public/navigation_item.h"
+#import "ios/web/public/navigation_manager.h"
+#import "ios/web/public/origin_util.h"
+#import "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state_user_data.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Creates or retrieves the |user_data| object in the SSLStatus attached to the
+// WebState's NavigationItem.
+security_state::SSLStatusInputEventData* GetOrCreateSSLStatusInputEventData(
+    web::WebState* web_state) {
+  web::NavigationItem* item =
+      web_state->GetNavigationManager()->GetLastCommittedItem();
+
+  // We aren't guaranteed to always have a navigation item.
+  if (!item)
+    return nullptr;
+
+  web::SSLStatus& ssl = item->GetSSL();
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl.user_data.get());
+  if (!input_events) {
+    ssl.user_data = base::MakeUnique<security_state::SSLStatusInputEventData>();
+    input_events = static_cast<security_state::SSLStatusInputEventData*>(
+        ssl.user_data.get());
+  }
+  return input_events;
+}
+
+}  // namespace
+
+DEFINE_WEB_STATE_USER_DATA_KEY(InsecureInputTabHelper);
+
+InsecureInputTabHelper::~InsecureInputTabHelper() = default;
+
+// static
+InsecureInputTabHelper* InsecureInputTabHelper::GetOrCreateForWebState(
+    web::WebState* web_state) {
+  InsecureInputTabHelper* helper = FromWebState(web_state);
+  if (!helper) {
+    CreateForWebState(web_state);
+    helper = FromWebState(web_state);
+    DCHECK(helper);
+  }
+  return helper;
+}
+
+void InsecureInputTabHelper::DidShowPasswordFieldInInsecureContext() {
+  DCHECK(!web::IsOriginSecure(web_state()->GetLastCommittedURL()));
+
+  security_state::SSLStatusInputEventData* input_events =
+      GetOrCreateSSLStatusInputEventData(web_state());
+  if (!input_events)
+    return;
+
+  // If the first password field for the web contents was just
+  // shown, update the SSLStatusInputEventData.
+  if (!input_events->input_events()->password_field_shown) {
+    input_events->input_events()->password_field_shown = true;
+    web_state()->DidChangeVisibleSecurityState();
+  }
+}
+
+void InsecureInputTabHelper::DidInteractWithNonsecureCreditCardInput() {
+  DCHECK(!web::IsOriginSecure(web_state()->GetLastCommittedURL()));
+
+  security_state::SSLStatusInputEventData* input_events =
+      GetOrCreateSSLStatusInputEventData(web_state());
+  if (!input_events)
+    return;
+
+  // If the first credit card field for the web contents was just
+  // shown, update the SSLStatusInputEventData.
+  if (!input_events->input_events()->credit_card_field_edited) {
+    input_events->input_events()->credit_card_field_edited = true;
+    web_state()->DidChangeVisibleSecurityState();
+  }
+}
+
+void InsecureInputTabHelper::DidEditFieldInInsecureContext() {
+  DCHECK(!web::IsOriginSecure(web_state()->GetLastCommittedURL()));
+
+  security_state::SSLStatusInputEventData* input_events =
+      GetOrCreateSSLStatusInputEventData(web_state());
+  if (!input_events)
+    return;
+
+  // If the first field edit in the web contents was just performed,
+  // update the SSLStatusInputEventData.
+  if (!input_events->input_events()->insecure_field_edited) {
+    input_events->input_events()->insecure_field_edited = true;
+    web_state()->DidChangeVisibleSecurityState();
+  }
+}
+
+InsecureInputTabHelper::InsecureInputTabHelper(web::WebState* web_state)
+    : web::WebStateObserver(web_state) {
+  DCHECK(web_state);
+}
+
+void InsecureInputTabHelper::FormActivityRegistered(
+    web::WebState* web_state,
+    const std::string& form_name,
+    const std::string& field_name,
+    const std::string& type,
+    const std::string& value,
+    bool input_missing) {
+  if (type == "input" &&
+      !web::IsOriginSecure(web_state->GetLastCommittedURL())) {
+    DidEditFieldInInsecureContext();
+  }
+}
diff --git a/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm b/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm
index 7a76ea49..68659a69 100644
--- a/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm
+++ b/ios/chrome/browser/ssl/ios_security_state_tab_helper.mm
@@ -10,6 +10,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/security_state/core/security_state.h"
+#include "components/security_state/ios/ssl_status_input_event_data.h"
 #include "ios/web/public/browser_state.h"
 #include "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
@@ -55,15 +56,13 @@
   state->displayed_mixed_content =
       (ssl.content_status & web::SSLStatus::DISPLAYED_INSECURE_CONTENT) ? true
                                                                         : false;
-  state->insecure_input_events.password_field_shown =
-      (ssl.content_status & web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP)
-          ? true
-          : false;
-  state->insecure_input_events.credit_card_field_edited =
-      (ssl.content_status & web::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP)
-          ? true
-          : false;
   state->is_incognito = web_state_->GetBrowserState()->IsOffTheRecord();
 
+  security_state::SSLStatusInputEventData* input_events =
+      static_cast<security_state::SSLStatusInputEventData*>(
+          ssl.user_data.get());
+  if (input_events)
+    state->insecure_input_events = *input_events->input_events();
+
   return state;
 }
diff --git a/ios/chrome/browser/ssl/ios_security_state_tab_helper_unittest.mm b/ios/chrome/browser/ssl/ios_security_state_tab_helper_unittest.mm
new file mode 100644
index 0000000..533c7195
--- /dev/null
+++ b/ios/chrome/browser/ssl/ios_security_state_tab_helper_unittest.mm
@@ -0,0 +1,84 @@
+// 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/ssl/ios_security_state_tab_helper.h"
+
+#include "components/security_state/core/security_state.h"
+#import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
+#import "ios/web/public/navigation_item.h"
+#import "ios/web/public/navigation_manager.h"
+#include "ios/web/public/ssl_status.h"
+#import "ios/web/public/test/web_test_with_web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// This test fixture creates an IOSSecurityStateTabHelper and an
+// InsecureInputTabHelper for the WebState, then loads a non-secure
+// HTML document.
+class IOSSecurityStateTabHelperTest : public web::WebTestWithWebState {
+ protected:
+  void SetUp() override {
+    web::WebTestWithWebState::SetUp();
+    InsecureInputTabHelper::CreateForWebState(web_state());
+    insecure_input_ = InsecureInputTabHelper::FromWebState(web_state());
+    ASSERT_TRUE(insecure_input_);
+
+    IOSSecurityStateTabHelper::CreateForWebState(web_state());
+    LoadHtml(@"<html><body></body></html>", GURL("http://chromium.test"));
+  }
+
+  // Returns the InsecureInputEventData for current WebState().
+  security_state::InsecureInputEventData GetInsecureInput() const {
+    security_state::SecurityInfo info;
+    IOSSecurityStateTabHelper::FromWebState(web_state())
+        ->GetSecurityInfo(&info);
+    return info.insecure_input_events;
+  }
+
+  InsecureInputTabHelper* insecure_input_;
+};
+
+// Ensures that |insecure_field_edited| is set only when an editing event has
+// been reported.
+TEST_F(IOSSecurityStateTabHelperTest, SecurityInfoAfterEditing) {
+  // Verify |insecure_field_edited| is not set prematurely.
+  security_state::InsecureInputEventData events = GetInsecureInput();
+  EXPECT_FALSE(events.insecure_field_edited);
+
+  // Simulate an edit and verify |insecure_field_edited| is noted in the
+  // insecure_input_events.
+  insecure_input_->DidEditFieldInInsecureContext();
+  events = GetInsecureInput();
+  EXPECT_TRUE(events.insecure_field_edited);
+}
+
+// Ensures that |password_field_shown| is set only when a password field has
+// been reported.
+TEST_F(IOSSecurityStateTabHelperTest, SecurityInfoWithInsecurePasswordField) {
+  // Verify |password_field_shown| is not set prematurely.
+  security_state::InsecureInputEventData events = GetInsecureInput();
+  EXPECT_FALSE(events.password_field_shown);
+
+  // Simulate a password field display and verify |password_field_shown|
+  // is noted in the insecure_input_events.
+  insecure_input_->DidShowPasswordFieldInInsecureContext();
+  events = GetInsecureInput();
+  EXPECT_TRUE(events.password_field_shown);
+}
+
+// Ensures that |credit_card_field_edited| is set only when a credit card field
+// interaction has been reported.
+TEST_F(IOSSecurityStateTabHelperTest, SecurityInfoWithInsecureCreditCardField) {
+  // Verify |credit_card_field_edited| is not set prematurely.
+  security_state::InsecureInputEventData events = GetInsecureInput();
+  EXPECT_FALSE(events.credit_card_field_edited);
+
+  // Simulate a credit card field display and verify |credit_card_field_edited|
+  // is noted in the insecure_input_events.
+  insecure_input_->DidInteractWithNonsecureCreditCardInput();
+  events = GetInsecureInput();
+  EXPECT_TRUE(events.credit_card_field_edited);
+}
diff --git a/ios/chrome/browser/tabs/tab_helper_util.mm b/ios/chrome/browser/tabs/tab_helper_util.mm
index fcb73e9..e85ca331 100644
--- a/ios/chrome/browser/tabs/tab_helper_util.mm
+++ b/ios/chrome/browser/tabs/tab_helper_util.mm
@@ -29,6 +29,7 @@
 #include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/reading_list/reading_list_web_state_observer.h"
 #import "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h"
+#import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
 #import "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #import "ios/chrome/browser/store_kit/store_kit_tab_helper.h"
 #import "ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h"
@@ -119,6 +120,8 @@
         ->GetAccessoryViewProvider(),
   ]);
 
+  InsecureInputTabHelper::CreateForWebState(web_state);
+
   // Allow the embedder to attach tab helpers.
   ios::GetChromeBrowserProvider()->AttachTabHelpers(web_state, tab);
 
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index 628ff9eb..02a46ae 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -31,6 +31,7 @@
     "//ios/chrome/browser/autofill",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/infobars",
+    "//ios/chrome/browser/ssl",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/autofill/cells",
     "//ios/chrome/browser/ui/collection_view/cells",
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
index 60f2f38..798df62 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
@@ -67,6 +67,8 @@
   void PropagateAutofillPredictions(
       content::RenderFrameHost* rfh,
       const std::vector<FormStructure*>& forms) override;
+  void DidInteractWithNonsecureCreditCardInput(
+      content::RenderFrameHost* rfh) override;
 
  private:
   infobars::InfoBarManager* infobar_manager_;
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index d407b3d0..a3fc5a4 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -24,9 +24,11 @@
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_utils.h"
+#import "ios/chrome/browser/ssl/insecure_input_tab_helper.h"
 #include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
 #include "ios/chrome/browser/web_data_service_factory.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
+#import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -128,4 +130,10 @@
   }
 }
 
+void ChromeAutofillClientIOS::DidInteractWithNonsecureCreditCardInput(
+    content::RenderFrameHost* rfh) {
+  InsecureInputTabHelper::GetOrCreateForWebState(web_state())
+      ->DidInteractWithNonsecureCreditCardInput();
+}
+
 }  // namespace autofill
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index a4253c9..a0dec62 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -347,6 +347,7 @@
     "public/origin_util_unittest.mm",
     "public/referrer_util_unittest.cc",
     "public/serializable_user_data_manager_unittest.mm",
+    "public/ssl_status_unittest.cc",
     "public/user_agent_unittest.mm",
     "public/web_state/page_viewport_state_unittest.mm",
   ]
diff --git a/ios/web/public/ssl_status.cc b/ios/web/public/ssl_status.cc
index f2abd73a..c7513c6 100644
--- a/ios/web/public/ssl_status.cc
+++ b/ios/web/public/ssl_status.cc
@@ -13,7 +13,25 @@
       content_status(NORMAL_CONTENT) {
 }
 
-SSLStatus::SSLStatus(const SSLStatus& other) = default;
+SSLStatus::SSLStatus(const SSLStatus& other)
+    : security_style(other.security_style),
+      certificate(other.certificate),
+      cert_status(other.cert_status),
+      connection_status(other.connection_status),
+      content_status(other.content_status),
+      cert_status_host(other.cert_status_host),
+      user_data(other.user_data ? other.user_data->Clone() : nullptr) {}
+
+SSLStatus& SSLStatus::operator=(SSLStatus other) {
+  security_style = other.security_style;
+  certificate = other.certificate;
+  cert_status = other.cert_status;
+  connection_status = other.connection_status;
+  content_status = other.content_status;
+  cert_status_host = other.cert_status_host;
+  user_data = other.user_data ? other.user_data->Clone() : nullptr;
+  return *this;
+}
 
 SSLStatus::~SSLStatus() {}
 
diff --git a/ios/web/public/ssl_status.h b/ios/web/public/ssl_status.h
index 12848fc..1c5df45 100644
--- a/ios/web/public/ssl_status.h
+++ b/ios/web/public/ssl_status.h
@@ -5,6 +5,7 @@
 #ifndef IOS_WEB_PUBLIC_SSL_STATUS_H_
 #define IOS_WEB_PUBLIC_SSL_STATUS_H_
 
+#include <memory>
 #include <string>
 
 #include "ios/web/public/security_style.h"
@@ -15,6 +16,20 @@
 
 // Collects the SSL information for this NavigationItem.
 struct SSLStatus {
+  // SSLStatus consumers can attach instances of derived UserData classes to an
+  // SSLStatus. This allows an embedder to attach data to the NavigationItem
+  // without SSLStatus having to know about it. Derived UserData classes have to
+  // be cloneable since NavigationItems are cloned during navigations.
+  class UserData {
+   public:
+    UserData() {}
+    virtual ~UserData() = default;
+    virtual std::unique_ptr<UserData> Clone() = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(UserData);
+  };
+
   // Flags used for the page security content status.
   enum ContentStatusFlags {
     // HTTP page, or HTTPS page with no insecure content.
@@ -25,18 +40,11 @@
 
     // The RAN_INSECURE_CONTENT flag is intentionally omitted on iOS because
     // there is no way to tell when insecure content is run in a web view.
-
-    // HTTP page containing a password input, used to adjust UI on nonsecure
-    // pages that collect sensitive data.
-    DISPLAYED_PASSWORD_FIELD_ON_HTTP = 1 << 4,
-
-    // HTTP page containing a credit card input, used to adjust UI on nonsecure
-    // pages that collect sensitive data.
-    DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP = 1 << 5,
   };
 
   SSLStatus();
   SSLStatus(const SSLStatus& other);
+  SSLStatus& operator=(SSLStatus other);
   ~SSLStatus();
 
   bool Equals(const SSLStatus& status) const {
@@ -47,6 +55,7 @@
            cert_status == status.cert_status &&
            content_status == status.content_status;
     // |cert_status_host| is not used for comparison intentionally.
+    // |user_data| also not used for comparison.
   }
 
   web::SecurityStyle security_style;
@@ -60,6 +69,10 @@
   // Used to check if |cert_status| is still valid or needs to be recalculated
   // (e.g. after redirect).
   std::string cert_status_host;
+  // Embedder-specific data attached to the SSLStatus is cloned when an
+  // |SSLStatus| is assigned or copy-constructed, and is cleared when a
+  // navigation commits.
+  std::unique_ptr<UserData> user_data;
 };
 
 }  // namespace web
diff --git a/ios/web/public/ssl_status_unittest.cc b/ios/web/public/ssl_status_unittest.cc
new file mode 100644
index 0000000..53588a8f
--- /dev/null
+++ b/ios/web/public/ssl_status_unittest.cc
@@ -0,0 +1,78 @@
+// 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 "ios/web/public/ssl_status.h"
+
+#include "base/memory/ptr_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace web {
+
+using SSLStatusTest = PlatformTest;
+
+// The TrivialUserData class stores an integer and copies that integer when
+// its Clone() method is called.
+class TrivialUserData : public SSLStatus::UserData {
+ public:
+  TrivialUserData() : value_(0) {}
+  explicit TrivialUserData(int value) : value_(value) {}
+  ~TrivialUserData() override {}
+
+  int value() { return value_; }
+
+  std::unique_ptr<SSLStatus::UserData> Clone() override {
+    return base::MakeUnique<TrivialUserData>(value_);
+  }
+
+ private:
+  int value_;
+  DISALLOW_COPY_AND_ASSIGN(TrivialUserData);
+};
+
+// Tests the Equals() method of the SSLStatus class.
+TEST_F(SSLStatusTest, SSLStatusEqualityTest) {
+  SSLStatus status;
+  EXPECT_EQ(SECURITY_STYLE_UNKNOWN, status.security_style);
+  EXPECT_EQ(0u, status.cert_status);
+  EXPECT_EQ(0, status.connection_status);
+  EXPECT_EQ(SSLStatus::NORMAL_CONTENT, status.content_status);
+
+  // Verify that the Equals() method returns false if two SSLStatus objects
+  // have different ContentStatusFlags.
+  SSLStatus other_status;
+  other_status.content_status =
+      SSLStatus::ContentStatusFlags::DISPLAYED_INSECURE_CONTENT;
+  EXPECT_FALSE(status.Equals(other_status));
+  EXPECT_FALSE(other_status.Equals(status));
+
+  // Verify a copied SSLStatus Equals() the original.
+  SSLStatus copied_status = status;
+  EXPECT_TRUE(status.Equals(copied_status));
+  EXPECT_TRUE(copied_status.Equals(status));
+
+  // Verify a copied SSLStatus still Equals() the original after a UserData is
+  // assigned to it.
+  copied_status.user_data = base::MakeUnique<TrivialUserData>();
+  EXPECT_TRUE(status.Equals(copied_status));
+  EXPECT_TRUE(copied_status.Equals(status));
+}
+
+// Tests that copying a SSLStatus class clones its UserData.
+TEST_F(SSLStatusTest, SSLStatusCloningTest) {
+  const int kMagic = 1234;
+  SSLStatus status;
+  status.user_data = base::MakeUnique<TrivialUserData>(kMagic);
+
+  // Verify that copying a SSLStatus with a UserData assigned will Clone()
+  // the UserData to the new copy.
+  SSLStatus copied_status = status;
+  EXPECT_TRUE(status.Equals(copied_status));
+  EXPECT_TRUE(copied_status.Equals(status));
+  TrivialUserData* data =
+      static_cast<TrivialUserData*>(copied_status.user_data.get());
+  EXPECT_EQ(kMagic, data->value());
+}
+
+}  // namespace web
diff --git a/ios/web/public/test/fakes/test_web_state.h b/ios/web/public/test/fakes/test_web_state.h
index fce4734..57f5bec 100644
--- a/ios/web/public/test/fakes/test_web_state.h
+++ b/ios/web/public/test/fakes/test_web_state.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 #include "base/observer_list.h"
@@ -70,8 +71,6 @@
   CRWWebViewProxyType GetWebViewProxy() const override;
   bool IsShowingWebInterstitial() const override;
   WebInterstitial* GetWebInterstitial() const override;
-  void OnPasswordInputShownOnHttp() override {}
-  void OnCreditCardInputShownOnHttp() override {}
 
   void AddObserver(WebStateObserver* observer) override;
 
@@ -80,6 +79,7 @@
   void AddPolicyDecider(WebStatePolicyDecider* decider) override {}
   void RemovePolicyDecider(WebStatePolicyDecider* decider) override {}
   WebStateInterfaceProvider* GetWebStateInterfaceProvider() override;
+  void DidChangeVisibleSecurityState() override {}
   bool HasOpener() const override;
   void SetHasOpener(bool has_opener) override;
   void TakeSnapshot(const SnapshotCallback& callback,
diff --git a/ios/web/public/web_state/web_state.h b/ios/web/public/web_state/web_state.h
index 527e740..ddc9bbf 100644
--- a/ios/web/public/web_state/web_state.h
+++ b/ios/web/public/web_state/web_state.h
@@ -7,7 +7,9 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/callback_forward.h"
@@ -234,16 +236,6 @@
   // Returns the currently visible WebInterstitial if one is shown.
   virtual WebInterstitial* GetWebInterstitial() const = 0;
 
-  // Called when the WebState has displayed a password field on an HTTP page.
-  // This method modifies the appropriate NavigationEntry's SSLStatus to record
-  // the sensitive input field, so that embedders can adjust the UI if desired.
-  virtual void OnPasswordInputShownOnHttp() = 0;
-
-  // Called when the WebState has displayed a credit card field on an HTTP page.
-  // This method modifies the appropriate NavigationEntry's SSLStatus to record
-  // the sensitive input field, so that embedders can adjust the UI if desired.
-  virtual void OnCreditCardInputShownOnHttp() = 0;
-
   // Callback used to handle script commands.
   // The callback must return true if the command was handled, and false
   // otherwise.
@@ -270,6 +262,15 @@
   // Returns Mojo interface registry for this WebState.
   virtual WebStateInterfaceProvider* GetWebStateInterfaceProvider() = 0;
 
+  // Typically an embedder will:
+  //    - Implement this method to receive notification of changes to the page's
+  //      |VisibleSecurityState|, updating security UI (e.g. a lock icon) to
+  //      reflect the current security state of the page.
+  // ...and optionally:
+  //    - Invoke this method upon detection of an event that will change
+  //      the security state (e.g. a non-secure form element is edited).
+  virtual void DidChangeVisibleSecurityState() = 0;
+
  protected:
   // Binds |interface_pipe| to an implementation of |interface_name| that is
   // scoped to this WebState instance (if that such an implementation is
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h
index 4833accf2..5b9b054 100644
--- a/ios/web/web_state/ui/crw_web_controller.h
+++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -212,14 +212,6 @@
 // Notifies the CRWWebController that it has been shown.
 - (void)wasShown;
 
-// Notifies the CRWWebController that the current page is an HTTP page
-// containing a password field.
-- (void)didShowPasswordInputOnHTTP;
-
-// Notifies the CRWWebController that the current page is an HTTP page
-// containing a credit card field.
-- (void)didShowCreditCardInputOnHTTP;
-
 // Notifies the CRWWebController that it has been hidden.
 - (void)wasHidden;
 
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index d1e496eb..f66028f 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -3699,24 +3699,6 @@
                                  hasOnlySecureContent:hasOnlySecureContent];
 }
 
-- (void)didShowPasswordInputOnHTTP {
-  DCHECK(!web::IsOriginSecure(self.webState->GetLastCommittedURL()));
-  web::NavigationItem* item =
-      _webStateImpl->GetNavigationManager()->GetLastCommittedItem();
-  item->GetSSL().content_status |=
-      web::SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP;
-  _webStateImpl->OnVisibleSecurityStateChange();
-}
-
-- (void)didShowCreditCardInputOnHTTP {
-  DCHECK(!web::IsOriginSecure(self.webState->GetLastCommittedURL()));
-  web::NavigationItem* item =
-      _webStateImpl->GetNavigationManager()->GetLastCommittedItem();
-  item->GetSSL().content_status |=
-      web::SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP;
-  _webStateImpl->OnVisibleSecurityStateChange();
-}
-
 - (void)handleSSLCertError:(NSError*)error
              forNavigation:(WKNavigation*)navigation {
   CHECK(web::IsWKWebViewSSLCertError(error));
@@ -3792,7 +3774,7 @@
         }
       }));
 
-  _webStateImpl->OnVisibleSecurityStateChange();
+  _webStateImpl->DidChangeVisibleSecurityState();
   [self loadCancelled];
 }
 
@@ -4760,7 +4742,7 @@
   web::NavigationItem* lastCommittedNavigationItem =
       _webStateImpl->GetNavigationManager()->GetLastCommittedItem();
   if (navigationItem == lastCommittedNavigationItem) {
-    _webStateImpl->OnVisibleSecurityStateChange();
+    _webStateImpl->DidChangeVisibleSecurityState();
   }
 }
 
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index dcca2b3f..95018df4 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -512,40 +512,6 @@
 // Test fixture for testing visible security state.
 typedef WebTestWithWebState CRWWebStateSecurityStateTest;
 
-// Tests that OnPasswordInputShownOnHttp updates the SSLStatus to indicate that
-// a password field has been displayed on an HTTP page.
-TEST_F(CRWWebStateSecurityStateTest, HttpPassword) {
-  LoadHtml(@"<html><body></body></html>", GURL("http://chromium.test"));
-  NavigationManager* nav_manager = web_state()->GetNavigationManager();
-  EXPECT_FALSE(nav_manager->GetLastCommittedItem()->GetSSL().content_status &
-               SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
-  TestWebStateObserver observer(web_state());
-  ASSERT_FALSE(observer.did_change_visible_security_state_info());
-  web_state()->OnPasswordInputShownOnHttp();
-  EXPECT_TRUE(nav_manager->GetLastCommittedItem()->GetSSL().content_status &
-              SSLStatus::DISPLAYED_PASSWORD_FIELD_ON_HTTP);
-  ASSERT_TRUE(observer.did_change_visible_security_state_info());
-  EXPECT_EQ(web_state(),
-            observer.did_change_visible_security_state_info()->web_state);
-}
-
-// Tests that OnCreditCardInputShownOnHttp updates the SSLStatus to indicate
-// that a credit card field has been displayed on an HTTP page.
-TEST_F(CRWWebStateSecurityStateTest, HttpCreditCard) {
-  LoadHtml(@"<html><body></body></html>", GURL("http://chromium.test"));
-  NavigationManager* nav_manager = web_state()->GetNavigationManager();
-  EXPECT_FALSE(nav_manager->GetLastCommittedItem()->GetSSL().content_status &
-               SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP);
-  TestWebStateObserver observer(web_state());
-  ASSERT_FALSE(observer.did_change_visible_security_state_info());
-  web_state()->OnCreditCardInputShownOnHttp();
-  EXPECT_TRUE(nav_manager->GetLastCommittedItem()->GetSSL().content_status &
-              SSLStatus::DISPLAYED_CREDIT_CARD_FIELD_ON_HTTP);
-  ASSERT_TRUE(observer.did_change_visible_security_state_info());
-  EXPECT_EQ(web_state(),
-            observer.did_change_visible_security_state_info()->web_state);
-}
-
 // Tests that loading HTTP page updates the SSLStatus.
 TEST_F(CRWWebStateSecurityStateTest, LoadHttpPage) {
   TestWebStateObserver observer(web_state());
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index a0122e7..78cf19c 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -66,7 +66,7 @@
 class WebStateImpl : public WebState, public NavigationManagerDelegate {
  public:
   // Constructor for WebStateImpls created for new sessions.
-  WebStateImpl(const CreateParams& params);
+  explicit WebStateImpl(const CreateParams& params);
   // Constructor for WebStatesImpls created for deserialized sessions
   WebStateImpl(const CreateParams& params, CRWSessionStorage* session_storage);
   ~WebStateImpl() override;
@@ -84,9 +84,6 @@
   // Called when page title was changed.
   void OnTitleChanged();
 
-  // Called when the visible security state of the page changes.
-  void OnVisibleSecurityStateChange();
-
   // Called when a dialog or child window open request was suppressed.
   void OnDialogSuppressed();
 
@@ -220,13 +217,12 @@
   void ShowTransientContentView(CRWContentView* content_view) override;
   bool IsShowingWebInterstitial() const override;
   WebInterstitial* GetWebInterstitial() const override;
-  void OnPasswordInputShownOnHttp() override;
-  void OnCreditCardInputShownOnHttp() override;
   void AddScriptCommandCallback(const ScriptCommandCallback& callback,
                                 const std::string& command_prefix) override;
   void RemoveScriptCommandCallback(const std::string& command_prefix) override;
   id<CRWWebViewProxy> GetWebViewProxy() const override;
   WebStateInterfaceProvider* GetWebStateInterfaceProvider() override;
+  void DidChangeVisibleSecurityState() override;
   void BindInterfaceRequestFromMainFrame(
       const std::string& interface_name,
       mojo::ScopedMessagePipeHandle interface_pipe) override;
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 8aff03e..36b3caa 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -182,11 +182,6 @@
     observer.TitleWasSet(this);
 }
 
-void WebStateImpl::OnVisibleSecurityStateChange() {
-  for (auto& observer : observers_)
-    observer.DidChangeVisibleSecurityState(this);
-}
-
 void WebStateImpl::OnDialogSuppressed() {
   DCHECK(ShouldSuppressDialogs());
   for (auto& observer : observers_)
@@ -352,14 +347,6 @@
   return interstitial_;
 }
 
-void WebStateImpl::OnPasswordInputShownOnHttp() {
-  [web_controller_ didShowPasswordInputOnHTTP];
-}
-
-void WebStateImpl::OnCreditCardInputShownOnHttp() {
-  [web_controller_ didShowCreditCardInputOnHTTP];
-}
-
 net::HttpResponseHeaders* WebStateImpl::GetHttpResponseHeaders() const {
   return http_response_headers_.get();
 }
@@ -540,6 +527,11 @@
   return web_state_interface_provider_.get();
 }
 
+void WebStateImpl::DidChangeVisibleSecurityState() {
+  for (auto& observer : observers_)
+    observer.DidChangeVisibleSecurityState(this);
+}
+
 void WebStateImpl::BindInterfaceRequestFromMainFrame(
     const std::string& interface_name,
     mojo::ScopedMessagePipeHandle interface_pipe) {
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index dc86ff56..a3fa9bc 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -299,7 +299,7 @@
 
   // Test that DidChangeVisibleSecurityState() is called.
   ASSERT_FALSE(observer->did_change_visible_security_state_info());
-  web_state_->OnVisibleSecurityStateChange();
+  web_state_->DidChangeVisibleSecurityState();
   ASSERT_TRUE(observer->did_change_visible_security_state_info());
   EXPECT_EQ(web_state_.get(),
             observer->did_change_visible_security_state_info()->web_state);
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/classes-pane-widget-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/classes-pane-widget-expected.txt
new file mode 100644
index 0000000..1c92ae4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/classes-pane-widget-expected.txt
@@ -0,0 +1,21 @@
+Tests that classes pane widget shows correct suggestions.
+
+
+Completion for prefix: .
+.a1
+.a2
+.abc
+.b
+
+Completion for prefix: a
+a1
+a2
+abc
+
+Adding class "abc"
+
+Completion for prefix: a
+a1
+a2
+abc
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/elements/classes-pane-widget.js b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/classes-pane-widget.js
new file mode 100644
index 0000000..13fefc4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/elements/classes-pane-widget.js
@@ -0,0 +1,41 @@
+// 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.
+
+(async function() {
+  TestRunner.addResult(`Tests that classes pane widget shows correct suggestions.\n`);
+
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+
+  await TestRunner.loadHTML(`
+    <style>
+      .b { width: 1px; }
+      .abc { width: 1px; }
+      .a1 { width: 1px; }
+      .a2 { width: 1px; }
+    </style>
+    <div id="myDiv"></div>
+  `);
+
+  var classesPane = new Elements.ClassesPaneWidget();
+  ElementsTestRunner.selectNodeWithId('myDiv', onNodeSelected);
+
+  async function onNodeSelected() {
+    await testCompletion('.');
+    await testCompletion('a');
+    TestRunner.addResult('\nAdding class "abc"');
+    await testCompletion('a');
+    TestRunner.completeTest();
+  }
+
+  /**
+   * @param {string} prefix
+   */
+  async function testCompletion(prefix) {
+    TestRunner.addResult('\nCompletion for prefix: ' + prefix);
+    var completions = await classesPane._prompt._buildClassNameCompletions('', prefix);
+    for (var completion of completions)
+      TestRunner.addResult(completion.text);
+  }
+})();
diff --git a/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp b/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp
index cfe742fb..624102a6 100644
--- a/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/RejectedPromises.cpp
@@ -232,7 +232,7 @@
 
 std::unique_ptr<RejectedPromises::MessageQueue>
 RejectedPromises::CreateMessageQueue() {
-  return WTF::MakeUnique<MessageQueue>();
+  return std::make_unique<MessageQueue>();
 }
 
 void RejectedPromises::Dispose() {
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
index b64a2e73..900586fb 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
@@ -627,7 +627,7 @@
       CodeGenerationCheckCallbackInMainThread);
   if (RuntimeEnabledFeatures::V8IdleTasksEnabled()) {
     V8PerIsolateData::EnableIdleTasks(
-        isolate, WTF::MakeUnique<V8IdleTaskRunner>(scheduler));
+        isolate, std::make_unique<V8IdleTaskRunner>(scheduler));
   }
 
   isolate->SetPromiseRejectCallback(PromiseRejectHandlerInMainThread);
@@ -645,7 +645,7 @@
       ScriptWrappableVisitor::PerformCleanup);
 
   V8PerIsolateData::From(isolate)->SetThreadDebugger(
-      WTF::MakeUnique<MainThreadDebugger>(isolate));
+      std::make_unique<MainThreadDebugger>(isolate));
 
   BindingSecurity::InitWrapperCreationSecurityCheck();
 }
diff --git a/third_party/WebKit/Source/bindings/modules/v8/serialization/V8ScriptValueSerializerForModulesTest.cpp b/third_party/WebKit/Source/bindings/modules/v8/serialization/V8ScriptValueSerializerForModulesTest.cpp
index 176ef28..b0d86a9 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/serialization/V8ScriptValueSerializerForModulesTest.cpp
+++ b/third_party/WebKit/Source/bindings/modules/v8/serialization/V8ScriptValueSerializerForModulesTest.cpp
@@ -405,7 +405,7 @@
   // Check that one can decrypt data encrypted with the other.
   Vector<unsigned char> iv(16, 0);
   WebCryptoAlgorithm encrypt_algorithm(
-      kWebCryptoAlgorithmIdAesCbc, WTF::MakeUnique<WebCryptoAesCbcParams>(iv));
+      kWebCryptoAlgorithmIdAesCbc, std::make_unique<WebCryptoAesCbcParams>(iv));
   Vector<unsigned char> plaintext{1, 2, 3};
   WebVector<uint8_t> ciphertext =
       SyncEncrypt(script_state, encrypt_algorithm, key->Key(), plaintext);
@@ -436,7 +436,7 @@
   Vector<uint8_t> ciphertext{0x33, 0x26, 0xe7, 0x64, 0x11, 0x5e, 0xf4, 0x60,
                              0x96, 0x08, 0x11, 0xaf, 0x65, 0x8b, 0x87, 0x04};
   WebCryptoAlgorithm encrypt_algorithm(
-      kWebCryptoAlgorithmIdAesCbc, WTF::MakeUnique<WebCryptoAesCbcParams>(iv));
+      kWebCryptoAlgorithmIdAesCbc, std::make_unique<WebCryptoAesCbcParams>(iv));
   WebVector<uint8_t> plaintext =
       SyncDecrypt(script_state, encrypt_algorithm, new_key->Key(), ciphertext);
   EXPECT_THAT(plaintext, ElementsAre(1, 2, 3));
@@ -551,7 +551,7 @@
   // Check that one can verify a message signed by the other.
   Vector<uint8_t> message{1, 2, 3};
   WebCryptoAlgorithm algorithm(kWebCryptoAlgorithmIdRsaPss,
-                               WTF::MakeUnique<WebCryptoRsaPssParams>(16));
+                               std::make_unique<WebCryptoRsaPssParams>(16));
   WebVector<uint8_t> signature =
       SyncSign(script_state, algorithm, new_private_key->Key(), message);
   EXPECT_TRUE(SyncVerifySignature(script_state, algorithm, public_key->Key(),
@@ -602,7 +602,7 @@
       0xeb, 0x17, 0x68, 0x1f, 0xbd, 0xfa, 0xf7, 0xd6, 0x1f, 0xa4, 0x7c, 0x9e,
       0x9e, 0xb1, 0x96, 0x8f, 0xe6, 0x5e, 0x89, 0x99};
   WebCryptoAlgorithm algorithm(kWebCryptoAlgorithmIdRsaPss,
-                               WTF::MakeUnique<WebCryptoRsaPssParams>(16));
+                               std::make_unique<WebCryptoRsaPssParams>(16));
   EXPECT_TRUE(SyncVerifySignature(script_state, algorithm,
                                   new_public_key->Key(), signature, message));
 }
@@ -643,7 +643,7 @@
   WebCryptoAlgorithm hash(kWebCryptoAlgorithmIdSha256, nullptr);
   Vector<uint8_t> message{1, 2, 3};
   WebCryptoAlgorithm algorithm(kWebCryptoAlgorithmIdEcdsa,
-                               WTF::MakeUnique<WebCryptoEcdsaParams>(hash));
+                               std::make_unique<WebCryptoEcdsaParams>(hash));
   WebVector<uint8_t> signature =
       SyncSign(script_state, algorithm, new_private_key->Key(), message);
   EXPECT_TRUE(SyncVerifySignature(script_state, algorithm, public_key->Key(),
@@ -684,7 +684,7 @@
       0x83, 0x27, 0x37, 0x69, 0x4d, 0x32, 0x63, 0x1e, 0x82};
   WebCryptoAlgorithm hash(kWebCryptoAlgorithmIdSha256, nullptr);
   WebCryptoAlgorithm algorithm(kWebCryptoAlgorithmIdEcdsa,
-                               WTF::MakeUnique<WebCryptoEcdsaParams>(hash));
+                               std::make_unique<WebCryptoEcdsaParams>(hash));
   EXPECT_TRUE(SyncVerifySignature(script_state, algorithm,
                                   new_public_key->Key(), signature, message));
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ClassesPaneWidget.js b/third_party/WebKit/Source/devtools/front_end/elements/ClassesPaneWidget.js
index 5b66cbf..3157a13 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/ClassesPaneWidget.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ClassesPaneWidget.js
@@ -14,7 +14,7 @@
     this.setDefaultFocusedElement(this._input);
     this._classesContainer = this.contentElement.createChild('div', 'source-code');
     this._classesContainer.classList.add('styles-element-classes-container');
-    this._prompt = new Elements.ClassesPaneWidget.ClassNamePrompt();
+    this._prompt = new Elements.ClassesPaneWidget.ClassNamePrompt(this._nodeClasses.bind(this));
     this._prompt.setAutocompletionTimeout(0);
     this._prompt.renderAsBlock();
 
@@ -262,8 +262,12 @@
  * @unrestricted
  */
 Elements.ClassesPaneWidget.ClassNamePrompt = class extends UI.TextPrompt {
-  constructor() {
+  /**
+   * @param {function(!SDK.DOMNode):!Map<string, boolean>} nodeClasses
+   */
+  constructor(nodeClasses) {
     super();
+    this._nodeClasses = nodeClasses;
     this.initialize(this._buildClassNameCompletions.bind(this), ' ');
     this.disableDefaultSuggestionForEmptyInput();
     this._selectedFrameId = '';
@@ -313,9 +317,12 @@
       this._classNamesPromise = this._getClassNames(selectedNode);
 
     return this._classNamesPromise.then(completions => {
+      var classesMap = this._nodeClasses(/** @type {!SDK.DOMNode} */ (selectedNode));
+      completions = completions.filter(value => !classesMap.get(value));
+
       if (prefix[0] === '.')
         completions = completions.map(value => '.' + value);
-      return completions.filter(value => value.startsWith(prefix)).map(completion => ({text: completion}));
+      return completions.filter(value => value.startsWith(prefix)).sort().map(completion => ({text: completion}));
     });
   }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
index a9a0e1c..5777cf9 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
@@ -128,6 +128,17 @@
   }
 
   /**
+   * @param {!Object} headers
+   * @return {!Object<string, string>}
+   */
+  static _lowercaseHeaders(headers) {
+    var newHeaders = {};
+    for (var headerName in headers)
+      newHeaders[headerName.toLowerCase()] = headers[headerName];
+    return newHeaders;
+  }
+
+  /**
    * @param {string} url
    * @return {!SDK.NetworkRequest}
    */
@@ -421,6 +432,7 @@
    */
   responseReceived(requestId, loaderId, time, resourceType, response, frameId) {
     var networkRequest = this._inflightRequestsById[requestId];
+    var lowercaseHeaders = SDK.NetworkManager._lowercaseHeaders(response.headers);
     if (!networkRequest) {
       // We missed the requestWillBeSent.
       var eventData = {};
@@ -429,7 +441,7 @@
       eventData.loaderId = loaderId;
       eventData.resourceType = resourceType;
       eventData.mimeType = response.mimeType;
-      var lastModifiedHeader = response.headers['last-modified'];
+      var lastModifiedHeader = lowercaseHeaders['last-modified'];
       eventData.lastModified = lastModifiedHeader ? new Date(lastModifiedHeader) : null;
       this._manager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestUpdateDropped, eventData);
       return;
@@ -439,7 +451,7 @@
     networkRequest.setResourceType(Common.resourceTypes[resourceType]);
 
     // net::ParsedCookie::kMaxCookieSize = 4096 (net/cookies/parsed_cookie.h)
-    if ('Set-Cookie' in response.headers && response.headers['Set-Cookie'].length > 4096) {
+    if ('set-cookie' in lowercaseHeaders && lowercaseHeaders['set-cookie'].length > 4096) {
       var message = Common.UIString(
           'Set-Cookie header is ignored in response from url: %s. Cookie length should be less than or equal to 4096 characters.',
           response.url);
diff --git a/third_party/WebKit/Source/platform/WebThreadSupportingGC.cpp b/third_party/WebKit/Source/platform/WebThreadSupportingGC.cpp
index 3223833e..551296f 100644
--- a/third_party/WebKit/Source/platform/WebThreadSupportingGC.cpp
+++ b/third_party/WebKit/Source/platform/WebThreadSupportingGC.cpp
@@ -51,7 +51,7 @@
 void WebThreadSupportingGC::InitializeOnThread() {
   DCHECK(thread_->IsCurrentThread());
   ThreadState::AttachCurrentThread();
-  gc_task_runner_ = WTF::MakeUnique<GCTaskRunner>(thread_);
+  gc_task_runner_ = std::make_unique<GCTaskRunner>(thread_);
 }
 
 void WebThreadSupportingGC::ShutdownOnThread() {
diff --git a/third_party/WebKit/Source/platform/animation/CompositorAnimationPlayer.cpp b/third_party/WebKit/Source/platform/animation/CompositorAnimationPlayer.cpp
index 8bf50418..47c621d8 100644
--- a/third_party/WebKit/Source/platform/animation/CompositorAnimationPlayer.cpp
+++ b/third_party/WebKit/Source/platform/animation/CompositorAnimationPlayer.cpp
@@ -13,13 +13,13 @@
 namespace blink {
 
 std::unique_ptr<CompositorAnimationPlayer> CompositorAnimationPlayer::Create() {
-  return WTF::MakeUnique<CompositorAnimationPlayer>(
+  return std::make_unique<CompositorAnimationPlayer>(
       cc::AnimationPlayer::Create(cc::AnimationIdProvider::NextPlayerId()));
 }
 
 std::unique_ptr<CompositorAnimationPlayer>
 CompositorAnimationPlayer::CreateWorkletPlayer(const String& name) {
-  return WTF::MakeUnique<CompositorAnimationPlayer>(
+  return std::make_unique<CompositorAnimationPlayer>(
       cc::WorkletAnimationPlayer::Create(
           cc::AnimationIdProvider::NextPlayerId(),
           std::string(name.Ascii().data(), name.length())));
diff --git a/third_party/WebKit/Source/platform/bindings/RuntimeCallStats.cpp b/third_party/WebKit/Source/platform/bindings/RuntimeCallStats.cpp
index 43e92f6..032e03b2 100644
--- a/third_party/WebKit/Source/platform/bindings/RuntimeCallStats.cpp
+++ b/third_party/WebKit/Source/platform/bindings/RuntimeCallStats.cpp
@@ -153,7 +153,7 @@
   CounterMap::iterator it = counter_map_.find(name);
   if (it != counter_map_.end())
     return it->value.get();
-  return counter_map_.insert(name, WTF::MakeUnique<RuntimeCallCounter>(name))
+  return counter_map_.insert(name, std::make_unique<RuntimeCallCounter>(name))
       .stored_value->value.get();
 }
 
diff --git a/third_party/WebKit/Source/platform/bindings/V8PerContextData.cpp b/third_party/WebKit/Source/platform/bindings/V8PerContextData.cpp
index ed5cbd7..98602dd3 100644
--- a/third_party/WebKit/Source/platform/bindings/V8PerContextData.cpp
+++ b/third_party/WebKit/Source/platform/bindings/V8PerContextData.cpp
@@ -46,7 +46,7 @@
     : isolate_(context->GetIsolate()),
       wrapper_boilerplates_(isolate_),
       constructor_map_(isolate_),
-      context_holder_(WTF::MakeUnique<gin::ContextHolder>(isolate_)),
+      context_holder_(std::make_unique<gin::ContextHolder>(isolate_)),
       context_(isolate_, context),
       activity_logger_(nullptr) {
   context_holder_->SetContext(context);
diff --git a/third_party/WebKit/Source/platform/blob/BlobBytesProviderTest.cpp b/third_party/WebKit/Source/platform/blob/BlobBytesProviderTest.cpp
index 9ba8d4f..8044064 100644
--- a/third_party/WebKit/Source/platform/blob/BlobBytesProviderTest.cpp
+++ b/third_party/WebKit/Source/platform/blob/BlobBytesProviderTest.cpp
@@ -4,6 +4,10 @@
 
 #include "platform/blob/BlobBytesProvider.h"
 
+#include <algorithm>
+#include <memory>
+#include <utility>
+
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/run_loop.h"
@@ -50,7 +54,7 @@
 };
 
 TEST_F(BlobBytesProviderTest, RequestAsReply) {
-  auto provider = WTF::MakeUnique<BlobBytesProvider>(test_data1_);
+  auto provider = std::make_unique<BlobBytesProvider>(test_data1_);
   Vector<uint8_t> received_bytes;
   provider->RequestAsReply(
       base::Bind([](Vector<uint8_t>* bytes_out,
@@ -59,7 +63,7 @@
   EXPECT_EQ(test_bytes1_, received_bytes);
 
   received_bytes.clear();
-  provider = WTF::MakeUnique<BlobBytesProvider>(test_data1_);
+  provider = std::make_unique<BlobBytesProvider>(test_data1_);
   provider->AppendData(test_data2_);
   provider->AppendData(test_data3_);
   provider->RequestAsReply(
@@ -83,7 +87,7 @@
  public:
   void SetUp() override {
     BlobBytesProviderTest::SetUp();
-    test_provider_ = WTF::MakeUnique<BlobBytesProvider>(test_data1_);
+    test_provider_ = std::make_unique<BlobBytesProvider>(test_data1_);
     test_provider_->AppendData(test_data2_);
     test_provider_->AppendData(test_data3_);
 
@@ -213,7 +217,7 @@
                         ::testing::ValuesIn(file_tests));
 
 TEST_F(BlobBytesProviderTest, RequestAsFile_MultipleChunks) {
-  auto provider = WTF::MakeUnique<BlobBytesProvider>(test_data1_);
+  auto provider = std::make_unique<BlobBytesProvider>(test_data1_);
   provider->AppendData(test_data2_);
   provider->AppendData(test_data3_);
 
@@ -245,7 +249,7 @@
 }
 
 TEST_F(BlobBytesProviderTest, RequestAsFile_InvaldFile) {
-  auto provider = WTF::MakeUnique<BlobBytesProvider>(test_data1_);
+  auto provider = std::make_unique<BlobBytesProvider>(test_data1_);
 
   provider->RequestAsFile(
       0, 16, base::File(), 0,
@@ -255,7 +259,7 @@
 }
 
 TEST_F(BlobBytesProviderTest, RequestAsFile_UnwritableFile) {
-  auto provider = WTF::MakeUnique<BlobBytesProvider>(test_data1_);
+  auto provider = std::make_unique<BlobBytesProvider>(test_data1_);
 
   base::FilePath path;
   base::CreateTemporaryFile(&path);
@@ -273,7 +277,7 @@
 }
 
 TEST_F(BlobBytesProviderTest, RequestAsStream) {
-  auto provider = WTF::MakeUnique<BlobBytesProvider>(test_data1_);
+  auto provider = std::make_unique<BlobBytesProvider>(test_data1_);
   provider->AppendData(test_data2_);
   provider->AppendData(test_data3_);
 
diff --git a/third_party/WebKit/Source/platform/blob/BlobData.cpp b/third_party/WebKit/Source/platform/blob/BlobData.cpp
index 42350fb..10c3756 100644
--- a/third_party/WebKit/Source/platform/blob/BlobData.cpp
+++ b/third_party/WebKit/Source/platform/blob/BlobData.cpp
@@ -343,7 +343,7 @@
             last_bytes_provider->AppendData(item.data);
           } else {
             BytesProviderPtr bytes_provider;
-            auto provider = WTF::MakeUnique<BlobBytesProvider>(item.data);
+            auto provider = std::make_unique<BlobBytesProvider>(item.data);
             last_bytes_provider = provider.get();
             if (file_runner) {
               // TODO(mek): Considering binding BytesProvider on the IO thread
diff --git a/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp b/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
index b6e72c0..c75c2f65 100644
--- a/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
+++ b/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
@@ -67,7 +67,7 @@
   explicit MockBlob(const String& uuid) : uuid_(uuid) {}
 
   void Clone(BlobRequest request) override {
-    mojo::MakeStrongBinding(WTF::MakeUnique<MockBlob>(uuid_),
+    mojo::MakeStrongBinding(std::make_unique<MockBlob>(uuid_),
                             std::move(request));
   }
 
@@ -101,7 +101,7 @@
                 RegisterCallback callback) override {
     registrations.push_back(Registration{
         uuid, content_type, content_disposition, std::move(elements)});
-    mojo::MakeStrongBinding(WTF::MakeUnique<MockBlob>(uuid), std::move(blob));
+    mojo::MakeStrongBinding(std::make_unique<MockBlob>(uuid), std::move(blob));
     std::move(callback).Run();
   }
 
@@ -109,7 +109,7 @@
                        const String& uuid,
                        GetBlobFromUUIDCallback callback) override {
     binding_requests.push_back(BindingRequest{uuid});
-    mojo::MakeStrongBinding(WTF::MakeUnique<MockBlob>(uuid), std::move(blob));
+    mojo::MakeStrongBinding(std::make_unique<MockBlob>(uuid), std::move(blob));
     std::move(callback).Run();
   }
 
@@ -158,7 +158,7 @@
  public:
   explicit MojoBlobTestPlatform(BlobRegistry* mock_registry)
       : interface_provider_(
-            WTF::MakeUnique<MojoBlobInterfaceProvider>(mock_registry)) {}
+            std::make_unique<MojoBlobInterfaceProvider>(mock_registry)) {}
 
   InterfaceProvider* GetInterfaceProvider() override {
     return interface_provider_.get();
diff --git a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
index e6e6afd..1cbcf849 100644
--- a/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/GraphicsLayer.cpp
@@ -489,7 +489,7 @@
 
 CompositedLayerRasterInvalidator& GraphicsLayer::EnsureRasterInvalidator() {
   if (!raster_invalidator_) {
-    raster_invalidator_ = WTF::MakeUnique<CompositedLayerRasterInvalidator>(
+    raster_invalidator_ = std::make_unique<CompositedLayerRasterInvalidator>(
         [this](const IntRect& r) { SetNeedsDisplayInRectInternal(r); });
     raster_invalidator_->SetTracksRasterInvalidations(
         client_->IsTrackingRasterInvalidations());
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/CompositedLayerRasterInvalidator.cpp b/third_party/WebKit/Source/platform/graphics/compositing/CompositedLayerRasterInvalidator.cpp
index 5bb5753..137b27b 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/CompositedLayerRasterInvalidator.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/CompositedLayerRasterInvalidator.cpp
@@ -4,6 +4,10 @@
 
 #include "platform/graphics/compositing/CompositedLayerRasterInvalidator.h"
 
+#include <algorithm>
+#include <memory>
+#include <utility>
+
 #include "platform/graphics/paint/GeometryMapper.h"
 
 namespace blink {
@@ -222,7 +226,7 @@
 
 RasterInvalidationTracking& CompositedLayerRasterInvalidator::EnsureTracking() {
   if (!tracking_info_)
-    tracking_info_ = WTF::MakeUnique<RasterInvalidationTrackingInfo>();
+    tracking_info_ = std::make_unique<RasterInvalidationTrackingInfo>();
   return tracking_info_->tracking;
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp
index 0755547..4708cac 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferSoftwareRenderingTest.cpp
@@ -25,9 +25,9 @@
  protected:
   void SetUp() override {
     IntSize initial_size(kInitialWidth, kInitialHeight);
-    auto gl = WTF::MakeUnique<GLES2InterfaceForTests>();
+    auto gl = std::make_unique<GLES2InterfaceForTests>();
     auto provider =
-        WTF::MakeUnique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+        std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
     GLES2InterfaceForTests* gl_ =
         static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
     bool gpu_compositing = false;
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
index d2b3369..ce54bf3 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBufferTest.cpp
@@ -73,9 +73,9 @@
 
   void Init(UseMultisampling use_multisampling) {
     IntSize initial_size(kInitialWidth, kInitialHeight);
-    auto gl = WTF::MakeUnique<GLES2InterfaceForTests>();
+    auto gl = std::make_unique<GLES2InterfaceForTests>();
     auto provider =
-        WTF::MakeUnique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+        std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
     GLES2InterfaceForTests* gl_ =
         static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
     bool gpu_compositing = true;
@@ -400,9 +400,9 @@
     platform_.reset(new ScopedTestingPlatformSupport<FakePlatformSupport>);
 
     IntSize initial_size(kInitialWidth, kInitialHeight);
-    auto gl = WTF::MakeUnique<GLES2InterfaceForTests>();
+    auto gl = std::make_unique<GLES2InterfaceForTests>();
     auto provider =
-        WTF::MakeUnique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+        std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
     GLES2InterfaceForTests* gl_ =
         static_cast<GLES2InterfaceForTests*>(provider->ContextGL());
     image_id0_ = gl_->NextImageIdToBeCreated();
@@ -648,10 +648,10 @@
 
   for (size_t i = 0; i < WTF_ARRAY_LENGTH(cases); i++) {
     SCOPED_TRACE(cases[i].test_case_name);
-    auto gl = WTF::MakeUnique<DepthStencilTrackingGLES2Interface>();
+    auto gl = std::make_unique<DepthStencilTrackingGLES2Interface>();
     DepthStencilTrackingGLES2Interface* tracking_gl = gl.get();
     auto provider =
-        WTF::MakeUnique<WebGraphicsContext3DProviderForTests>(std::move(gl));
+        std::make_unique<WebGraphicsContext3DProviderForTests>(std::move(gl));
     DrawingBuffer::PreserveDrawingBuffer preserve = DrawingBuffer::kPreserve;
 
     bool premultiplied_alpha = false;
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.cpp b/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.cpp
index 26d6f07c..cc9585e 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.cpp
@@ -67,7 +67,7 @@
       Platform::Current()->CreateOffscreenGraphicsContext3DProvider(
           context_attributes, WebURL(), nullptr, &graphics_info);
   if (context_provider) {
-    *wrapper = WTF::MakeUnique<WebGraphicsContext3DProviderWrapper>(
+    *wrapper = std::make_unique<WebGraphicsContext3DProviderWrapper>(
         std::move(context_provider));
   }
   waitable_event->Signal();
@@ -100,7 +100,7 @@
         context_provider_factory_.Run(&is_gpu_compositing_disabled_);
     if (context_provider) {
       context_provider_wrapper_ =
-          WTF::MakeUnique<WebGraphicsContext3DProviderWrapper>(
+          std::make_unique<WebGraphicsContext3DProviderWrapper>(
               std::move(context_provider));
     }
   } else if (IsMainThread()) {
@@ -112,7 +112,7 @@
         Platform::Current()->CreateSharedOffscreenGraphicsContext3DProvider();
     if (context_provider) {
       context_provider_wrapper_ =
-          WTF::MakeUnique<WebGraphicsContext3DProviderWrapper>(
+          std::make_unique<WebGraphicsContext3DProviderWrapper>(
               std::move(context_provider));
     }
   } else {
diff --git a/third_party/WebKit/Source/platform/heap/Heap.cpp b/third_party/WebKit/Source/platform/heap/Heap.cpp
index dd44dfe..6340fe0 100644
--- a/third_party/WebKit/Source/platform/heap/Heap.cpp
+++ b/third_party/WebKit/Source/platform/heap/Heap.cpp
@@ -148,7 +148,7 @@
 
 ThreadHeap::ThreadHeap(ThreadState* thread_state)
     : thread_state_(thread_state),
-      region_tree_(WTF::MakeUnique<RegionTree>()),
+      region_tree_(std::make_unique<RegionTree>()),
       heap_does_not_contain_cache_(
           WTF::WrapUnique(new HeapDoesNotContainCache)),
       free_page_pool_(WTF::WrapUnique(new PagePool)),
diff --git a/third_party/WebKit/Source/platform/heap/HeapTest.cpp b/third_party/WebKit/Source/platform/heap/HeapTest.cpp
index 93692e3..99bf5dd8 100644
--- a/third_party/WebKit/Source/platform/heap/HeapTest.cpp
+++ b/third_party/WebKit/Source/platform/heap/HeapTest.cpp
@@ -1202,7 +1202,7 @@
     ObserverMap::AddResult result = map.insert(&target, nullptr);
     if (result.is_new_entry) {
       result.stored_value->value =
-          WTF::MakeUnique<FinalizationObserverWithHashMap>(target);
+          std::make_unique<FinalizationObserverWithHashMap>(target);
     } else {
       DCHECK(result.stored_value->value);
     }
@@ -6367,7 +6367,7 @@
 TEST(HeapTest, WeakPersistent) {
   Persistent<IntWrapper> object = new IntWrapper(20);
   std::unique_ptr<WeakPersistentHolder> holder =
-      WTF::MakeUnique<WeakPersistentHolder>(object);
+      std::make_unique<WeakPersistentHolder>(object);
   PreciselyCollectGarbage();
   EXPECT_TRUE(holder->Object());
   object = nullptr;
diff --git a/third_party/WebKit/Source/platform/heap/SparseHeapBitmap.cpp b/third_party/WebKit/Source/platform/heap/SparseHeapBitmap.cpp
index eb947b9..72216bc 100644
--- a/third_party/WebKit/Source/platform/heap/SparseHeapBitmap.cpp
+++ b/third_party/WebKit/Source/platform/heap/SparseHeapBitmap.cpp
@@ -95,7 +95,7 @@
 
 void SparseHeapBitmap::CreateBitmap() {
   DCHECK(!bitmap_ && size() == 1);
-  bitmap_ = WTF::MakeUnique<std::bitset<kBitmapChunkSize>>();
+  bitmap_ = std::make_unique<std::bitset<kBitmapChunkSize>>();
   size_ = kBitmapChunkRange;
   bitmap_->set(0);
 }
diff --git a/third_party/WebKit/Source/platform/heap/ThreadState.cpp b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
index 5a8582c..e68c4ab 100644
--- a/third_party/WebKit/Source/platform/heap/ThreadState.cpp
+++ b/third_party/WebKit/Source/platform/heap/ThreadState.cpp
@@ -104,7 +104,7 @@
 
 ThreadState::ThreadState()
     : thread_(CurrentThread()),
-      persistent_region_(WTF::MakeUnique<PersistentRegion>()),
+      persistent_region_(std::make_unique<PersistentRegion>()),
       start_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
       end_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
       safe_point_scope_marker_(nullptr),
diff --git a/third_party/WebKit/Source/platform/heap/Visitor.cpp b/third_party/WebKit/Source/platform/heap/Visitor.cpp
index fe347a3..61462f64 100644
--- a/third_party/WebKit/Source/platform/heap/Visitor.cpp
+++ b/third_party/WebKit/Source/platform/heap/Visitor.cpp
@@ -13,7 +13,7 @@
 namespace blink {
 
 std::unique_ptr<Visitor> Visitor::Create(ThreadState* state, MarkingMode mode) {
-  return WTF::MakeUnique<Visitor>(state, mode);
+  return std::make_unique<Visitor>(state, mode);
 }
 
 Visitor::Visitor(ThreadState* state, MarkingMode marking_mode)
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
index 1879171..90193853 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp
@@ -262,7 +262,7 @@
     return;
 
   if (!reader_) {
-    reader_ = WTF::MakeUnique<GIFImageReader>(this);
+    reader_ = std::make_unique<GIFImageReader>(this);
     reader_->SetData(data_);
   }
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
index 3f5ad37..620e0edc 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
@@ -347,7 +347,7 @@
     if (!IsDataSizeDefined() || !IsHeaderDefined())
       return true;
 
-    lzw_context_ = WTF::MakeUnique<GIFLZWContext>(client, this);
+    lzw_context_ = std::make_unique<GIFLZWContext>(client, this);
     if (!lzw_context_->PrepareToDecode()) {
       lzw_context_.reset();
       return false;
diff --git a/third_party/WebKit/Source/platform/loader/fetch/BufferingDataPipeWriterTest.cpp b/third_party/WebKit/Source/platform/loader/fetch/BufferingDataPipeWriterTest.cpp
index 6d6e3c91..b1143ab 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/BufferingDataPipeWriterTest.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/BufferingDataPipeWriterTest.cpp
@@ -40,7 +40,7 @@
   for (size_t i = 0; i < total; ++i)
     input.push_back(static_cast<char>(engine() % 26 + 'A'));
 
-  auto writer = WTF::MakeUnique<BufferingDataPipeWriter>(
+  auto writer = std::make_unique<BufferingDataPipeWriter>(
       std::move(producer), platform->CurrentThread()->GetWebTaskRunner());
 
   for (size_t i = 0; i < total;) {
diff --git a/third_party/WebKit/Source/platform/loader/fetch/FetchParameters.cpp b/third_party/WebKit/Source/platform/loader/fetch/FetchParameters.cpp
index 8b1b0d7..11b09e6d 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/FetchParameters.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/FetchParameters.cpp
@@ -25,6 +25,8 @@
 
 #include "platform/loader/fetch/FetchParameters.h"
 
+#include <memory>
+
 #include "platform/loader/fetch/ResourceFetcher.h"
 #include "platform/weborigin/KURL.h"
 #include "platform/weborigin/SecurityOrigin.h"
@@ -151,7 +153,7 @@
 
 std::unique_ptr<CrossThreadFetchParametersData> FetchParameters::CopyData()
     const {
-  auto data = WTF::MakeUnique<CrossThreadFetchParametersData>();
+  auto data = std::make_unique<CrossThreadFetchParametersData>();
   data->resource_request = resource_request_.CopyData();
   data->decoder_options = decoder_options_;
   data->options = CrossThreadResourceLoaderOptionsData(options_);
diff --git a/third_party/WebKit/Source/platform/loader/fetch/RawResource.cpp b/third_party/WebKit/Source/platform/loader/fetch/RawResource.cpp
index 63bccc6..86b37442 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/RawResource.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/RawResource.cpp
@@ -299,7 +299,7 @@
 
   data_consumer_handle_ =
       Platform::Current()->CreateDataConsumerHandle(std::move(consumer));
-  data_pipe_writer_ = WTF::MakeUnique<BufferingDataPipeWriter>(
+  data_pipe_writer_ = std::make_unique<BufferingDataPipeWriter>(
       std::move(producer), task_runner);
 
   if (Data()) {
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceRequest.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceRequest.cpp
index 7d33b129..d5537605 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceRequest.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceRequest.cpp
@@ -130,7 +130,7 @@
 std::unique_ptr<CrossThreadResourceRequestData> ResourceRequest::CopyData()
     const {
   std::unique_ptr<CrossThreadResourceRequestData> data =
-      WTF::MakeUnique<CrossThreadResourceRequestData>();
+      std::make_unique<CrossThreadResourceRequestData>();
   data->url_ = Url().Copy();
   data->timeout_interval_ = TimeoutInterval();
   data->site_for_cookies_ = SiteForCookies().Copy();
diff --git a/third_party/WebKit/Source/platform/threading/BackgroundTaskRunnerTest.cpp b/third_party/WebKit/Source/platform/threading/BackgroundTaskRunnerTest.cpp
index f2e525238..3e2f3d70 100644
--- a/third_party/WebKit/Source/platform/threading/BackgroundTaskRunnerTest.cpp
+++ b/third_party/WebKit/Source/platform/threading/BackgroundTaskRunnerTest.cpp
@@ -24,7 +24,7 @@
 
 TEST(BackgroundTaskRunnerTest, RunOnBackgroundThread) {
   base::test::ScopedTaskEnvironment scoped_task_environment;
-  std::unique_ptr<WaitableEvent> done_event = WTF::MakeUnique<WaitableEvent>();
+  std::unique_ptr<WaitableEvent> done_event = std::make_unique<WaitableEvent>();
   BackgroundTaskRunner::PostOnBackgroundThread(
       BLINK_FROM_HERE,
       CrossThreadBind(&PingPongTask, CrossThreadUnretained(done_event.get())));
diff --git a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h
index aa376da7..78d85c11 100644
--- a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h
+++ b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h
@@ -76,11 +76,11 @@
 #endif
 
   static std::unique_ptr<TransformationMatrix> Create() {
-    return WTF::MakeUnique<TransformationMatrix>();
+    return std::make_unique<TransformationMatrix>();
   }
   static std::unique_ptr<TransformationMatrix> Create(
       const TransformationMatrix& t) {
-    return WTF::MakeUnique<TransformationMatrix>(t);
+    return std::make_unique<TransformationMatrix>(t);
   }
   static std::unique_ptr<TransformationMatrix> Create(double a,
                                                       double b,
@@ -88,7 +88,7 @@
                                                       double d,
                                                       double e,
                                                       double f) {
-    return WTF::MakeUnique<TransformationMatrix>(a, b, c, d, e, f);
+    return std::make_unique<TransformationMatrix>(a, b, c, d, e, f);
   }
   static std::unique_ptr<TransformationMatrix> Create(double m11,
                                                       double m12,
diff --git a/tools/cfi/blacklist.txt b/tools/cfi/blacklist.txt
index e1f2a22..d903f7e6 100644
--- a/tools/cfi/blacklist.txt
+++ b/tools/cfi/blacklist.txt
@@ -181,6 +181,9 @@
 # net/cert/cert_verify_proc_nss.cc
 fun:*CertVerifyProcNSS*VerifyInternalImpl*
 
+# third_party/skia/include/gpu/gl/GrGLFunctions.h
+fun:*GrGLFunction*
+
 ######### Function pointers cast to incorrect type signatures
 
 # libicu is currently compiled such that in libicu the 'UChar' type is a
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 0bd3b8bb..7bb024c 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
   CLANG_REVISION = 'HEAD'
 
 # This is incremented when pushing a new build of Clang at the same revision.
-CLANG_SUB_REVISION=2
+CLANG_SUB_REVISION=3
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
@@ -799,7 +799,7 @@
       for f in glob.glob(os.path.join(toolchain_dir, 'include/c++/*/unwind.h')):
         os.remove(f)
 
-      # Build ASan runtime for Android in a separate build tree.
+      # Build sanitizer runtimes for Android in a separate build tree.
       build_dir = os.path.join(LLVM_BUILD_DIR, 'android-' + target_arch)
       if not os.path.exists(build_dir):
         os.mkdir(os.path.join(build_dir))
@@ -817,13 +817,11 @@
         '-DANDROID=1']
       RmCmakeCache('.')
       RunCommand(['cmake'] + android_args + [COMPILER_RT_DIR])
-      RunCommand(['ninja', 'libclang_rt.asan-%s-android.so' % target_arch])
+      RunCommand(['ninja', 'asan', 'ubsan'])
 
-      # And copy it into the main build tree.
-      runtime = 'libclang_rt.asan-%s-android.so' % target_arch
-      for root, _, files in os.walk(build_dir):
-        if runtime in files:
-          shutil.copy(os.path.join(root, runtime), asan_rt_lib_dst_dir)
+      # And copy them into the main build tree.
+      for f in glob.glob(os.path.join(build_dir, 'lib/linux/*.so')):
+        shutil.copy(f, asan_rt_lib_dst_dir)
 
   # Run tests.
   if args.run_tests or use_head_revision:
diff --git a/tools/perf/page_sets/media_cases.py b/tools/perf/page_sets/media_cases.py
index cd4b97267..8ef404a 100644
--- a/tools/perf/page_sets/media_cases.py
+++ b/tools/perf/page_sets/media_cases.py
@@ -90,7 +90,6 @@
                traffic_setting=traffic_setting_module.NONE):
     tags.append('beginning_to_end')
     tags.append('src')
-    self.add_browser_metrics = True
     super(_BeginningToEndPlayPage, self).__init__(
         url, page_set, tags, extra_browser_args,
         traffic_setting=traffic_setting)
@@ -112,7 +111,6 @@
                traffic_setting=traffic_setting_module.NONE):
     tags.append('seek')
     tags.append('src')
-    self.skip_basic_metrics = True
     self._action_timeout = action_timeout_in_seconds
     super(_SeekPage, self).__init__(
         url, page_set, tags, extra_browser_args,
@@ -146,7 +144,6 @@
                background_time=10,
                traffic_setting=traffic_setting_module.NONE):
     self._background_time = background_time
-    self.skip_basic_metrics = True
     tags.append('background')
     tags.append('src')
     # disable-media-suspend is required since for Android background playback
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index f03bba2..2024d6fe 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -25,6 +25,8 @@
     "app_list_model_observer.h",
     "app_list_switches.cc",
     "app_list_switches.h",
+    "app_list_util.cc",
+    "app_list_util.h",
     "app_list_view_delegate.h",
     "folder_image.cc",
     "folder_image.h",
diff --git a/ui/app_list/app_list_util.cc b/ui/app_list/app_list_util.cc
new file mode 100644
index 0000000..ffd0671
--- /dev/null
+++ b/ui/app_list/app_list_util.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/app_list/app_list_util.h"
+
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/focus/focus_manager.h"
+
+namespace app_list {
+
+bool CanProcessLeftRightKeyTraversal(const ui::KeyEvent& event) {
+  if (event.handled() || event.type() != ui::ET_KEY_PRESSED)
+    return false;
+
+  if (event.key_code() != ui::VKEY_LEFT && event.key_code() != ui::VKEY_RIGHT)
+    return false;
+
+  if (event.IsShiftDown() || event.IsControlDown() || event.IsAltDown())
+    return false;
+
+  return true;
+}
+
+bool CanProcessUpDownKeyTraversal(const ui::KeyEvent& event) {
+  if (event.handled() || event.type() != ui::ET_KEY_PRESSED)
+    return false;
+
+  if (event.key_code() != ui::VKEY_UP && event.key_code() != ui::VKEY_DOWN)
+    return false;
+
+  if (event.IsShiftDown() || event.IsControlDown() || event.IsAltDown())
+    return false;
+
+  return true;
+}
+
+bool ProcessLeftRightKeyTraversalForTextfield(views::Textfield* textfield,
+                                              const ui::KeyEvent& key_event) {
+  DCHECK(CanProcessLeftRightKeyTraversal(key_event));
+
+  const bool move_focus_reverse = base::i18n::IsRTL()
+                                      ? key_event.key_code() == ui::VKEY_RIGHT
+                                      : key_event.key_code() == ui::VKEY_LEFT;
+  if (textfield->text().empty()) {
+    textfield->GetFocusManager()->AdvanceFocus(move_focus_reverse);
+    return true;
+  }
+
+  if (textfield->HasSelection())
+    return false;
+
+  if (textfield->GetCursorPosition() != 0 &&
+      textfield->GetCursorPosition() != textfield->text().length()) {
+    return false;
+  }
+
+  // For RTL language, the beginning position of the cursor will be at the right
+  // side and it grows towards left as we are typing.
+  const bool text_rtl =
+      textfield->GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
+  const bool cursor_at_beginning = textfield->GetCursorPosition() == 0;
+  const bool move_cursor_reverse =
+      (text_rtl && key_event.key_code() == ui::VKEY_RIGHT) ||
+      (!text_rtl && key_event.key_code() == ui::VKEY_LEFT);
+
+  if ((cursor_at_beginning && !move_cursor_reverse) ||
+      (!cursor_at_beginning && move_cursor_reverse)) {
+    // Cursor is at either the beginning or the end of the textfield, and it
+    // will move inward.
+    return false;
+  }
+
+  // Move focus outside the textfield.
+  textfield->GetFocusManager()->AdvanceFocus(move_focus_reverse);
+  return true;
+}
+
+}  // namespace app_list
diff --git a/ui/app_list/app_list_util.h b/ui/app_list/app_list_util.h
new file mode 100644
index 0000000..377142d
--- /dev/null
+++ b/ui/app_list/app_list_util.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_APP_LIST_APP_LIST_UTIL_H_
+#define UI_APP_LIST_APP_LIST_UTIL_H_
+
+#include "ui/app_list/app_list_export.h"
+#include "ui/events/event.h"
+
+namespace views {
+class Textfield;
+}
+
+namespace app_list {
+
+// Returns true if the key event can be handled to do left or right focus
+// traversal.
+APP_LIST_EXPORT bool CanProcessLeftRightKeyTraversal(const ui::KeyEvent& event);
+
+// Returns true if the key event can be handled to do up or down focus
+// traversal.
+APP_LIST_EXPORT bool CanProcessUpDownKeyTraversal(const ui::KeyEvent& event);
+
+// Processes left/right key traversal for the given Textfield. Returns true
+// if focus is moved.
+APP_LIST_EXPORT bool ProcessLeftRightKeyTraversalForTextfield(
+    views::Textfield* textfield,
+    const ui::KeyEvent& key_event);
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_APP_LIST_UTIL_H_
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 20f3097..e46eaa3 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -21,6 +21,7 @@
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_model.h"
+#include "ui/app_list/app_list_util.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/speech_ui_model.h"
 #include "ui/app_list/views/app_list_folder_view.h"
@@ -1131,29 +1132,7 @@
 }
 
 void AppListView::OnKeyEvent(ui::KeyEvent* event) {
-  views::Textfield* search_box = search_box_view_->search_box();
-  bool is_search_box_focused = search_box->HasFocus();
-  bool is_folder_header_view_focused = app_list_main_view_->contents_view()
-                                           ->apps_container_view()
-                                           ->app_list_folder_view()
-                                           ->folder_header_view()
-                                           ->HasTextFocus();
-  if (is_app_list_focus_enabled_ && !is_search_box_focused &&
-      !is_folder_header_view_focused && !SearchBoxView::IsArrowKey(*event)) {
-    views::Textfield* search_box = search_box_view_->search_box();
-    // Redirect key event to |search_box_|.
-    search_box->OnKeyEvent(event);
-    if (event->handled()) {
-      // Set search box focused if the key event is consumed.
-      search_box->RequestFocus();
-      return;
-    }
-    if (event->type() == ui::ET_KEY_PRESSED) {
-      // Insert it into search box if the key event is a character. Released
-      // key should not be handled to prevent inserting duplicate character.
-      search_box->InsertChar(*event);
-    }
-  }
+  RedirectKeyEventToSearchBox(event);
 }
 
 void AppListView::OnTabletModeChanged(bool started) {
@@ -1401,6 +1380,47 @@
   Layout();
 }
 
+void AppListView::RedirectKeyEventToSearchBox(ui::KeyEvent* event) {
+  if (!is_app_list_focus_enabled_)
+    return;
+
+  if (event->handled())
+    return;
+
+  views::Textfield* search_box = search_box_view_->search_box();
+  const bool is_search_box_focused = search_box->HasFocus();
+  const bool is_folder_header_view_focused =
+      app_list_main_view_->contents_view()
+          ->apps_container_view()
+          ->app_list_folder_view()
+          ->folder_header_view()
+          ->HasTextFocus();
+  if (is_search_box_focused || is_folder_header_view_focused) {
+    // Do not redirect the key event to the |search_box_| when focus is on a
+    // text field.
+    return;
+  }
+
+  if (CanProcessLeftRightKeyTraversal(*event) ||
+      CanProcessUpDownKeyTraversal(*event)) {
+    // Do not redirect the arrow keys that are used to do focus traversal.
+    return;
+  }
+
+  // Redirect key event to |search_box_|.
+  search_box->OnKeyEvent(event);
+  if (event->handled()) {
+    // Set search box focused if the key event is consumed.
+    search_box->RequestFocus();
+    return;
+  }
+  if (event->type() == ui::ET_KEY_PRESSED) {
+    // Insert it into search box if the key event is a character. Released
+    // key should not be handled to prevent inserting duplicate character.
+    search_box->InsertChar(*event);
+  }
+}
+
 void AppListView::OnSpeechRecognitionStateChanged(
     SpeechRecognitionState new_state) {
   if (!speech_view_)
diff --git a/ui/app_list/views/app_list_view.h b/ui/app_list/views/app_list_view.h
index ba117f96..2896da07 100644
--- a/ui/app_list/views/app_list_view.h
+++ b/ui/app_list/views/app_list_view.h
@@ -189,6 +189,11 @@
   // Layouts the app list during dragging.
   void DraggingLayout();
 
+  // The search box cannot actively listen to all key events. To control and
+  // input into the search box when it does not have focus, we need to redirect
+  // necessary key events to the search box.
+  void RedirectKeyEventToSearchBox(ui::KeyEvent* event);
+
   // Sets |is_in_drag_| and updates the visibility of app list items.
   void SetIsInDrag(bool is_in_drag);
 
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc
index 92a61c5..498097e 100644
--- a/ui/app_list/views/app_list_view_unittest.cc
+++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -177,7 +177,8 @@
 
 // TODO(weidongg/766807) Remove all old focus tests after the flag is enabled
 // by default.
-class AppListViewFocusTest : public views::ViewsTestBase {
+class AppListViewFocusTest : public views::ViewsTestBase,
+                             public testing::WithParamInterface<bool> {
  public:
   AppListViewFocusTest() = default;
   ~AppListViewFocusTest() override = default;
@@ -186,6 +187,13 @@
   void SetUp() override {
     views::ViewsTestBase::SetUp();
 
+    // Setup right to left environment if necessary.
+    if (testing::UnitTest::GetInstance()->current_test_info()->value_param()) {
+      is_rtl_ = GetParam();
+      if (is_rtl_)
+        base::i18n::SetICUDefaultLocale("he");
+    }
+
     // Enable app list focus.
     scoped_feature_list_.InitAndEnableFeature(features::kEnableAppListFocus);
 
@@ -296,6 +304,111 @@
     }
   }
 
+  // Test the behavior triggered by left and right key when focus is on the
+  // |textfield|. |text_rtl| indicates whether to type RTL or non-RTL text in
+  // the |textfield| during the test.
+  void TestLeftAndRightKeyOnTextfield(views::Textfield* textfield,
+                                      bool text_rtl) {
+    EXPECT_TRUE(textfield->text().empty());
+    EXPECT_EQ(textfield, focused_view());
+
+    views::View* next_view =
+        view_->GetWidget()->GetFocusManager()->GetNextFocusableView(
+            textfield, view_->GetWidget(), false, false);
+    views::View* prev_view =
+        view_->GetWidget()->GetFocusManager()->GetNextFocusableView(
+            textfield, view_->GetWidget(), true, false);
+
+    // Only need to hit left or right key once to move focus outside the
+    // textfield when it is empty.
+    SimulateKeyPress(ui::VKEY_RIGHT, false);
+    EXPECT_EQ(is_rtl_ ? prev_view : next_view, focused_view());
+
+    SimulateKeyPress(ui::VKEY_LEFT, false);
+    EXPECT_EQ(textfield, focused_view());
+
+    SimulateKeyPress(ui::VKEY_LEFT, false);
+    EXPECT_EQ(is_rtl_ ? next_view : prev_view, focused_view());
+
+    SimulateKeyPress(ui::VKEY_RIGHT, false);
+    EXPECT_EQ(textfield, focused_view());
+
+    // Type something in textfield.
+    base::string16 text =
+        text_rtl
+            // Arabic word of "test".
+            ? base::UTF8ToUTF16(
+                  "\xd8\xa7\xd8\xae\xd8\xaa\xd8\xa8\xd8\xa7\xd8\xb1")
+            : base::UTF8ToUTF16("test");
+    textfield->InsertText(text);
+    next_view = view_->GetWidget()->GetFocusManager()->GetNextFocusableView(
+        textfield, view_->GetWidget(), false, false);
+    prev_view = view_->GetWidget()->GetFocusManager()->GetNextFocusableView(
+        textfield, view_->GetWidget(), true, false);
+    EXPECT_EQ(text.length(), textfield->GetCursorPosition());
+    EXPECT_FALSE(textfield->HasSelection());
+    EXPECT_EQ(textfield, focused_view());
+
+    const ui::KeyboardCode backward_key =
+        text_rtl ? ui::VKEY_RIGHT : ui::VKEY_LEFT;
+    const ui::KeyboardCode forward_key =
+        text_rtl ? ui::VKEY_LEFT : ui::VKEY_RIGHT;
+
+    // Move cursor backward.
+    SimulateKeyPress(backward_key, false);
+    EXPECT_EQ(text.length() - 1, textfield->GetCursorPosition());
+    EXPECT_EQ(textfield, focused_view());
+
+    // Move cursor forward.
+    SimulateKeyPress(forward_key, false);
+    EXPECT_EQ(text.length(), textfield->GetCursorPosition());
+    EXPECT_EQ(textfield, focused_view());
+
+    // Hit forward key to move focus outside the textfield.
+    SimulateKeyPress(forward_key, false);
+    EXPECT_EQ((!is_rtl_ && !text_rtl) || (is_rtl_ && text_rtl) ? next_view
+                                                               : prev_view,
+              focused_view());
+
+    // Hit backward key to move focus back to textfield and select all text.
+    SimulateKeyPress(backward_key, false);
+    EXPECT_EQ(text, textfield->GetSelectedText());
+    EXPECT_EQ(textfield, focused_view());
+
+    // Hit backward key to move cursor to the beginning.
+    SimulateKeyPress(backward_key, false);
+    EXPECT_EQ(0U, textfield->GetCursorPosition());
+    EXPECT_FALSE(textfield->HasSelection());
+    EXPECT_EQ(textfield, focused_view());
+
+    // Hit backward key to move focus outside the textfield.
+    SimulateKeyPress(backward_key, false);
+    EXPECT_EQ((!is_rtl_ && !text_rtl) || (is_rtl_ && text_rtl) ? prev_view
+                                                               : next_view,
+              focused_view());
+
+    // Hit forward key to move focus back to textfield and select all text.
+    SimulateKeyPress(forward_key, false);
+    EXPECT_EQ(text, textfield->GetSelectedText());
+    EXPECT_EQ(textfield, focused_view());
+
+    // Hit forward key to move cursor to the end.
+    SimulateKeyPress(forward_key, false);
+    EXPECT_EQ(text.length(), textfield->GetCursorPosition());
+    EXPECT_FALSE(textfield->HasSelection());
+    EXPECT_EQ(textfield, focused_view());
+
+    // Hitt forward key to move focus outside the textfield.
+    SimulateKeyPress(forward_key, false);
+    EXPECT_EQ((!is_rtl_ && !text_rtl) || (is_rtl_ && text_rtl) ? next_view
+                                                               : prev_view,
+              focused_view());
+
+    // Clean up
+    textfield->SetText(base::UTF8ToUTF16(""));
+    textfield->RequestFocus();
+  }
+
   AppListView* app_list_view() { return view_; }
 
   AppListMainView* main_view() { return view_->app_list_main_view(); }
@@ -332,6 +445,9 @@
     return view_->GetWidget()->GetFocusManager()->GetFocusedView();
   }
 
+ protected:
+  bool is_rtl_ = false;
+
  private:
   AppListView* view_ = nullptr;  // Owned by native widget.
   std::unique_ptr<AppListTestViewDelegate> delegate_;
@@ -341,6 +457,10 @@
   DISALLOW_COPY_AND_ASSIGN(AppListViewFocusTest);
 };
 
+// Instantiate the Boolean which is used to toggle RTL in the parameterized
+// tests.
+INSTANTIATE_TEST_CASE_P(, AppListViewFocusTest, testing::Bool());
+
 }  // namespace
 
 // Tests that the initial focus is on search box.
@@ -350,7 +470,7 @@
 }
 
 // Tests the linear focus traversal in PEEKING state.
-TEST_F(AppListViewFocusTest, LinearFocusTraversalInPeekingState) {
+TEST_P(AppListViewFocusTest, LinearFocusTraversalInPeekingState) {
   Show();
   SetAppListState(AppListView::PEEKING);
 
@@ -369,21 +489,17 @@
   // Test traversal triggered by shift+tab.
   TestFocusTraversal(backward_view_list, ui::VKEY_TAB, true);
 
-  // Test traversal triggered by right, Left and right key are handled by
-  // search box when focus is on it, so focus will not move. Move focus to
-  // next element before testing.
-  forward_view_list.erase(forward_view_list.begin());
-  forward_view_list.front()->RequestFocus();
-  TestFocusTraversal(forward_view_list, ui::VKEY_RIGHT, false);
+  // Test traversal triggered by right.
+  TestFocusTraversal(is_rtl_ ? backward_view_list : forward_view_list,
+                     ui::VKEY_RIGHT, false);
 
   // Test traversal triggered by left.
-  backward_view_list.erase(backward_view_list.begin());
-  backward_view_list.front()->RequestFocus();
-  TestFocusTraversal(backward_view_list, ui::VKEY_LEFT, false);
+  TestFocusTraversal(is_rtl_ ? forward_view_list : backward_view_list,
+                     ui::VKEY_LEFT, false);
 }
 
 // Tests the linear focus traversal in FULLSCREEN_ALL_APPS state.
-TEST_F(AppListViewFocusTest, LinearFocusTraversalInFullscreenAllAppsState) {
+TEST_P(AppListViewFocusTest, LinearFocusTraversalInFullscreenAllAppsState) {
   Show();
   SetAppListState(AppListView::FULLSCREEN_ALL_APPS);
 
@@ -405,21 +521,17 @@
   // Test traversal triggered by shift+tab.
   TestFocusTraversal(backward_view_list, ui::VKEY_TAB, true);
 
-  // Test traversal triggered by right, Left and right key are handled by
-  // search box when focus is on it, so focus will not move. Move focus to
-  // next element before testing.
-  forward_view_list.erase(forward_view_list.begin());
-  forward_view_list.front()->RequestFocus();
-  TestFocusTraversal(forward_view_list, ui::VKEY_RIGHT, false);
+  // Test traversal triggered by right.
+  TestFocusTraversal(is_rtl_ ? backward_view_list : forward_view_list,
+                     ui::VKEY_RIGHT, false);
 
   // Test traversal triggered by left.
-  backward_view_list.erase(backward_view_list.begin());
-  backward_view_list.front()->RequestFocus();
-  TestFocusTraversal(backward_view_list, ui::VKEY_LEFT, false);
+  TestFocusTraversal(is_rtl_ ? forward_view_list : backward_view_list,
+                     ui::VKEY_LEFT, false);
 }
 
 // Tests the linear focus traversal in HALF state with opened search box.
-TEST_F(AppListViewFocusTest, LinearFocusTraversalInHalfState) {
+TEST_P(AppListViewFocusTest, LinearFocusTraversalInHalfState) {
   Show();
 
   // Type something in search box to transition to HALF state and populate
@@ -454,21 +566,24 @@
   // Test traversal triggered by shift+tab.
   TestFocusTraversal(backward_view_list, ui::VKEY_TAB, true);
 
-  // Test traversal triggered by right, Left and right key are handled by
-  // search box when focus is on it, so focus will not move. Move focus to
-  // next element before testing.
-  forward_view_list.erase(forward_view_list.begin());
-  forward_view_list.front()->RequestFocus();
-  TestFocusTraversal(forward_view_list, ui::VKEY_RIGHT, false);
+  // Test traversal triggered by right. When the search box is focused, all
+  // text are selected. Hitting right key will move the cursor to the right end
+  // and unselect the text. Hitting right key again will move the focus to the
+  // next view. Left key is handled in similar way.
+  forward_view_list.insert(forward_view_list.begin(),
+                           search_box_view()->search_box());
+  backward_view_list.insert(backward_view_list.begin(),
+                            search_box_view()->search_box());
+  TestFocusTraversal(is_rtl_ ? backward_view_list : forward_view_list,
+                     ui::VKEY_RIGHT, false);
 
   // Test traversal triggered by left.
-  backward_view_list.erase(backward_view_list.begin());
-  backward_view_list.front()->RequestFocus();
-  TestFocusTraversal(backward_view_list, ui::VKEY_LEFT, false);
+  TestFocusTraversal(is_rtl_ ? forward_view_list : backward_view_list,
+                     ui::VKEY_LEFT, false);
 }
 
 // Tests the linear focus traversal in FULLSCREEN_ALL_APPS state within folder.
-TEST_F(AppListViewFocusTest, LinearFocusTraversalInFolder) {
+TEST_P(AppListViewFocusTest, LinearFocusTraversalInFolder) {
   Show();
 
   // Transition to FULLSCREEN_ALL_APPS state and open the folder.
@@ -496,19 +611,13 @@
   // Test traversal triggered by shift+tab.
   TestFocusTraversal(backward_view_list, ui::VKEY_TAB, true);
 
-  // Test traversal triggered by right, Left and right key are handled by
-  // search box when focus is on it, so focus will not move. Move focus to
-  // non-textfield element before testing.
-  forward_view_list.erase(forward_view_list.begin(),
-                          forward_view_list.begin() + 2);
-  forward_view_list.front()->RequestFocus();
-  TestFocusTraversal(forward_view_list, ui::VKEY_RIGHT, false);
+  // Test traversal triggered by right.
+  TestFocusTraversal(is_rtl_ ? backward_view_list : forward_view_list,
+                     ui::VKEY_RIGHT, false);
 
   // Test traversal triggered by left.
-  backward_view_list.erase(backward_view_list.begin());
-  backward_view_list.erase(backward_view_list.end() - 1);
-  backward_view_list.front()->RequestFocus();
-  TestFocusTraversal(backward_view_list, ui::VKEY_LEFT, false);
+  TestFocusTraversal(is_rtl_ ? forward_view_list : backward_view_list,
+                     ui::VKEY_LEFT, false);
 }
 
 // Tests the vertical focus traversal by in PEEKING state.
@@ -882,6 +991,32 @@
   EXPECT_TRUE(search_box_view()->search_box()->HasFocus());
 }
 
+// Tests the left and right key when focus is on the textfield.
+TEST_P(AppListViewFocusTest, HittingLeftRightWhenFocusOnTextfield) {
+  Show();
+
+  // Transition to FULLSCREEN_ALL_APPS state and open the folder.
+  SetAppListState(AppListView::FULLSCREEN_ALL_APPS);
+  folder_item_view()->RequestFocus();
+  SimulateKeyPress(ui::VKEY_RETURN, false);
+
+  // Set focus on the folder name.
+  views::Textfield* folder_name_view = static_cast<views::Textfield*>(
+      app_list_folder_view()->folder_header_view()->GetFolderNameViewForTest());
+  folder_name_view->RequestFocus();
+
+  // Test folder name.
+  TestLeftAndRightKeyOnTextfield(folder_name_view, false);
+  TestLeftAndRightKeyOnTextfield(folder_name_view, true);
+
+  // Set focus on the search box.
+  search_box_view()->search_box()->RequestFocus();
+
+  // Test search box.
+  TestLeftAndRightKeyOnTextfield(search_box_view()->search_box(), false);
+  TestLeftAndRightKeyOnTextfield(search_box_view()->search_box(), true);
+}
+
 // Tests that opening the app list opens in peeking mode by default.
 TEST_F(AppListViewTest, ShowPeekingByDefault) {
   Initialize(0, false, false);
diff --git a/ui/app_list/views/apps_grid_view.cc b/ui/app_list/views/apps_grid_view.cc
index 3e11c857..6d0f532 100644
--- a/ui/app_list/views/apps_grid_view.cc
+++ b/ui/app_list/views/apps_grid_view.cc
@@ -18,6 +18,7 @@
 #include "ui/app_list/app_list_folder_item.h"
 #include "ui/app_list/app_list_item.h"
 #include "ui/app_list/app_list_switches.h"
+#include "ui/app_list/app_list_util.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/pagination_controller.h"
 #include "ui/app_list/views/app_list_drag_and_drop_host.h"
@@ -956,7 +957,7 @@
 bool AppsGridView::OnKeyPressed(const ui::KeyEvent& event) {
   if (is_app_list_focus_enabled_) {
     // Let the FocusManager handle Left/Right keys.
-    if (event.key_code() != ui::VKEY_UP && event.key_code() != ui::VKEY_DOWN)
+    if (!CanProcessUpDownKeyTraversal(event))
       return false;
 
     AppListView::AppListState state =
diff --git a/ui/app_list/views/folder_header_view.cc b/ui/app_list/views/folder_header_view.cc
index 08e8b8f..2ae63be 100644
--- a/ui/app_list/views/folder_header_view.cc
+++ b/ui/app_list/views/folder_header_view.cc
@@ -12,6 +12,7 @@
 #include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_folder_item.h"
 #include "ui/app_list/app_list_switches.h"
+#include "ui/app_list/app_list_util.h"
 #include "ui/app_list/views/app_list_folder_view.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
@@ -255,6 +256,15 @@
   Layout();
 }
 
+bool FolderHeaderView::HandleKeyEvent(views::Textfield* sender,
+                                      const ui::KeyEvent& key_event) {
+  if (!features::IsAppListFocusEnabled())
+    return false;
+  if (!CanProcessLeftRightKeyTraversal(key_event))
+    return false;
+  return ProcessLeftRightKeyTraversalForTextfield(folder_name_view_, key_event);
+}
+
 void FolderHeaderView::ButtonPressed(views::Button* sender,
                                      const ui::Event& event) {
   delegate_->NavigateBack(folder_item_, event);
diff --git a/ui/app_list/views/folder_header_view.h b/ui/app_list/views/folder_header_view.h
index cdbdcd0..89ec638 100644
--- a/ui/app_list/views/folder_header_view.h
+++ b/ui/app_list/views/folder_header_view.h
@@ -73,6 +73,8 @@
   // views::TextfieldController overrides:
   void ContentsChanged(views::Textfield* sender,
                        const base::string16& new_contents) override;
+  bool HandleKeyEvent(views::Textfield* sender,
+                      const ui::KeyEvent& key_event) override;
 
   // views::ButtonListener overrides:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
diff --git a/ui/app_list/views/search_box_view.cc b/ui/app_list/views/search_box_view.cc
index 89f50b8a..257fc23 100644
--- a/ui/app_list/views/search_box_view.cc
+++ b/ui/app_list/views/search_box_view.cc
@@ -14,6 +14,7 @@
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_switches.h"
+#include "ui/app_list/app_list_util.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/resources/grit/app_list_resources.h"
 #include "ui/app_list/search_box_model.h"
@@ -674,12 +675,9 @@
 }
 
 void SearchBoxView::OnKeyEvent(ui::KeyEvent* event) {
-  app_list_view_->OnKeyEvent(event);
+  app_list_view_->RedirectKeyEventToSearchBox(event);
 
-  if (event->handled() || event->type() != ui::ET_KEY_PRESSED)
-    return;
-
-  if (event->key_code() != ui::VKEY_UP && event->key_code() != ui::VKEY_DOWN)
+  if (!CanProcessUpDownKeyTraversal(*event))
     return;
 
   // If focus is in search box view, up key moves focus to the last element of
@@ -882,7 +880,11 @@
         SetSearchBoxActive(true);
         return true;
       }
+      return false;
     }
+
+    if (CanProcessLeftRightKeyTraversal(key_event))
+      return ProcessLeftRightKeyTraversalForTextfield(search_box_, key_event);
     return false;
   }
   // TODO(weidongg/766807) Remove everything below when the flag is enabled by
diff --git a/ui/app_list/views/search_result_page_view.cc b/ui/app_list/views/search_result_page_view.cc
index 45f5b6e..60efe86 100644
--- a/ui/app_list/views/search_result_page_view.cc
+++ b/ui/app_list/views/search_result_page_view.cc
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_features.h"
+#include "ui/app_list/app_list_util.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/views/app_list_main_view.h"
 #include "ui/app_list/views/contents_view.h"
@@ -226,11 +227,16 @@
 
 bool SearchResultPageView::OnKeyPressed(const ui::KeyEvent& event) {
   if (is_app_list_focus_enabled_) {
+    // Let the FocusManager handle Left/Right keys.
+    if (!CanProcessUpDownKeyTraversal(event))
+      return false;
+
     views::View* next_focusable_view = nullptr;
     if (event.key_code() == ui::VKEY_UP) {
       next_focusable_view = GetFocusManager()->GetNextFocusableView(
           GetFocusManager()->GetFocusedView(), GetWidget(), true, false);
-    } else if (event.key_code() == ui::VKEY_DOWN) {
+    } else {
+      DCHECK_EQ(event.key_code(), ui::VKEY_DOWN);
       next_focusable_view = GetFocusManager()->GetNextFocusableView(
           GetFocusManager()->GetFocusedView(), GetWidget(), false, false);
     }
diff --git a/ui/app_list/views/search_result_tile_item_list_view.cc b/ui/app_list/views/search_result_tile_item_list_view.cc
index 8688a28..882b737 100644
--- a/ui/app_list/views/search_result_tile_item_list_view.cc
+++ b/ui/app_list/views/search_result_tile_item_list_view.cc
@@ -9,6 +9,7 @@
 #include "base/i18n/rtl.h"
 #include "ui/app_list/app_list_constants.h"
 #include "ui/app_list/app_list_features.h"
+#include "ui/app_list/app_list_util.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/search_result.h"
 #include "ui/app_list/views/search_result_page_view.h"
@@ -195,6 +196,10 @@
 
 bool SearchResultTileItemListView::OnKeyPressed(const ui::KeyEvent& event) {
   if (features::IsAppListFocusEnabled()) {
+    // Let the FocusManager handle Left/Right keys.
+    if (!CanProcessUpDownKeyTraversal(event))
+      return false;
+
     views::View* next_focusable_view = nullptr;
 
     // Since search result tile item views have horizontal layout, hitting
@@ -209,7 +214,8 @@
         search_box_->RequestFocus();
         return true;
       }
-    } else if (event.key_code() == ui::VKEY_DOWN) {
+    } else {
+      DCHECK_EQ(event.key_code(), ui::VKEY_DOWN);
       next_focusable_view = GetFocusManager()->GetNextFocusableView(
           tile_views_.back(), GetWidget(), false, false);
     }
@@ -218,6 +224,9 @@
       next_focusable_view->RequestFocus();
       return true;
     }
+
+    // Return false to let FocusManager to handle default focus move by key
+    // events.
     return false;
   }
   // TODO(weidongg/766807) Remove everything below when the flag is enabled by
diff --git a/ui/chromeos/events/event_rewriter_chromeos.cc b/ui/chromeos/events/event_rewriter_chromeos.cc
index 4ef27cc..269d235 100644
--- a/ui/chromeos/events/event_rewriter_chromeos.cc
+++ b/ui/chromeos/events/event_rewriter_chromeos.cc
@@ -871,6 +871,26 @@
          key_event.type() == ui::ET_KEY_RELEASED);
   MutableKeyState incoming = *state;
 
+  if ((incoming.flags &
+       (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)) ==
+      (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) {
+    // Search + Alt + Arrow keys are used to move window between displays, do
+    // not do remappings on these.
+    static const KeyboardRemapping::Condition kUseExistingKeys[] = {
+        {// Alt+Left
+         ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_LEFT},
+        {// Alt+Right
+         ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_RIGHT},
+        {// Alt+Up
+         ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_UP},
+        {// Alt+Down
+         ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_DOWN}};
+    for (const auto& condition : kUseExistingKeys) {
+      if (MatchKeyboardRemapping(*state, condition))
+        return;
+    }
+  }
+
   if ((incoming.flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) ==
       (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) {
     // Allow Search to avoid rewriting extended keys.
@@ -881,13 +901,9 @@
         {// Control+Alt+Up
          ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
          ui::VKEY_UP},
-        {// Alt+Up
-         ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_UP},
         {// Control+Alt+Down
          ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
-         ui::VKEY_DOWN},
-        {// Alt+Down
-         ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_DOWN}};
+         ui::VKEY_DOWN}};
     for (const auto& condition : kAvoidRemappings) {
       if (MatchKeyboardRemapping(*state, condition)) {
         state->flags = incoming.flags & ~ui::EF_COMMAND_DOWN;
diff --git a/ui/gfx/icc_profile.cc b/ui/gfx/icc_profile.cc
index 75fdec5..8edac94 100644
--- a/ui/gfx/icc_profile.cc
+++ b/ui/gfx/icc_profile.cc
@@ -54,17 +54,6 @@
     if (!icc_profile->id_)
       icc_profile->id_ = next_unused_id_++;
 
-    // Ensure that GetColorSpace() point back to this ICCProfile.
-    gfx::ColorSpace& color_space = icc_profile->color_space_;
-    color_space.icc_profile_id_ = icc_profile->id_;
-
-    // Ensure that the GetParametricColorSpace() point back to this ICCProfile
-    // only if the parametric version is accurate.
-    if (color_space.primaries_ != ColorSpace::PrimaryID::ICC_BASED &&
-        color_space.transfer_ != ColorSpace::TransferID::ICC_BASED) {
-      icc_profile->parametric_color_space_.icc_profile_id_ = icc_profile->id_;
-    }
-
     Entry entry;
     entry.icc_profile = *icc_profile;
     id_to_icc_profile_mru_.Put(icc_profile->id_, entry);
@@ -201,76 +190,59 @@
 
 }  // namespace
 
-// static
-ICCProfile::AnalyzeResult ICCProfile::ExtractColorSpaces(
-    const std::vector<char>& data,
-    gfx::ColorSpace* parametric_color_space,
-    float* parametric_tr_fn_max_error,
-    sk_sp<SkColorSpace>* useable_sk_color_space) {
-  // Initialize the output parameters as invalid.
-  *parametric_color_space = gfx::ColorSpace();
-  *parametric_tr_fn_max_error = 0;
-  *useable_sk_color_space = nullptr;
+ICCProfile::AnalyzeResult ICCProfile::Initialize() {
+  // Start out with no parametric data.
 
   // Parse the profile and attempt to create a SkColorSpaceXform out of it.
   sk_sp<SkColorSpace> sk_srgb_color_space = SkColorSpace::MakeSRGB();
-  sk_sp<SkICC> sk_icc = SkICC::Make(data.data(), data.size());
+  sk_sp<SkICC> sk_icc = SkICC::Make(data_.data(), data_.size());
   if (!sk_icc) {
     DLOG(ERROR) << "Failed to parse ICC profile to SkICC.";
     return kICCFailedToParse;
   }
-  sk_sp<SkColorSpace> sk_icc_color_space =
-      SkColorSpace::MakeICC(data.data(), data.size());
-  if (!sk_icc_color_space) {
+  sk_color_space_ = SkColorSpace::MakeICC(data_.data(), data_.size());
+  if (!sk_color_space_) {
     DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace.";
     return kICCFailedToExtractSkColorSpace;
   }
   std::unique_ptr<SkColorSpaceXform> sk_color_space_xform =
-      SkColorSpaceXform::New(sk_srgb_color_space.get(),
-                             sk_icc_color_space.get());
+      SkColorSpaceXform::New(sk_srgb_color_space.get(), sk_color_space_.get());
   if (!sk_color_space_xform) {
     DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform.";
     return kICCFailedToCreateXform;
   }
 
-  // Because this SkColorSpace can be used to construct a transform, mark it
-  // as "useable". Mark the "best approximation" as sRGB to start.
-  *useable_sk_color_space = sk_icc_color_space;
-  *parametric_color_space = ColorSpace::CreateSRGB();
+  // Because this SkColorSpace can be used to construct a transform, we can use
+  // it to create a LUT based color transform, at the very least. If we fail to
+  // get any better approximation, we'll use sRGB as our approximation.
+  ColorSpace::CreateSRGB().GetPrimaryMatrix(&to_XYZD50_);
+  ColorSpace::CreateSRGB().GetTransferFunction(&transfer_fn_);
 
   // If our SkColorSpace representation is sRGB then return that.
-  if (SkColorSpace::Equals(sk_srgb_color_space.get(),
-                           sk_icc_color_space.get())) {
+  if (sk_color_space_->isSRGB())
     return kICCExtractedSRGBColorSpace;
-  }
 
-  // A primary matrix is required for our parametric approximation.
+  // A primary matrix is required for our parametric representations. Use it if
+  // it exists.
   SkMatrix44 to_XYZD50_matrix;
   if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) {
     DLOG(ERROR) << "Failed to extract ICC profile primary matrix.";
     return kICCFailedToExtractMatrix;
   }
+  to_XYZD50_ = to_XYZD50_matrix;
 
-  // Try to directly extract a numerical transfer function.
+  // Try to directly extract a numerical transfer function. Use it if it
+  // exists.
   SkColorSpaceTransferFn exact_tr_fn;
   if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) {
-    *parametric_color_space =
-        gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, exact_tr_fn);
+    transfer_fn_ = exact_tr_fn;
     return kICCExtractedMatrixAndAnalyticTrFn;
   }
 
-  // If we fail to get a transfer function, use the sRGB transfer function,
-  // and return false to indicate that the gfx::ColorSpace isn't accurate, but
-  // we can construct accurate LUT transforms using the underlying
-  // SkColorSpace.
-  *parametric_color_space = gfx::ColorSpace::CreateCustom(
-      to_XYZD50_matrix, ColorSpace::TransferID::IEC61966_2_1);
-
   // Attempt to fit a parametric transfer function to the table data in the
   // profile.
   SkColorSpaceTransferFn approx_tr_fn;
-  if (!SkApproximateTransferFn(sk_icc, parametric_tr_fn_max_error,
-                               &approx_tr_fn)) {
+  if (!SkApproximateTransferFn(sk_icc, &transfer_fn_error_, &approx_tr_fn)) {
     DLOG(ERROR) << "Failed approximate transfer function.";
     return kICCFailedToConvergeToApproximateTrFn;
   }
@@ -278,16 +250,15 @@
   // If this converged, but has too high error, use the sRGB transfer function
   // from above.
   const float kMaxError = 2.f / 256.f;
-  if (*parametric_tr_fn_max_error >= kMaxError) {
+  if (transfer_fn_error_ >= kMaxError) {
     DLOG(ERROR) << "Failed to accurately approximate transfer function, error: "
-                << 256.f * (*parametric_tr_fn_max_error) << "/256";
+                << 256.f * transfer_fn_error_ << "/256";
     return kICCFailedToApproximateTrFnAccurately;
   };
 
   // If the error is sufficiently low, declare that the approximation is
   // accurate.
-  *parametric_color_space =
-      gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, approx_tr_fn);
+  transfer_fn_ = approx_tr_fn;
   return kICCExtractedMatrixAndApproximatedTrFn;
 }
 
@@ -306,18 +277,6 @@
   return !(*this == other);
 }
 
-bool ICCProfile::IsValid() const {
-  switch (analyze_result_) {
-    case kICCFailedToParse:
-    case kICCFailedToExtractSkColorSpace:
-    case kICCFailedToCreateXform:
-      return false;
-    default:
-      break;
-  }
-  return true;
-}
-
 // static
 ICCProfile ICCProfile::FromData(const void* data, size_t size) {
   return FromDataWithId(data, size, 0);
@@ -346,14 +305,40 @@
   return data_;
 }
 
-const ColorSpace& ICCProfile::GetColorSpace() const {
+ColorSpace ICCProfile::GetColorSpace() const {
   g_cache.Get().TouchEntry(*this);
-  return color_space_;
+
+  if (!is_valid_)
+    return ColorSpace();
+
+  gfx::ColorSpace color_space;
+  if (is_parametric_) {
+    color_space = GetParametricColorSpace();
+    color_space.icc_profile_sk_color_space_ = sk_color_space_;
+  } else {
+    color_space.matrix_ = ColorSpace::MatrixID::RGB;
+    color_space.range_ = ColorSpace::RangeID::FULL;
+    color_space.primaries_ = ColorSpace::PrimaryID::ICC_BASED;
+    color_space.transfer_ = ColorSpace::TransferID::ICC_BASED;
+    color_space.icc_profile_id_ = id_;
+    color_space.icc_profile_sk_color_space_ = sk_color_space_;
+  }
+  return color_space;
 }
 
-const ColorSpace& ICCProfile::GetParametricColorSpace() const {
+ColorSpace ICCProfile::GetParametricColorSpace() const {
   g_cache.Get().TouchEntry(*this);
-  return parametric_color_space_;
+
+  if (!is_valid_)
+    return ColorSpace();
+
+  ColorSpace color_space =
+      sk_color_space_->isSRGB()
+          ? ColorSpace::CreateSRGB()
+          : ColorSpace::CreateCustom(to_XYZD50_, transfer_fn_);
+  if (is_parametric_)
+    color_space.icc_profile_id_ = id_;
+  return color_space;
 }
 
 // static
@@ -376,32 +361,29 @@
     return;
 
   // Parse the ICC profile
-  sk_sp<SkColorSpace> useable_sk_color_space;
-  analyze_result_ =
-      ExtractColorSpaces(data_, &parametric_color_space_,
-                         &parametric_tr_fn_error_, &useable_sk_color_space);
+  analyze_result_ = Initialize();
   switch (analyze_result_) {
     case kICCExtractedSRGBColorSpace:
     case kICCExtractedMatrixAndAnalyticTrFn:
     case kICCExtractedMatrixAndApproximatedTrFn:
       // Successfully and accurately extracted color space.
-      color_space_ = parametric_color_space_;
+      is_valid_ = true;
+      is_parametric_ = true;
       break;
     case kICCFailedToExtractRawTrFn:
     case kICCFailedToExtractMatrix:
     case kICCFailedToConvergeToApproximateTrFn:
     case kICCFailedToApproximateTrFnAccurately:
       // Successfully but extracted a color space, but it isn't accurate enough.
-      color_space_ = ColorSpace(ColorSpace::PrimaryID::ICC_BASED,
-                                ColorSpace::TransferID::ICC_BASED);
-      color_space_.icc_profile_sk_color_space_ = useable_sk_color_space;
+      is_valid_ = true;
+      is_parametric_ = false;
       break;
     case kICCFailedToParse:
     case kICCFailedToExtractSkColorSpace:
     case kICCFailedToCreateXform:
       // Can't even use this color space as a LUT.
-      DCHECK(!parametric_color_space_.IsValid());
-      color_space_ = parametric_color_space_;
+      is_valid_ = false;
+      is_parametric_ = false;
       break;
   }
 
@@ -429,7 +411,7 @@
   if (nonlinear_fit_converged) {
     UMA_HISTOGRAM_CUSTOM_COUNTS(
         "Blink.ColorSpace.Destination.NonlinearFitError",
-        static_cast<int>(parametric_tr_fn_error_ * 255), 0, 127, 16);
+        static_cast<int>(transfer_fn_error_ * 255), 0, 127, 16);
   }
 }
 
diff --git a/ui/gfx/icc_profile.h b/ui/gfx/icc_profile.h
index bfbdc4e..bb601d5 100644
--- a/ui/gfx/icc_profile.h
+++ b/ui/gfx/icc_profile.h
@@ -41,7 +41,7 @@
   bool operator!=(const ICCProfile& other) const;
 
   // Returns true if this profile was successfully parsed by SkICC.
-  bool IsValid() const;
+  bool IsValid() const { return is_valid_; }
 
 #if defined(OS_MACOSX)
   static ICCProfile FromCGColorSpace(CGColorSpaceRef cg_color_space);
@@ -52,12 +52,12 @@
 
   // Return a ColorSpace that references this ICCProfile. ColorTransforms
   // created using this ColorSpace will match this ICCProfile precisely.
-  const ColorSpace& GetColorSpace() const;
+  ColorSpace GetColorSpace() const;
 
   // Return a ColorSpace that is the best parametric approximation of this
   // ICCProfile. The resulting ColorSpace will reference this ICCProfile only
   // if the parametric approximation is almost exact.
-  const ColorSpace& GetParametricColorSpace() const;
+  ColorSpace GetParametricColorSpace() const;
 
   const std::vector<char>& GetData() const;
 
@@ -103,12 +103,7 @@
                                    size_t size,
                                    uint64_t id);
 
-  static AnalyzeResult ExtractColorSpaces(
-      const std::vector<char>& data,
-      gfx::ColorSpace* parametric_color_space,
-      float* parametric_tr_fn_max_error,
-      sk_sp<SkColorSpace>* useable_sk_color_space);
-
+  AnalyzeResult Initialize();
   void ComputeColorSpaceAndCache();
 
   // This globally identifies this ICC profile. It is used to look up this ICC
@@ -120,18 +115,27 @@
   // The result of attepting to extract a color space from the color profile.
   AnalyzeResult analyze_result_ = kICCFailedToParse;
 
-  // |color_space| always links back to this ICC profile, and its SkColorSpace
-  // is always equal to the SkColorSpace created from this ICCProfile.
-  gfx::ColorSpace color_space_;
+  // True iff we can create a valid ColorSpace (and ColorTransform) from this
+  // object. The transform may be LUT-based (using an SkColorSpaceXform to
+  // compute the lut).
+  bool is_valid_ = false;
 
-  // |parametric_color_space_| will only link back to this ICC profile if it
-  // is accurate, and its SkColorSpace will always be parametrically created.
-  gfx::ColorSpace parametric_color_space_;
+  // True iff |to_XYZD50_| and |transfer_fn_| are accurate representations of
+  // the data in this profile. In this case ColorTransforms created from this
+  // profile will be analytic and not LUT-based.
+  bool is_parametric_ = false;
+
+  // Results of Skia parsing the ICC profile data.
+  sk_sp<SkColorSpace> sk_color_space_;
+
+  // The best-fit parametric primaries and transfer function.
+  SkMatrix44 to_XYZD50_;
+  SkColorSpaceTransferFn transfer_fn_;
 
   // The L-infinity error of the parametric color space fit. This is undefined
   // unless |analyze_result_| is kICCFailedToApproximateTrFnAccurately or
   // kICCExtractedMatrixAndApproximatedTrFn.
-  float parametric_tr_fn_error_ = -1;
+  float transfer_fn_error_ = 0;
 
   FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, BT709toSRGBICC);
   FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, GetColorSpace);