diff --git a/BUILD.gn b/BUILD.gn
index b28cb49..0687919 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -165,12 +165,14 @@
       "//media/capture:capture_unittests",
       "//media/cast:cast_unittests",
       "//media/midi:midi_unittests",
+      "//media/mojo:media_mojo_unittests",
       "//mojo",
       "//mojo/common:mojo_common_unittests",
       "//mojo/edk/system:mojo_system_unittests",
       "//mojo/edk/test:mojo_public_bindings_unittests",
       "//mojo/edk/test:mojo_public_system_unittests",
       "//net:net_perftests",
+      "//services:service_unittests",
       "//services/service_manager/public/cpp",
       "//storage:storage_unittests",
       "//third_party/WebKit/Source/platform:blink_platform_unittests",
@@ -189,6 +191,7 @@
       "//ui/latency:latency_unittests",
       "//ui/touch_selection:ui_touch_selection_unittests",
       "//url/ipc:url_ipc_unittests",
+      "//v8:gn_all",
     ]
   } else {
     deps += [ "//ios:all" ]
@@ -232,6 +235,7 @@
     deps += [
       "//ui/ozone",
       "//ui/ozone:ozone_unittests",
+      "//ui/ozone/demo",
       "//ui/ozone/gl:ozone_gl_unittests",
     ]
   }
@@ -256,7 +260,9 @@
       "//base:base_junit_tests",
       "//base/android/linker:chromium_android_linker",
       "//build/android/gyp/test:hello_world",
+      "//build/android/gyp/test:hello_world",
       "//chrome/android/webapk/shell_apk:webapk",
+      "//chrome/test/vr/perf:motopho_latency_test",
       "//components/invalidation/impl:components_invalidation_impl_junit_tests",
       "//components/policy/android:components_policy_junit_tests",
       "//content/public/android:content_junit_tests",
@@ -568,13 +574,6 @@
     ]
   }
 
-  if (!is_ios) {
-    deps += [
-      "//media/mojo:media_mojo_unittests",
-      "//services:service_unittests",
-    ]
-  }
-
   if (!is_android && !is_ios) {
     deps += [ "//content/browser/bluetooth/tools:bluetooth_metrics_hash" ]
   }
@@ -659,10 +658,6 @@
     }
   }
 
-  if (use_ozone) {
-    deps += [ "//ui/ozone/demo" ]
-  }
-
   if ((is_linux && !is_chromeos && !is_chromecast) || (is_win && use_drfuzz) ||
       (use_libfuzzer && is_mac)) {
     deps += [
@@ -683,23 +678,12 @@
     deps += [ "//native_client_sdk/src:nacl_core_sdk" ]
   }
 
-  if (is_android) {
-    deps += [
-      "//build/android/gyp/test:hello_world",
-      "//chrome/test/vr/perf:motopho_latency_test",
-    ]
-  }
-
   if (is_linux && use_ozone) {
     deps += [
       "//headless",
       "//headless:headless_tests",
     ]
   }
-
-  if (!is_ios) {
-    deps += [ "//v8:gn_all" ]
-  }
 }
 
 # TODO(GYP_GONE): This target exists for compatibility with GYP, specifically
diff --git a/DEPS b/DEPS
index b4a86c70..ef204dd 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '452a7a248be8b9e190bb45570930437516f99d41',
+  'skia_revision': 'ca572f9c90e1187913423901bff783f3462478b9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'b53bc92f91bda182c9eb33b6c28e766e6fdbe702',
+  'v8_revision': '738ba5ef1cbdf3414401426469ec0542cba7abaa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -232,7 +232,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'd6b3a364ad022e96c4b5347f674abb426d69d75d', # commit position 18023
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'b2a88552d51ffeb5f3159f65fafa12cab912c4de', # commit position 18079
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 1b1de9f..7e16e84 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -356,6 +356,12 @@
     "system/date/tray_system_info.h",
     "system/devicetype_utils.cc",
     "system/devicetype_utils.h",
+    "system/display_scale/scale_detailed_view.cc",
+    "system/display_scale/scale_detailed_view.h",
+    "system/display_scale/scale_view.cc",
+    "system/display_scale/scale_view.h",
+    "system/display_scale/tray_scale.cc",
+    "system/display_scale/tray_scale.h",
     "system/enterprise/enterprise_domain_observer.h",
     "system/enterprise/tray_enterprise.cc",
     "system/enterprise/tray_enterprise.h",
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index b41d8ad..719b198 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1031,6 +1031,12 @@
                desc="Notification message to tell users that they just pressed the Search+Shift+H shortcut to toggle High Contrast Mode, and that pressing it again will toggle it off.">
         High Contrast Mode enabled. Press Search+Shift+H again to toggle it off.
       </message>
+
+      <!-- Tray scale strings -->
+      <message name="IDS_ASH_STATUS_TRAY_SCALE" desc="The label used in scale setting detailed page of ash tray popup.">
+        Display scale settings
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_SCALE_SLIDER" desc="The accessible text for the scale slider">Display scale</message>
     </messages>
   </release>
 </grit>
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 0e75f12a..c873dc4f 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -58,6 +58,10 @@
 // Enables mirrored screen.
 const char kAshEnableMirroredScreen[] = "ash-enable-mirrored-screen";
 
+// Enables display scale tray settings. This uses force-device-scale-factor flag
+// to modify the dsf of the device to any non discrete value.
+const char kAshEnableScaleSettingsTray[] = "ash-enable-scale-settings-tray";
+
 // Disables a smoother animation for screen rotation.
 const char kAshDisableSmoothScreenRotation[] =
     "ash-disable-smooth-screen-rotation";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 10f5bee..f26d1310 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -26,6 +26,7 @@
 ASH_EXPORT extern const char kAshDisableTouchExplorationMode[];
 ASH_EXPORT extern const char kAshEnableMagnifierKeyScroller[];
 ASH_EXPORT extern const char kAshEnablePaletteOnAllDisplays[];
+ASH_EXPORT extern const char kAshEnableScaleSettingsTray[];
 ASH_EXPORT extern const char kAshEnableTouchView[];
 ASH_EXPORT extern const char kAshEnableMirroredScreen[];
 ASH_EXPORT extern const char kAshEstimatedPresentationDelay[];
diff --git a/ash/system/display_scale/scale_detailed_view.cc b/ash/system/display_scale/scale_detailed_view.cc
new file mode 100644
index 0000000..d16af62
--- /dev/null
+++ b/ash/system/display_scale/scale_detailed_view.cc
@@ -0,0 +1,81 @@
+// 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/system/display_scale/scale_detailed_view.h"
+
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/tray/hover_highlight_view.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/display/display.h"
+#include "ui/display/display_switches.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/views/controls/scroll_view.h"
+
+namespace ash {
+namespace tray {
+namespace {
+
+const double scales[] = {1.0f,  1.25f, 1.33f, 1.5f,  1.66f,
+                         1.75f, 1.9f,  2.0f,  2.25f, 2.5f};
+constexpr double kEpsilon = 0.01;
+
+bool IsSameScaleFactor(double new_value) {
+  return (std::abs(display::Display::GetForcedDeviceScaleFactor() - new_value) <
+          kEpsilon);
+}
+
+}  // namespace
+
+ScaleDetailedView::ScaleDetailedView(SystemTrayItem* owner)
+    : TrayDetailsView(owner) {
+  CreateScrollableList();
+  CreateTitleRow(IDS_ASH_STATUS_TRAY_SCALE);
+  UpdateScrollableList();
+  Layout();
+}
+
+ScaleDetailedView::~ScaleDetailedView() {}
+
+HoverHighlightView* ScaleDetailedView::AddScrollListItem(
+    const base::string16& text,
+    bool highlight,
+    bool checked) {
+  HoverHighlightView* container = new HoverHighlightView(this);
+
+  container->AddLabelRow(text);
+  TrayPopupUtils::InitializeAsCheckableRow(container, checked);
+
+  scroll_content()->AddChildView(container);
+  return container;
+}
+
+void ScaleDetailedView::UpdateScrollableList() {
+  scroll_content()->RemoveAllChildViews(true);
+  view_to_scale_.clear();
+
+  for (double scale : scales) {
+    HoverHighlightView* container = AddScrollListItem(
+        base::UTF8ToUTF16(base::StringPrintf("%.2f", scale)),
+        false /* highlight */, IsSameScaleFactor(scale) /* checkmark icon */);
+    view_to_scale_[container] = scale;
+  }
+}
+
+void ScaleDetailedView::HandleViewClicked(views::View* view) {
+  // The selected dsf is already active.
+  double new_scale = view_to_scale_[view];
+  if (IsSameScaleFactor(new_scale))
+    return;
+  display::Display::SetForceDeviceScaleFactor(new_scale);
+  ash::Shell::Get()->display_manager()->UpdateDisplays();
+  UpdateScrollableList();
+  Layout();
+}
+
+}  // namespace tray
+}  // namespace ash
diff --git a/ash/system/display_scale/scale_detailed_view.h b/ash/system/display_scale/scale_detailed_view.h
new file mode 100644
index 0000000..355f0f0
--- /dev/null
+++ b/ash/system/display_scale/scale_detailed_view.h
@@ -0,0 +1,44 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_DISPLAY_SCALE_SCALE_DETAILED_VIEW_H_
+#define ASH_SYSTEM_DISPLAY_SCALE_SCALE_DETAILED_VIEW_H_
+
+#include "ash/system/tray/tray_details_view.h"
+#include "base/macros.h"
+
+namespace views {
+class View;
+}
+
+namespace ash {
+class HoverHighlightView;
+
+namespace tray {
+
+class ScaleDetailedView : public TrayDetailsView {
+ public:
+  explicit ScaleDetailedView(SystemTrayItem* owner);
+
+  ~ScaleDetailedView() override;
+
+ private:
+  HoverHighlightView* AddScrollListItem(const base::string16& text,
+                                        bool highlight,
+                                        bool checked);
+
+  void UpdateScrollableList();
+
+  // TrayDetailsView:
+  void HandleViewClicked(views::View* view) override;
+
+  std::map<views::View*, double> view_to_scale_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScaleDetailedView);
+};
+
+}  // namespace tray
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_DISPLAY_SCALE_SCALE_DETAILED_VIEW_H_
diff --git a/ash/system/display_scale/scale_view.cc b/ash/system/display_scale/scale_view.cc
new file mode 100644
index 0000000..8a80128
--- /dev/null
+++ b/ash/system/display_scale/scale_view.cc
@@ -0,0 +1,100 @@
+// 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/system/display_scale/scale_view.h"
+
+#include <algorithm>
+
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/tray/actionable_view.h"
+#include "ash/system/tray/system_tray_item.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "ash/system/tray/tri_view.h"
+#include "base/command_line.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/native_theme/native_theme.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/slider.h"
+#include "ui/views/layout/fill_layout.h"
+
+namespace ash {
+namespace tray {
+
+ScaleView::ScaleView(SystemTrayItem* owner, bool is_default_view)
+    : owner_(owner),
+      tri_view_(TrayPopupUtils::CreateMultiTargetRowView()),
+      more_button_(nullptr),
+      label_(nullptr),
+      slider_(nullptr),
+      is_default_view_(is_default_view) {
+  SetLayoutManager(new views::FillLayout);
+  AddChildView(tri_view_);
+
+  label_ = new views::Label(base::UTF8ToUTF16(base::StringPrintf(
+      "%.2f", display::Display::GetForcedDeviceScaleFactor())));
+  tri_view_->AddView(TriView::Container::START, label_);
+
+  slider_ = TrayPopupUtils::CreateSlider(this);
+  slider_->SetValue((display::Display::GetForcedDeviceScaleFactor() - 1.f) / 2);
+  slider_->SetAccessibleName(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SCALE_SLIDER));
+  tri_view_->AddView(TriView::Container::CENTER, slider_);
+
+  set_background(views::Background::CreateThemedSolidBackground(
+      this, ui::NativeTheme::kColorId_BubbleBackground));
+
+  if (!is_default_view_) {
+    tri_view_->SetContainerVisible(TriView::Container::END, false);
+    Layout();
+    return;
+  }
+
+  more_button_ = new ButtonListenerActionableView(
+      owner_, TrayPopupInkDropStyle::INSET_BOUNDS, this);
+  TrayPopupUtils::ConfigureContainer(TriView::Container::END, more_button_);
+
+  more_button_->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
+  more_button_->SetBorder(
+      views::CreateEmptyBorder(gfx::Insets(0, kTrayPopupButtonEndMargin)));
+  tri_view_->AddView(TriView::Container::END, more_button_);
+
+  more_button_->AddChildView(TrayPopupUtils::CreateMoreImageView());
+  more_button_->SetAccessibleName(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SCALE));
+  Layout();
+}
+
+ScaleView::~ScaleView() {}
+
+void ScaleView::ButtonPressed(views::Button* sender, const ui::Event& event) {
+  if (sender == more_button_)
+    owner_->TransitionDetailedView();
+}
+
+void ScaleView::SliderValueChanged(views::Slider* sender,
+                                   float value,
+                                   float old_value,
+                                   views::SliderChangeReason reason) {
+  if (reason == views::VALUE_CHANGED_BY_USER) {
+    label_->SetText(
+        base::UTF8ToUTF16(base::StringPrintf("%.2f", 1.f + value * 2.0f)));
+  }
+}
+
+void ScaleView::SliderDragEnded(views::Slider* sender) {
+  display::Display::SetForceDeviceScaleFactor(1.f + slider_->value() * 2.0f);
+  ash::Shell::Get()->display_manager()->UpdateDisplays();
+}
+
+}  // namespace tray
+}  // namespace ash
diff --git a/ash/system/display_scale/scale_view.h b/ash/system/display_scale/scale_view.h
new file mode 100644
index 0000000..9f77c8a
--- /dev/null
+++ b/ash/system/display_scale/scale_view.h
@@ -0,0 +1,57 @@
+// 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_SYSTEM_DISPLAY_SCALE_SCALE_VIEW_H_
+#define ASH_SYSTEM_DISPLAY_SCALE_SCALE_VIEW_H_
+
+#include "base/macros.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/slider.h"
+#include "ui/views/view.h"
+
+namespace views {
+class CustomButton;
+class Label;
+}
+
+namespace ash {
+class SystemTrayItem;
+class TriView;
+
+namespace tray {
+class ScaleView : public views::View,
+                  public views::SliderListener,
+                  public views::ButtonListener {
+ public:
+  ScaleView(SystemTrayItem* owner, bool is_default_view);
+
+  ~ScaleView() override;
+
+ private:
+  // SliderListener:
+  void SliderValueChanged(views::Slider* sender,
+                          float value,
+                          float old_value,
+                          views::SliderChangeReason reason) override;
+  void SliderDragEnded(views::Slider* sender) override;
+
+  // views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  SystemTrayItem* owner_;
+  // The only immediate child view of |this|. All other view elements are added
+  // to the |tri_view_| to handle layout.
+  TriView* tri_view_;
+  views::CustomButton* more_button_;
+  views::Label* label_;
+  views::Slider* slider_;
+  bool is_default_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScaleView);
+};
+
+}  // namespace tray
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_DISPLAY_SCALE_SCALE_VIEW_H_
diff --git a/ash/system/display_scale/tray_scale.cc b/ash/system/display_scale/tray_scale.cc
new file mode 100644
index 0000000..3e67189
--- /dev/null
+++ b/ash/system/display_scale/tray_scale.cc
@@ -0,0 +1,59 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/display_scale/tray_scale.h"
+
+#include "ash/ash_switches.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/root_window_controller.h"
+#include "ash/shell_port.h"
+#include "ash/system/display_scale/scale_detailed_view.h"
+#include "ash/system/display_scale/scale_view.h"
+#include "ash/system/tray/system_tray.h"
+#include "ash/system/tray/tray_constants.h"
+#include "base/command_line.h"
+#include "ui/views/view.h"
+
+namespace ash {
+namespace {
+
+bool IsDisplayScaleTrayEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kAshEnableScaleSettingsTray);
+}
+
+}  // namespace
+
+TrayScale::TrayScale(SystemTray* system_tray)
+    : SystemTrayItem(system_tray, UMA_NOT_RECORDED) {}
+
+TrayScale::~TrayScale() {}
+
+views::View* TrayScale::CreateDefaultView(LoginStatus status) {
+  if (!IsDisplayScaleTrayEnabled())
+    return nullptr;
+  scale_view_ = new tray::ScaleView(this, true);
+  return scale_view_;
+}
+
+views::View* TrayScale::CreateDetailedView(LoginStatus status) {
+  if (!IsDisplayScaleTrayEnabled())
+    return nullptr;
+  scale_detail_view_ = new tray::ScaleDetailedView(this);
+  return scale_detail_view_;
+}
+
+void TrayScale::DestroyDefaultView() {
+  scale_view_ = nullptr;
+}
+
+void TrayScale::DestroyDetailedView() {
+  scale_detail_view_ = nullptr;
+}
+
+bool TrayScale::ShouldShowShelf() const {
+  return false;
+}
+
+}  // namespace ash
diff --git a/ash/system/display_scale/tray_scale.h b/ash/system/display_scale/tray_scale.h
new file mode 100644
index 0000000..64d3277
--- /dev/null
+++ b/ash/system/display_scale/tray_scale.h
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_DISPLAY_SCALE_TRAY_SCALE_H_
+#define ASH_SYSTEM_DISPLAY_SCALE_TRAY_SCALE_H_
+
+#include <stdint.h>
+
+#include "ash/ash_export.h"
+#include "ash/system/tray/tray_image_item.h"
+#include "base/macros.h"
+
+namespace ash {
+namespace tray {
+class ScaleView;
+class ScaleDetailedView;
+}  // namespace tray
+
+// The system tray item for display scale.
+class ASH_EXPORT TrayScale : public SystemTrayItem {
+ public:
+  explicit TrayScale(SystemTray* system_tray);
+  ~TrayScale() override;
+
+ private:
+  // Overridden from SystemTrayItem.
+  views::View* CreateDefaultView(LoginStatus status) override;
+  views::View* CreateDetailedView(LoginStatus status) override;
+  void DestroyDefaultView() override;
+  void DestroyDetailedView() override;
+  bool ShouldShowShelf() const override;
+
+  tray::ScaleView* scale_view_;
+
+  tray::ScaleDetailedView* scale_detail_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(TrayScale);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_DISPLAY_SCALE_TRAY_SCALE_H_
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 80c2694..ca00a63 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -22,6 +22,7 @@
 #include "ash/system/brightness/tray_brightness.h"
 #include "ash/system/cast/tray_cast.h"
 #include "ash/system/date/tray_system_info.h"
+#include "ash/system/display_scale/tray_scale.h"
 #include "ash/system/enterprise/tray_enterprise.h"
 #include "ash/system/ime/tray_ime_chromeos.h"
 #include "ash/system/media_security/multi_profile_media_tray_item.h"
@@ -272,6 +273,8 @@
   AddTrayItem(base::MakeUnique<MultiProfileMediaTrayItem>(this));
   tray_audio_ = new TrayAudio(this);
   AddTrayItem(base::WrapUnique(tray_audio_));
+  tray_scale_ = new TrayScale(this);
+  AddTrayItem(base::WrapUnique(tray_scale_));
   AddTrayItem(base::MakeUnique<TrayBrightness>(this));
   AddTrayItem(base::MakeUnique<TrayCapsLock>(this));
   tray_night_light_ = new TrayNightLight(this);
diff --git a/ash/system/tray/system_tray.h b/ash/system/tray/system_tray.h
index bffa9e7..ce8408fe 100644
--- a/ash/system/tray/system_tray.h
+++ b/ash/system/tray/system_tray.h
@@ -30,6 +30,7 @@
 class TrayEnterprise;
 class TrayNetwork;
 class TrayNightLight;
+class TrayScale;
 class TraySupervisedUser;
 class TraySystemInfo;
 class TrayTiles;
@@ -234,6 +235,7 @@
   TrayEnterprise* tray_enterprise_ = nullptr;
   TrayNetwork* tray_network_ = nullptr;
   TrayTiles* tray_tiles_ = nullptr;
+  TrayScale* tray_scale_ = nullptr;
   TraySupervisedUser* tray_supervised_user_ = nullptr;
   TraySystemInfo* tray_system_info_ = nullptr;
   TrayUpdate* tray_update_ = nullptr;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 7ab61ccc..3ff0410b 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2144,6 +2144,7 @@
     "test/histogram_tester_unittest.cc",
     "test/mock_callback_unittest.cc",
     "test/scoped_mock_time_message_loop_task_runner_unittest.cc",
+    "test/scoped_task_environment_unittest.cc",
     "test/scoped_task_scheduler_unittest.cc",
     "test/test_pending_task_unittest.cc",
     "test/test_reg_util_win_unittest.cc",
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index af871e6a..d1c0907 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -12,6 +12,27 @@
 namespace base {
 namespace test {
 
+namespace {
+
+class TaskObserver : public MessageLoop::TaskObserver {
+ public:
+  TaskObserver() = default;
+
+  // MessageLoop::TaskObserver:
+  void WillProcessTask(const PendingTask& pending_task) override {}
+  void DidProcessTask(const PendingTask& pending_task) override {
+    ran_task_ = true;
+  }
+
+  bool ran_task() const { return ran_task_; }
+
+ private:
+  bool ran_task_ = false;
+  DISALLOW_COPY_AND_ASSIGN(TaskObserver);
+};
+
+}  // namespace
+
 ScopedTaskEnvironment::ScopedTaskEnvironment(MainThreadType main_thread_type)
     : message_loop_(main_thread_type == MainThreadType::DEFAULT
                         ? MessageLoop::TYPE_DEFAULT
@@ -45,5 +66,19 @@
   TaskScheduler::SetInstance(nullptr);
 }
 
+void ScopedTaskEnvironment::RunUntilIdle() {
+  for (;;) {
+    TaskScheduler::GetInstance()->FlushForTesting();
+
+    TaskObserver task_observer;
+    MessageLoop::current()->AddTaskObserver(&task_observer);
+    RunLoop().RunUntilIdle();
+    MessageLoop::current()->RemoveTaskObserver(&task_observer);
+
+    if (!task_observer.ran_task())
+      return;
+  }
+}
+
 }  // namespace test
 }  // namespace base
diff --git a/base/test/scoped_task_environment.h b/base/test/scoped_task_environment.h
index 04b9c75..7b7180ae 100644
--- a/base/test/scoped_task_environment.h
+++ b/base/test/scoped_task_environment.h
@@ -22,12 +22,14 @@
 // ScopedTaskEnvironment.
 //
 // Tasks posted to the (Thread|Sequenced)TaskRunnerHandle run synchronously when
-// RunLoop::Run(UntilIdle) is called on the thread where the
-// ScopedTaskEnvironment lives.
+// RunLoop::Run(UntilIdle) or ScopedTaskEnvironment::RunUntilIdle is called on
+// the thread where the ScopedTaskEnvironment lives.
 //
 // Tasks posted through base/task_scheduler/post_task.h run on dedicated threads
 // as they are posted.
 //
+// All methods of ScopedTaskEnvironment must be called from the same thread.
+//
 // Usage:
 //
 //   class MyTestFixture : public testing::Test {
@@ -66,6 +68,10 @@
   // the (Thread|Sequenced)TaskRunnerHandle.
   ~ScopedTaskEnvironment();
 
+  // Synchronously runs (Thread|Sequenced)TaskRunnerHandle tasks until no
+  // undelayed (Thread|Sequenced)TaskRunnerHandle or TaskScheduler tasks remain.
+  void RunUntilIdle();
+
  private:
   // Note: |message_loop_| is an implementation detail and will be replaced in
   // the future, do NOT rely on the presence of a MessageLoop beyond
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc
new file mode 100644
index 0000000..245cda28
--- /dev/null
+++ b/base/test/scoped_task_environment_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_task_environment.h"
+
+#include "base/bind.h"
+#include "base/synchronization/atomic_flag.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace test {
+
+namespace {
+
+void VerifyRunUntilIdleDidNotReturnAndSetFlag(
+    AtomicFlag* run_until_idle_returned,
+    AtomicFlag* task_ran) {
+  EXPECT_FALSE(run_until_idle_returned->IsSet());
+  task_ran->Set();
+}
+
+}  // namespace
+
+TEST(ScopedTaskEnvironmentTest, RunUntilIdle) {
+  AtomicFlag run_until_idle_returned;
+  ScopedTaskEnvironment scoped_task_environment;
+
+  AtomicFlag first_main_thread_task_ran;
+  ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, BindOnce(&VerifyRunUntilIdleDidNotReturnAndSetFlag,
+                          Unretained(&run_until_idle_returned),
+                          Unretained(&first_main_thread_task_ran)));
+
+  AtomicFlag first_task_scheduler_task_ran;
+  PostTask(FROM_HERE, BindOnce(&VerifyRunUntilIdleDidNotReturnAndSetFlag,
+                               Unretained(&run_until_idle_returned),
+                               Unretained(&first_task_scheduler_task_ran)));
+
+  AtomicFlag second_task_scheduler_task_ran;
+  AtomicFlag second_main_thread_task_ran;
+  PostTaskAndReply(FROM_HERE,
+                   BindOnce(&VerifyRunUntilIdleDidNotReturnAndSetFlag,
+                            Unretained(&run_until_idle_returned),
+                            Unretained(&second_task_scheduler_task_ran)),
+                   BindOnce(&VerifyRunUntilIdleDidNotReturnAndSetFlag,
+                            Unretained(&run_until_idle_returned),
+                            Unretained(&second_main_thread_task_ran)));
+
+  scoped_task_environment.RunUntilIdle();
+  run_until_idle_returned.Set();
+
+  EXPECT_TRUE(first_main_thread_task_ran.IsSet());
+  EXPECT_TRUE(first_task_scheduler_task_ran.IsSet());
+  EXPECT_TRUE(second_task_scheduler_task_ran.IsSet());
+  EXPECT_TRUE(second_main_thread_task_ran.IsSet());
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 0868486..8d307cef 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -761,7 +761,7 @@
                                   TRACE_ID_LOCAL(dump_guid));
 }
 
-void MemoryDumpManager::Enable(
+void MemoryDumpManager::SetupForTracing(
     const TraceConfig::MemoryDumpConfig& memory_dump_config) {
   scoped_refptr<HeapProfilerSerializationState>
       heap_profiler_serialization_state = new HeapProfilerSerializationState;
@@ -841,7 +841,7 @@
   }
 }
 
-void MemoryDumpManager::Disable() {
+void MemoryDumpManager::TeardownForTracing() {
   // There might be a memory dump in progress while this happens. Therefore,
   // ensure that the MDM state which depends on the tracing enabled / disabled
   // state is always accessed by the dumping methods holding the |lock_|.
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index f3580b9..8111a6a 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -132,10 +132,11 @@
   // related modes (non-SUMMARY_ONLY).
   // Initializes the peak detector, scheduler and heap profiler with the given
   // config.
-  void Enable(const TraceConfig::MemoryDumpConfig&);
+  void SetupForTracing(const TraceConfig::MemoryDumpConfig&);
 
   // Tear-down tracing related state.
-  void Disable();
+  // Non-tracing modes (e.g. SUMMARY_ONLY) will continue to work.
+  void TeardownForTracing();
 
   // NOTE: Use RequestGlobalDump() to create memory dumps. Creates a memory dump
   // for the current process and appends it to the trace. |callback| will be
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index c1e9bef..dbb16a69 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -1258,7 +1258,7 @@
   const TraceConfig::MemoryDumpConfig& memory_dump_config =
       trace_config.memory_dump_config();
 
-  mdm_->Enable(memory_dump_config);
+  mdm_->SetupForTracing(memory_dump_config);
 
   EXPECT_CALL(global_dump_handler_, RequestGlobalMemoryDump(_, _)).Times(3);
   EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3).WillRepeatedly(Return(true));
@@ -1271,7 +1271,7 @@
   // successful we also managed to add the dump to the trace.
   EXPECT_FALSE(last_callback_success_);
 
-  mdm_->Disable();
+  mdm_->TeardownForTracing();
 
   mdm_->UnregisterDumpProvider(&mdp);
 }
diff --git a/base/trace_event/memory_tracing_observer.cc b/base/trace_event/memory_tracing_observer.cc
index 8847b804..2208cedf 100644
--- a/base/trace_event/memory_tracing_observer.cc
+++ b/base/trace_event/memory_tracing_observer.cc
@@ -49,7 +49,8 @@
     return;
 
   // Initialize the TraceLog for the current thread. This is to avoids that the
-  // TraceLog memory dump provider is registered lazily during the MDM Enable()
+  // TraceLog memory dump provider is registered lazily during the MDM
+  // SetupForTracing().
   TraceLog::GetInstance()->InitializeThreadLocalEventBufferIfSupported();
 
   const TraceConfig& trace_config =
@@ -60,11 +61,11 @@
   memory_dump_config_ =
       MakeUnique<TraceConfig::MemoryDumpConfig>(memory_dump_config);
 
-  memory_dump_manager_->Enable(memory_dump_config);
+  memory_dump_manager_->SetupForTracing(memory_dump_config);
 }
 
 void MemoryTracingObserver::OnTraceLogDisabled() {
-  memory_dump_manager_->Disable();
+  memory_dump_manager_->TeardownForTracing();
   memory_dump_config_.reset();
 }
 
diff --git a/chrome/android/webapk/shell_apk/shell_apk_version.gni b/chrome/android/webapk/shell_apk/shell_apk_version.gni
index c4d66ff..2be5556 100644
--- a/chrome/android/webapk/shell_apk/shell_apk_version.gni
+++ b/chrome/android/webapk/shell_apk/shell_apk_version.gni
@@ -12,4 +12,4 @@
 # if the WebAPK's ShellAPK version is less than |expected_shell_apk_version|.
 # The version should be incremented after a new ShellAPK has been uploaded to
 # the WebAPK Minting Server.
-expected_shell_apk_version = 3
+expected_shell_apk_version = 4
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 7261a37..2bd94354 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -5661,7 +5661,7 @@
     <ph name="PROGRESS_PERCENT">$1<ex>90</ex></ph>% done
   </message>
   <message name="IDS_ENCRYPTION_MIGRATION_BATTERY_WARNING_LABEL" desc="Label to tell the user that the battery level is too low to start the migration.">
-    Battery too low for update (<ph name="PROGRESS_PERCENT">$1<ex>10</ex></ph>%)
+    Battery too low for update (<ph name="BATTERY_PERCENT">$1<ex>10</ex></ph>%)
   </message>
   <message name="IDS_ENCRYPTION_MIGRATION_ASK_CHARGE_MESSAGE" desc="Label to ask the user to charge the device.">
     Please plug your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> into a power source.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 6d45463..7cd0eeb1 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7478,6 +7478,7 @@
         <message name="IDS_OPTIONS_PASSWORDS_AUTOLOGIN" desc="The label of the 'autologinEnabled' checkbox">
           Offer to sign in to Google sites automatically with this account
         </message>
+        <!-- TODO(718146) Move autofill strings to autofill_strings.grd -->
         <message name="IDS_OPTIONS_AUTOFILL_ENABLE" desc="The label of the 'Ask me to enable Autofill' radio button">
           Enable Autofill to fill out web forms in a single click.
         </message>
@@ -7548,6 +7549,12 @@
         <message name="IDS_AUTOFILL_FIELD_LABEL_EXPIRATION_YEAR" desc="The label of the Expiration date entry.">
           Expiration year
         </message>
+        <message name="IDS_AUTOFILL_FIELD_LABEL_BILLING_ADDRESS" desc="The label of the Billing Address chooser.">
+          Billing address
+        </message>
+        <message name="IDS_AUTOFILL_BUTTON_LABEL_ADD_BILLING_ADDRESS" desc="The label of the button to add a new billing address.">
+          Add billing address
+        </message>
 
         <!-- Extension settings -->
         <message name="IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE" desc="Title that appears in the dialogue title bar for manage extensions settings">
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index 76d3c5e0..bc017613 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -104,10 +104,6 @@
       "//ui/vector_icons",
     ]
 
-    public_deps = [
-      "//device/vr:mojo_bindings",
-    ]
-
     libs = [
       "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a",
       "android",
diff --git a/chrome/browser/browsing_data/autofill_counter_browsertest.cc b/chrome/browser/browsing_data/autofill_counter_browsertest.cc
index e7a660e..561881b8 100644
--- a/chrome/browser/browsing_data/autofill_counter_browsertest.cc
+++ b/chrome/browser/browsing_data/autofill_counter_browsertest.cc
@@ -72,10 +72,11 @@
 
   void AddCreditCard(const char* card_number,
                      const char* exp_month,
-                     const char* exp_year) {
+                     const char* exp_year,
+                     const char* billing_address_id) {
     autofill::CreditCard card;
     autofill::test::SetCreditCardInfo(&card, nullptr, card_number, exp_month,
-                                      exp_year);
+                                      exp_year, billing_address_id);
     credit_card_ids_.push_back(card.guid());
     web_data_service_->AddCreditCard(card);
   }
@@ -271,17 +272,17 @@
   WaitForCounting();
   EXPECT_EQ(0, GetNumCreditCards());
 
-  AddCreditCard("0000-0000-0000-0000", "1", "2015");
+  AddCreditCard("0000-0000-0000-0000", "1", "2015", "1");
   counter.Restart();
   WaitForCounting();
   EXPECT_EQ(1, GetNumCreditCards());
 
-  AddCreditCard("0123-4567-8910-1112", "10", "2015");
+  AddCreditCard("0123-4567-8910-1112", "10", "2015", "1");
   counter.Restart();
   WaitForCounting();
   EXPECT_EQ(2, GetNumCreditCards());
 
-  AddCreditCard("1211-1098-7654-3210", "10", "2030");
+  AddCreditCard("1211-1098-7654-3210", "10", "2030", "1");
   counter.Restart();
   WaitForCounting();
   EXPECT_EQ(3, GetNumCreditCards());
@@ -343,8 +344,8 @@
   AddAutocompleteSuggestion("tel", "+987654321");
   AddAutocompleteSuggestion("city", "Munich");
 
-  AddCreditCard("0000-0000-0000-0000", "1", "2015");
-  AddCreditCard("1211-1098-7654-3210", "10", "2030");
+  AddCreditCard("0000-0000-0000-0000", "1", "2015", "1");
+  AddCreditCard("1211-1098-7654-3210", "10", "2030", "1");
 
   AddAddress("John", "Doe", "Main Street 12345");
   AddAddress("Jane", "Smith", "Main Street 12346");
@@ -370,7 +371,7 @@
   base::Time time1 = base::Time::FromTimeT(base::Time::Now().ToTimeT());
 
   AddAutocompleteSuggestion("email", "example@example.com");
-  AddCreditCard("0000-0000-0000-0000", "1", "2015");
+  AddCreditCard("0000-0000-0000-0000", "1", "2015", "1");
   AddAddress("John", "Doe", "Main Street 12345");
   WaitForDBThread();
 
@@ -378,7 +379,7 @@
   base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
   base::Time time2 = base::Time::FromTimeT(base::Time::Now().ToTimeT());
 
-  AddCreditCard("0123-4567-8910-1112", "10", "2015");
+  AddCreditCard("0123-4567-8910-1112", "10", "2015", "1");
   AddAddress("Jane", "Smith", "Main Street 12346");
   AddAddress("John", "Smith", "Side Street 47");
   WaitForDBThread();
@@ -388,7 +389,7 @@
   base::Time time3 = base::Time::FromTimeT(base::Time::Now().ToTimeT());
 
   AddAutocompleteSuggestion("tel", "+987654321");
-  AddCreditCard("1211-1098-7654-3210", "10", "2030");
+  AddCreditCard("1211-1098-7654-3210", "10", "2030", "1");
   WaitForDBThread();
 
   // Test the results for different starting points.
diff --git a/chrome/browser/chromeos/policy/device_status_collector.cc b/chrome/browser/chromeos/policy/device_status_collector.cc
index 521edf7..3c7445e 100644
--- a/chrome/browser/chromeos/policy/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector.cc
@@ -48,6 +48,7 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/update_engine_client.h"
 #include "chromeos/disks/disk_mount_manager.h"
+#include "chromeos/login/login_state.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
@@ -301,6 +302,12 @@
   return signal_strength - 120;
 }
 
+bool IsKioskApp() {
+  auto user_type = chromeos::LoginState::Get()->GetLoggedInUserType();
+  return user_type == chromeos::LoginState::LOGGED_IN_USER_KIOSK_APP ||
+         user_type == chromeos::LoginState::LOGGED_IN_USER_ARC_KIOSK_APP;
+}
+
 }  // namespace
 
 namespace policy {
@@ -668,7 +675,8 @@
 
   Time now = GetCurrentTime();
 
-  if (state == ui::IDLE_STATE_ACTIVE) {
+  // For kiosk apps we report total uptime instead of active time.
+  if (state == ui::IDLE_STATE_ACTIVE || IsKioskApp()) {
     // If it's been too long since the last report, or if the activity is
     // negative (which can happen when the clock changes), assume a single
     // interval of activity.
diff --git a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
index 276c636..0afa872e 100644
--- a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc
@@ -47,6 +47,7 @@
 #include "chromeos/dbus/shill_service_client.h"
 #include "chromeos/disks/disk_mount_manager.h"
 #include "chromeos/disks/mock_disk_mount_manager.h"
+#include "chromeos/login/login_state.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
@@ -344,6 +345,7 @@
         base::WrapUnique<chromeos::UpdateEngineClient>(update_engine_client_));
 
     chromeos::CrasAudioHandler::InitializeForTesting();
+    chromeos::LoginState::Initialize();
   }
 
   void AddMountPoint(const std::string& mount_point) {
@@ -355,6 +357,7 @@
   }
 
   ~DeviceStatusCollectorTest() override {
+    chromeos::LoginState::Shutdown();
     chromeos::CrasAudioHandler::Shutdown();
     chromeos::KioskAppManager::Shutdown();
     TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
@@ -580,6 +583,47 @@
             GetActiveMilliseconds(device_status_));
 }
 
+// For kiosks report total uptime instead of only active periods.
+TEST_F(DeviceStatusCollectorTest, MixedStatesForKiosk) {
+  ui::IdleState test_states[] = {
+    ui::IDLE_STATE_ACTIVE,
+    ui::IDLE_STATE_IDLE,
+    ui::IDLE_STATE_ACTIVE,
+    ui::IDLE_STATE_ACTIVE,
+    ui::IDLE_STATE_IDLE,
+    ui::IDLE_STATE_IDLE,
+  };
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_KIOSK_APP);
+  settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
+  status_collector_->Simulate(test_states,
+                              sizeof(test_states) / sizeof(ui::IdleState));
+  GetStatus();
+  EXPECT_EQ(6 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+}
+
+// For Arc kiosks report total uptime instead of only active periods.
+TEST_F(DeviceStatusCollectorTest, MixedStatesForArcKiosk) {
+  ui::IdleState test_states[] = {
+    ui::IDLE_STATE_ACTIVE,
+    ui::IDLE_STATE_IDLE,
+    ui::IDLE_STATE_ACTIVE,
+    ui::IDLE_STATE_ACTIVE,
+    ui::IDLE_STATE_IDLE,
+  };
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_ARC_KIOSK_APP);
+  settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
+  status_collector_->Simulate(test_states,
+                              sizeof(test_states) / sizeof(ui::IdleState));
+  GetStatus();
+  EXPECT_EQ(5 * ActivePeriodMilliseconds(),
+            GetActiveMilliseconds(device_status_));
+}
+
 TEST_F(DeviceStatusCollectorTest, StateKeptInPref) {
   ui::IdleState test_states[] = {
     ui::IDLE_STATE_ACTIVE,
@@ -608,23 +652,6 @@
             GetActiveMilliseconds(device_status_));
 }
 
-TEST_F(DeviceStatusCollectorTest, Times) {
-  ui::IdleState test_states[] = {
-    ui::IDLE_STATE_ACTIVE,
-    ui::IDLE_STATE_IDLE,
-    ui::IDLE_STATE_ACTIVE,
-    ui::IDLE_STATE_ACTIVE,
-    ui::IDLE_STATE_IDLE,
-    ui::IDLE_STATE_IDLE
-  };
-  settings_helper_.SetBoolean(chromeos::kReportDeviceActivityTimes, true);
-  status_collector_->Simulate(test_states,
-                              sizeof(test_states) / sizeof(ui::IdleState));
-  GetStatus();
-  EXPECT_EQ(3 * ActivePeriodMilliseconds(),
-            GetActiveMilliseconds(device_status_));
-}
-
 TEST_F(DeviceStatusCollectorTest, MaxStoredPeriods) {
   ui::IdleState test_states[] = {
     ui::IDLE_STATE_ACTIVE,
diff --git a/chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc b/chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc
index cf2a18b..aea97d5 100644
--- a/chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc
@@ -2,22 +2,149 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
+
+#include "base/bind.h"
 #include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
 #include "base/macros.h"
+#include "base/strings/string_util.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/policy/device_policy_builder.h"
+#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
+#include "chrome/browser/chromeos/policy/proto/chrome_device_policy.pb.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/net/url_request_mock_util.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/fake_session_manager_client.h"
+#include "components/version_info/channel.h"
+#include "components/version_info/version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_source.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/notification_types.h"
+#include "extensions/browser/test_extension_registry_observer.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/features/feature_channel.h"
+#include "net/test/url_request/url_request_mock_http_job.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 namespace policy {
 
 namespace {
 
+// Parameters for the several extensions and apps that are used by the tests in
+// this file (note that the paths are given relative to the src/chrome/test/data
+// directory):
+// * The test app which is whitelisted for running in the sign-in profile:
+const char kTestAppId[] = "bjaiihebfngildkcjkjckolinodhliff";
+const char kTestAppUpdateManifestPath[] =
+    "extensions/signin_screen_test_app/update_manifest.xml";
+// * A trivial test app which is NOT whitelisted for running in the sign-in
+//   profile:
+const char kTrivialAppId[] = "mockapnacjbcdncmpkjngjalkhphojek";
+const char kTrivialAppUpdateManifestPath[] =
+    "extensions/trivial_platform_app/update_manifest.xml";
+// * A trivial test extension (note that extensions cannot be whitelisted for
+//   running in the sign-in profile):
+const char kTrivialExtensionId[] = "mockepjebcnmhmhcahfddgfcdgkdifnc";
+const char kTrivialExtensionUpdateManifestPath[] =
+    "extensions/trivial_extension/update_manifest.xml";
+
+// Observer that allows waiting for an installation failure of a specific
+// extension/app.
+// TODO(emaxx): Extract this into a more generic helper class for using in other
+// tests.
+class ExtensionInstallErrorObserver final {
+ public:
+  ExtensionInstallErrorObserver(const Profile* profile,
+                                const std::string& extension_id)
+      : profile_(profile),
+        extension_id_(extension_id),
+        notification_observer_(
+            extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
+            base::Bind(&ExtensionInstallErrorObserver::IsNotificationRelevant,
+                       base::Unretained(this))) {}
+
+  void Wait() { notification_observer_.Wait(); }
+
+ private:
+  // Callback which is used for |WindowedNotificationObserver| for checking
+  // whether the condition being awaited is met.
+  bool IsNotificationRelevant(
+      const content::NotificationSource& source,
+      const content::NotificationDetails& details) const {
+    extensions::CrxInstaller* const crx_installer =
+        content::Source<extensions::CrxInstaller>(source).ptr();
+    return crx_installer->profile() == profile_ &&
+           crx_installer->extension()->id() == extension_id_;
+  }
+
+  const Profile* const profile_;
+  const std::string extension_id_;
+  content::WindowedNotificationObserver notification_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionInstallErrorObserver);
+};
+
+// Observer that allows waiting until the background page of the specified
+// extension/app loads.
+// TODO(emaxx): Extract this into a more generic helper class for using in other
+// tests.
+class ExtensionBackgroundPageReadyObserver final {
+ public:
+  explicit ExtensionBackgroundPageReadyObserver(const std::string& extension_id)
+      : extension_id_(extension_id),
+        notification_observer_(
+            extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
+            base::Bind(
+                &ExtensionBackgroundPageReadyObserver::IsNotificationRelevant,
+                base::Unretained(this))) {}
+
+  void Wait() { notification_observer_.Wait(); }
+
+ private:
+  // Callback which is used for |WindowedNotificationObserver| for checking
+  // whether the condition being awaited is met.
+  bool IsNotificationRelevant(
+      const content::NotificationSource& source,
+      const content::NotificationDetails& details) const {
+    return content::Source<const extensions::Extension>(source)->id() ==
+           extension_id_;
+  }
+
+  const std::string extension_id_;
+  content::WindowedNotificationObserver notification_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionBackgroundPageReadyObserver);
+};
+
+// Returns the initial profile, which is the original profile of the sign-in
+// profile. The apps specified in the policy will be installed into the initial
+// profile, and then become available in both.
+Profile* GetProfile() {
+  // Intentionally not using the |chromeos::ProfileHelper::GetSigninProfile|
+  // method here, as it performs the lazy construction of the profile, while for
+  // the testing purposes it's better to assert that it has been created before.
+  Profile* const profile =
+      g_browser_process->profile_manager()->GetProfileByPath(
+          chromeos::ProfileHelper::GetSigninProfileDir());
+  DCHECK(profile);
+  return profile;
+}
+
 // Tests for the sign-in profile apps being enabled via the command line flag.
 // TODO(emaxx): Remove this smoke test once it's investigated whether just
 // specifying this command line flag leads to tests being timed out.
@@ -38,13 +165,189 @@
 
 IN_PROC_BROWSER_TEST_F(SigninProfileAppsEnabledViaCommandLineTest,
                        NoExtensions) {
-  Profile* const initial_profile =
-      g_browser_process->profile_manager()->GetProfileByPath(
-          chromeos::ProfileHelper::GetSigninProfileDir());
-  ASSERT_TRUE(initial_profile);
-  EXPECT_TRUE(extensions::ExtensionSystem::Get(initial_profile)
+  EXPECT_TRUE(extensions::ExtensionSystem::Get(GetProfile())
                   ->extension_service()
                   ->extensions_enabled());
 }
 
+namespace {
+
+// Base class for testing sign-in profile apps that are installed via the device
+// policy.
+class SigninProfileAppsPolicyTestBase : public DevicePolicyCrosBrowserTest {
+ protected:
+  explicit SigninProfileAppsPolicyTestBase(version_info::Channel channel)
+      : channel_(channel), scoped_current_channel_(channel) {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(chromeos::switches::kLoginManager);
+    command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(switches::kEnableLoginScreenApps);
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
+    InstallOwnerKey();
+    MarkAsEnterpriseOwned();
+  }
+
+  void SetUpOnMainThread() override {
+    EnableUrlRequestMocks();
+    DevicePolicyCrosBrowserTest::SetUpOnMainThread();
+  }
+
+  void AddExtensionForForceInstallation(
+      const std::string& extension_id,
+      const base::FilePath& update_manifest_relative_path) {
+    const GURL update_manifest_url = net::URLRequestMockHTTPJob::GetMockUrl(
+        update_manifest_relative_path.MaybeAsASCII());
+    const std::string policy_item_value = base::ReplaceStringPlaceholders(
+        "$1;$2", {extension_id, update_manifest_url.spec()}, nullptr);
+    device_policy()
+        ->payload()
+        .mutable_device_login_screen_app_install_list()
+        ->add_device_login_screen_app_install_list(policy_item_value);
+    RefreshDevicePolicy();
+  }
+
+  const version_info::Channel channel_;
+
+ private:
+  // Enables URL request mocks for making the test data files (extensions'
+  // update manifests and packages) available under corresponding URLs.
+  static void EnableUrlRequestMocks() {
+    content::BrowserThread::PostTask(
+        content::BrowserThread::IO, FROM_HERE,
+        base::BindOnce(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
+  }
+
+  const extensions::ScopedCurrentChannel scoped_current_channel_;
+
+  DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyTestBase);
+};
+
+// Class for testing sign-in profile apps under different browser channels.
+class SigninProfileAppsPolicyPerChannelTest
+    : public SigninProfileAppsPolicyTestBase,
+      public testing::WithParamInterface<version_info::Channel> {
+ protected:
+  SigninProfileAppsPolicyPerChannelTest()
+      : SigninProfileAppsPolicyTestBase(GetParam()) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyPerChannelTest);
+};
+
+}  // namespace
+
+// Tests that a whitelisted app gets installed on any browser channel.
+IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest,
+                       WhitelistedAppInstallation) {
+  extensions::TestExtensionRegistryObserver registry_observer(
+      extensions::ExtensionRegistry::Get(GetProfile()), kTestAppId);
+
+  AddExtensionForForceInstallation(kTestAppId,
+                                   base::FilePath(kTestAppUpdateManifestPath));
+
+  registry_observer.WaitForExtensionLoaded();
+  EXPECT_TRUE(extensions::ExtensionRegistry::Get(GetProfile())
+                  ->enabled_extensions()
+                  .Contains(kTestAppId));
+}
+
+// Tests that a non-whitelisted app is installed only when on Dev, Canary or
+// "unknown" (trunk) channels, but not on Beta or Stable channels.
+IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest,
+                       NotWhitelistedAppInstallation) {
+  extensions::TestExtensionRegistryObserver registry_observer(
+      extensions::ExtensionRegistry::Get(GetProfile()), kTrivialAppId);
+  ExtensionInstallErrorObserver install_error_observer(GetProfile(),
+                                                       kTrivialAppId);
+
+  AddExtensionForForceInstallation(
+      kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath));
+
+  switch (channel_) {
+    case version_info::Channel::UNKNOWN:
+    case version_info::Channel::CANARY:
+    case version_info::Channel::DEV:
+      registry_observer.WaitForExtensionLoaded();
+      EXPECT_TRUE(extensions::ExtensionRegistry::Get(GetProfile())
+                      ->enabled_extensions()
+                      .Contains(kTrivialAppId));
+      break;
+    case version_info::Channel::BETA:
+    case version_info::Channel::STABLE:
+      install_error_observer.Wait();
+      EXPECT_FALSE(extensions::ExtensionRegistry::Get(GetProfile())
+                       ->GetInstalledExtension(kTrivialAppId));
+      break;
+  }
+}
+
+// Tests that an extension (as opposed to an app) is forbidden from installation
+// regardless of the browser channel.
+IN_PROC_BROWSER_TEST_P(SigninProfileAppsPolicyPerChannelTest,
+                       ExtensionInstallation) {
+  ExtensionInstallErrorObserver install_error_observer(GetProfile(),
+                                                       kTrivialExtensionId);
+
+  AddExtensionForForceInstallation(
+      kTrivialExtensionId, base::FilePath(kTrivialExtensionUpdateManifestPath));
+
+  install_error_observer.Wait();
+  EXPECT_FALSE(extensions::ExtensionRegistry::Get(GetProfile())
+                   ->GetInstalledExtension(kTrivialExtensionId));
+}
+
+// TODO(emaxx): Add the STABLE option once the scoped feature channel bug is
+// fixed (https://crrev.com/2854293003).
+INSTANTIATE_TEST_CASE_P(,
+                        SigninProfileAppsPolicyPerChannelTest,
+                        testing::Values(version_info::Channel::UNKNOWN,
+                                        version_info::Channel::CANARY,
+                                        version_info::Channel::DEV,
+                                        version_info::Channel::BETA));
+
+namespace {
+
+// Class for testing sign-in profile apps under the "unknown" browser channel,
+// which allows to bypass the troublesome whitelist checks.
+class SigninProfileAppsPolicyTest : public SigninProfileAppsPolicyTestBase {
+ protected:
+  SigninProfileAppsPolicyTest()
+      : SigninProfileAppsPolicyTestBase(version_info::Channel::UNKNOWN) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SigninProfileAppsPolicyTest);
+};
+
+}  // namespace
+
+// Tests that a background page is created for the installed sign-in profile
+// app.
+IN_PROC_BROWSER_TEST_F(SigninProfileAppsPolicyTest, BackgroundPage) {
+  ExtensionBackgroundPageReadyObserver page_observer(kTrivialAppId);
+  AddExtensionForForceInstallation(
+      kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath));
+  page_observer.Wait();
+}
+
+// Tests installation of multiple sign-in profile apps.
+IN_PROC_BROWSER_TEST_F(SigninProfileAppsPolicyTest, MultipleApps) {
+  extensions::TestExtensionRegistryObserver registry_observer1(
+      extensions::ExtensionRegistry::Get(GetProfile()), kTestAppId);
+  extensions::TestExtensionRegistryObserver registry_observer2(
+      extensions::ExtensionRegistry::Get(GetProfile()), kTrivialAppId);
+
+  AddExtensionForForceInstallation(kTestAppId,
+                                   base::FilePath(kTestAppUpdateManifestPath));
+  AddExtensionForForceInstallation(
+      kTrivialAppId, base::FilePath(kTrivialAppUpdateManifestPath));
+
+  registry_observer1.WaitForExtensionLoaded();
+  registry_observer2.WaitForExtensionLoaded();
+}
+
 }  // namespace policy
diff --git a/chrome/browser/notifications/notification_platform_bridge_linux.cc b/chrome/browser/notifications/notification_platform_bridge_linux.cc
index aa3a6efc..10b05ac 100644
--- a/chrome/browser/notifications/notification_platform_bridge_linux.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc
@@ -29,11 +29,13 @@
 #include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/shell_integration_linux.h"
+#include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/image/image_skia.h"
 
 namespace {
@@ -467,8 +469,8 @@
       }
       // Always add a settings button.
       actions.push_back(kSettingsButtonId);
-      // TODO(thomasanderson): Localize this string.
-      actions.push_back("Settings");
+      actions.push_back(
+          l10n_util::GetStringUTF8(IDS_NOTIFICATION_BUTTON_SETTINGS));
     }
     writer.AppendArrayOfStrings(actions);
 
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index 841897d..e2b5b0a 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -147,11 +147,8 @@
   // These numbers have been obtained through experimentation on various
   // Mac OS platforms.
 
-  // Corresponds to the string "mmmmmmmmmmmmmm"
-  constexpr size_t kMaxDomainLenghtAlert = 14;
-
-  // Corresponds to the string "mmmmmmmmmmmmmmmmmmmmm"
-  constexpr size_t kMaxDomainLenghtBanner = 21;
+  constexpr size_t kMaxDomainLenghtAlert = 19;
+  constexpr size_t kMaxDomainLenghtBanner = 28;
 
   size_t max_characters = IsPersistentNotification(notification)
                               ? kMaxDomainLenghtAlert
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
index 06c4419..8ebf42f 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
@@ -422,9 +422,9 @@
 }
 
 TEST_F(NotificationPlatformBridgeMacTest, TestDisplayETLDPlusOne) {
-  std::unique_ptr<Notification> notification =
-      CreateBanner("Title", "Context", "https://hello.world.test.co.uk",
-                   "Button 1", nullptr);
+  std::unique_ptr<Notification> notification = CreateBanner(
+      "Title", "Context", "https://overthelimit.hello.world.test.co.uk",
+      "Button 1", nullptr);
 
   std::unique_ptr<NotificationPlatformBridgeMac> bridge(
       new NotificationPlatformBridgeMac(notification_center(),
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index a3bcd2e..be43d6e 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -17,6 +17,7 @@
 #include "base/environment.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
@@ -131,9 +132,14 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/locale_change_guard.h"
+#include "chrome/browser/chromeos/login/session/user_session_manager.h"
+#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
+#include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
 #include "chrome/browser/chromeos/preferences.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
+#include "components/session_manager/core/session_manager.h"
+#include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #endif
 
@@ -141,11 +147,7 @@
 #include "chrome/browser/background/background_mode_manager.h"
 #endif
 
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/login/session/user_session_manager.h"
-#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
-#include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h"
-#else
+#if !defined(OS_CHROMEOS)
 #include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h"
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
 #endif
@@ -424,6 +426,21 @@
   DCHECK(!path.empty()) << "Using an empty path will attempt to write " <<
                             "profile files to the root directory!";
 
+#if defined(OS_CHROMEOS)
+  if (!chromeos::ProfileHelper::IsSigninProfile(this)) {
+    const user_manager::User* user =
+        chromeos::ProfileHelper::Get()->GetUserByProfile(this);
+    // A |User| instance should always exist for a profile which is not the
+    // initial or the sign-in profile.
+    CHECK(user);
+    LOG_IF(FATAL,
+           !session_manager::SessionManager::Get()->HasSessionForAccountId(
+               user->GetAccountId()))
+        << "Attempting to construct the profile before starting the user "
+           "session";
+  }
+#endif
+
 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
   create_session_service_timer_.Start(FROM_HERE,
       TimeDelta::FromMilliseconds(kCreateSessionServiceDelayMS), this,
diff --git a/chrome/browser/resources/ntp4/md_incognito_tab.css b/chrome/browser/resources/ntp4/md_incognito_tab.css
index ccad8be..4866c64 100644
--- a/chrome/browser/resources/ntp4/md_incognito_tab.css
+++ b/chrome/browser/resources/ntp4/md_incognito_tab.css
@@ -16,14 +16,14 @@
 
 .content {
   background-color: #303030;
-  color: rgb(179, 179, 179);
+  color: rgba(255, 255, 255, 0.7);
   font-size: calc(100% - 2px);
   line-height: calc(100% + 6px);
   min-width: 240px;
 }
 
 h1 {
-  color: rgb(204, 204, 204);
+  color: rgba(255, 255, 255, 0.8);
   font-size: calc(100% + 8px);
   font-weight: 400;
   line-height: calc(100% + 8px);
@@ -180,7 +180,7 @@
 
 .bulletpoints + .bulletpoints.tooWide {
   -webkit-margin-start: 0;
-  margin-top: 24px;
+  margin-top: 1.5rem;
 }
 
 /* Wide screens. */
@@ -189,8 +189,8 @@
   h1,
   #subtitle,
   .learn-more-button {
-    margin-bottom: 24px;
-    margin-top: 24px;
+    margin-bottom: 1.5rem;
+    margin-top: 1.5rem;
   }
 
   .content {
@@ -247,14 +247,14 @@
   h1,
   #subtitle,
   .learn-more-button {
-    margin-bottom: 24px;
-    margin-top: 24px;
+    margin-bottom: 1.5rem;
+    margin-top: 1.5rem;
   }
 
   /* The two columns of bulletpoints are moved under each other. */
   .bulletpoints + .bulletpoints {
     -webkit-margin-start: 0;
-    margin-top: 24px;
+    margin-top: 1.5rem;
   }
 
   /* Smaller offsets on smaller screens. */
@@ -267,12 +267,12 @@
     h1,
     #subtitle,
     .learn-more-button {
-      margin-bottom: 16px;
-      margin-top: 16px;
+      margin-bottom: 1rem;
+      margin-top: 1rem;
     }
 
     .bulletpoints + .bulletpoints {
-      margin-top: 16px;
+      margin-top: 1rem;
     }
   }
 
diff --git a/chrome/browser/ui/views/payments/contact_info_editor_view_controller.cc b/chrome/browser/ui/views/payments/contact_info_editor_view_controller.cc
index 9ac6c8d8..2fb8d327 100644
--- a/chrome/browser/ui/views/payments/contact_info_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/contact_info_editor_view_controller.cc
@@ -26,16 +26,13 @@
     PaymentRequestSpec* spec,
     PaymentRequestState* state,
     PaymentRequestDialogView* dialog,
+    BackNavigationType back_navigation_type,
     autofill::AutofillProfile* profile)
-    : EditorViewController(spec, state, dialog), profile_to_edit_(profile) {}
+    : EditorViewController(spec, state, dialog, back_navigation_type),
+      profile_to_edit_(profile) {}
 
 ContactInfoEditorViewController::~ContactInfoEditorViewController() {}
 
-std::unique_ptr<views::View>
-ContactInfoEditorViewController::CreateHeaderView() {
-  return base::MakeUnique<views::View>();
-}
-
 std::vector<EditorField>
 ContactInfoEditorViewController::GetFieldDefinitions() {
   std::vector<EditorField> fields;
diff --git a/chrome/browser/ui/views/payments/contact_info_editor_view_controller.h b/chrome/browser/ui/views/payments/contact_info_editor_view_controller.h
index 5af696374d..b50ec9d 100644
--- a/chrome/browser/ui/views/payments/contact_info_editor_view_controller.h
+++ b/chrome/browser/ui/views/payments/contact_info_editor_view_controller.h
@@ -28,11 +28,11 @@
   ContactInfoEditorViewController(PaymentRequestSpec* spec,
                                   PaymentRequestState* state,
                                   PaymentRequestDialogView* dialog,
+                                  BackNavigationType back_navigation_type,
                                   autofill::AutofillProfile* profile);
   ~ContactInfoEditorViewController() override;
 
   // EditorViewController:
-  std::unique_ptr<views::View> CreateHeaderView() override;
   std::vector<EditorField> GetFieldDefinitions() override;
   base::string16 GetInitialValueForType(
       autofill::ServerFieldType type) override;
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
index 7f0e894..f86e335 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
@@ -9,17 +9,20 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
 #include "chrome/browser/ui/views/payments/preselected_combobox_model.h"
 #include "chrome/browser/ui/views/payments/validating_combobox.h"
 #include "chrome/browser/ui/views/payments/validating_textfield.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/autofill/core/browser/address_combobox_model.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/credit_card.h"
@@ -33,10 +36,12 @@
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/native_theme/native_theme.h"
+#include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/grid_layout.h"
 #include "ui/views/view.h"
 
 namespace payments {
@@ -47,6 +52,10 @@
 // year dropdown.
 const int kNumberOfExpirationYears = 10;
 
+// This is not quite right but is the closest server type that wasn't already
+// used.
+const auto kBillingAddressType = autofill::ADDRESS_BILLING_LINE1;
+
 // Returns the items that are in the expiration month dropdown. Will return the
 // months in order starting at "01" until "12". Uses a clock so that the
 // |default_index| is set to the current month.
@@ -83,13 +92,16 @@
     PaymentRequestSpec* spec,
     PaymentRequestState* state,
     PaymentRequestDialogView* dialog,
+    BackNavigationType back_navigation,
+    int next_ui_tag,
     base::OnceClosure on_edited,
     base::OnceCallback<void(const autofill::CreditCard&)> on_added,
     autofill::CreditCard* credit_card)
-    : EditorViewController(spec, state, dialog),
+    : EditorViewController(spec, state, dialog, back_navigation),
       on_edited_(std::move(on_edited)),
       on_added_(std::move(on_added)),
-      credit_card_to_edit_(credit_card) {}
+      credit_card_to_edit_(credit_card),
+      add_billing_address_button_tag_(next_ui_tag) {}
 
 CreditCardEditorViewController::~CreditCardEditorViewController() {}
 
@@ -148,6 +160,80 @@
   return view;
 }
 
+// Creates the "Billing Address" custom field view.
+//           +------------------------------------+
+// Label*    | | Combobox     | | Add button |    |
+//           +------------------------------------+
+std::unique_ptr<views::View>
+CreditCardEditorViewController::CreateCustomFieldView(
+    autofill::ServerFieldType type) {
+  if (type != kBillingAddressType)
+    return std::unique_ptr<views::View>();
+  std::unique_ptr<views::View> view = base::MakeUnique<views::View>();
+
+  std::unique_ptr<views::GridLayout> layout =
+      base::MakeUnique<views::GridLayout>(view.get());
+
+  // Combobox column.
+  views::ColumnSet* columns = layout->AddColumnSet(0);
+  columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 0,
+                     views::GridLayout::FIXED, kMaximumLabelWidth, 0);
+
+  // This is the horizontal padding between the combobox and the add button.
+  constexpr int kComboboxAddButtonHorizontalPadding = 8;
+  columns->AddPaddingColumn(0, kComboboxAddButtonHorizontalPadding);
+
+  columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0,
+                     views::GridLayout::USE_PREF, 0, 0);
+
+  layout->StartRow(0, 0);
+
+  EditorField billing_address_field(
+      kBillingAddressType,
+      l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_BILLING_ADDRESS),
+      EditorField::LengthHint::HINT_SHORT, /*required=*/true,
+      EditorField::ControlType::COMBOBOX);
+
+  // The combobox filled with potential billing addresses.
+  std::unique_ptr<autofill::AddressComboboxModel> address_combobox_model =
+      base::MakeUnique<autofill::AddressComboboxModel>(
+          *state()->GetPersonalDataManager(), state()->GetApplicationLocale());
+  int selected_index = -1;
+  if (credit_card_to_edit_ &&
+      !credit_card_to_edit_->billing_address_id().empty()) {
+    selected_index = address_combobox_model->GetIndexOfIdentifier(
+        credit_card_to_edit_->billing_address_id());
+  }
+  // This takes care of rare cases where the the billing address set on the
+  // current card isn't valid anymore.
+  if (selected_index == -1)
+    selected_index = address_combobox_model->GetDefaultIndex();
+
+  ValidatingCombobox* combobox =
+      new ValidatingCombobox(std::move(address_combobox_model),
+                             CreateValidationDelegate(billing_address_field));
+  combobox->SetSelectedIndex(selected_index);
+
+  // Using autofill field type as a view ID (for testing).
+  combobox->set_id(static_cast<int>(billing_address_field.type));
+  combobox->set_listener(this);
+
+  // |combobox| will now be owned by |row|.
+  layout->AddView(combobox);
+
+  // The button to add new billing addresses.
+  std::unique_ptr<views::Button> add_button(
+      views::MdTextButton::Create(this, l10n_util::GetStringUTF16(IDS_ADD)));
+  add_button->set_id(
+      static_cast<int>(DialogViewID::ADD_BILLING_ADDRESS_BUTTON));
+  add_button->set_tag(add_billing_address_button_tag_);
+
+  // |add_button| will now be owned by |row|.
+  layout->AddView(add_button.release());
+  view->SetLayoutManager(layout.release());
+  return view;
+}
+
 std::vector<EditorField> CreditCardEditorViewController::GetFieldDefinitions() {
   return std::vector<EditorField>{
       {autofill::CREDIT_CARD_NUMBER,
@@ -163,7 +249,11 @@
       {autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR,
        l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_EXPIRATION_YEAR),
        EditorField::LengthHint::HINT_SHORT, /* required= */ true,
-       EditorField::ControlType::COMBOBOX}};
+       EditorField::ControlType::COMBOBOX},
+      {kBillingAddressType,
+       l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_BILLING_ADDRESS),
+       EditorField::LengthHint::HINT_LONG, /* required= */ true,
+       EditorField::ControlType::CUSTOMFIELD}};
 }
 
 base::string16 CreditCardEditorViewController::GetInitialValueForType(
@@ -204,8 +294,18 @@
                         locale);
   }
 
+  views::Combobox* address_combobox =
+      static_cast<views::Combobox*>(dialog()->GetViewByID(kBillingAddressType));
+  autofill::AddressComboboxModel* model =
+      static_cast<autofill::AddressComboboxModel*>(address_combobox->model());
+
+  credit_card.set_billing_address_id(
+      model->GetItemIdentifierAt(address_combobox->selected_index()));
+
   // TODO(crbug.com/711365): Display global error message.
-  if (autofill::GetCompletionStatusForCard(credit_card, locale) !=
+  if (autofill::GetCompletionStatusForCard(
+          credit_card, locale,
+          state()->GetPersonalDataManager()->GetProfiles()) !=
       autofill::CREDIT_CARD_COMPLETE) {
     return false;
   }
@@ -215,6 +315,8 @@
     state()->GetPersonalDataManager()->AddCreditCard(credit_card);
     std::move(on_added_).Run(credit_card);
   } else {
+    credit_card_to_edit_->set_billing_address_id(
+        credit_card.billing_address_id());
     // We were in edit mode. Copy the data from the temporary object to retain
     // the edited object's other properties (use count, use date, guid, etc.).
     for (const auto& field : text_fields()) {
@@ -259,12 +361,11 @@
       int default_index = 0;
       std::vector<base::string16> months =
           GetExpirationMonthItems(&default_index);
-      return std::unique_ptr<ui::ComboboxModel>(
-          new PreselectedComboboxModel(months, default_index));
+      return base::MakeUnique<PreselectedComboboxModel>(months, default_index);
     }
     case autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR:
-      return std::unique_ptr<ui::ComboboxModel>(
-          new ui::SimpleComboboxModel(GetExpirationYearItems()));
+      return base::MakeUnique<ui::SimpleComboboxModel>(
+          GetExpirationYearItems());
     default:
       NOTREACHED();
       break;
@@ -278,12 +379,42 @@
 
   // Gets the completion message, or empty if nothing is missing from the card.
   base::string16 title = autofill::GetCompletionMessageForCard(
-      autofill::GetCompletionStatusForCard(*credit_card_to_edit_,
-                                           state()->GetApplicationLocale()));
+      autofill::GetCompletionStatusForCard(
+          *credit_card_to_edit_, state()->GetApplicationLocale(),
+          state()->GetPersonalDataManager()->GetProfiles()));
   return title.empty() ? l10n_util::GetStringUTF16(IDS_PAYMENTS_EDIT_CARD)
                        : title;
 }
 
+void CreditCardEditorViewController::ButtonPressed(views::Button* sender,
+                                                   const ui::Event& event) {
+  if (sender->tag() == add_billing_address_button_tag_) {
+    dialog()->ShowShippingAddressEditor(
+        BackNavigationType::kOneStep,
+        /*on_edited=*/
+        base::OnceClosure(),
+        /*on_added=*/
+        base::BindOnce(
+            &CreditCardEditorViewController::AddAndSelectNewBillingAddress,
+            base::Unretained(this)),
+        /*profile=*/nullptr);
+  } else {
+    EditorViewController::ButtonPressed(sender, event);
+  }
+}
+
+void CreditCardEditorViewController::AddAndSelectNewBillingAddress(
+    const autofill::AutofillProfile& profile) {
+  views::Combobox* address_combobox =
+      static_cast<views::Combobox*>(dialog()->GetViewByID(kBillingAddressType));
+  autofill::AddressComboboxModel* model =
+      static_cast<autofill::AddressComboboxModel*>(address_combobox->model());
+  int index = model->AddNewProfile(profile);
+  address_combobox->SetSelectedIndex(index);
+  // Force revalidation.
+  address_combobox->OnBlur();
+}
+
 CreditCardEditorViewController::CreditCardValidationDelegate::
     CreditCardValidationDelegate(
         const EditorField& field,
@@ -303,6 +434,15 @@
 
 bool CreditCardEditorViewController::CreditCardValidationDelegate::
     ValidateCombobox(views::Combobox* combobox) {
+  // The billing address ID is the selected item identifier and not the combobox
+  // value itself.
+  if (field_.type == kBillingAddressType) {
+    // TODO(crbug.com/718905) Find a way to deal with existing incomplete
+    // addresses when choosing them as billing addresses.
+    autofill::AddressComboboxModel* model =
+        static_cast<autofill::AddressComboboxModel*>(combobox->model());
+    return !model->GetItemIdentifierAt(combobox->selected_index()).empty();
+  }
   return ValidateValue(combobox->GetTextForRow(combobox->selected_index()));
 }
 
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.h b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.h
index f400dd04..0238586 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.h
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.h
@@ -17,6 +17,7 @@
 #include "ui/base/models/simple_combobox_model.h"
 
 namespace autofill {
+class AutofillProfile;
 class CreditCard;
 }  // namespace autofill
 
@@ -38,6 +39,8 @@
       PaymentRequestSpec* spec,
       PaymentRequestState* state,
       PaymentRequestDialogView* dialog,
+      BackNavigationType back_navigation,
+      int next_ui_tag,
       base::OnceClosure on_edited,
       base::OnceCallback<void(const autofill::CreditCard&)> on_added,
       autofill::CreditCard* credit_card);
@@ -45,6 +48,8 @@
 
   // EditorViewController:
   std::unique_ptr<views::View> CreateHeaderView() override;
+  std::unique_ptr<views::View> CreateCustomFieldView(
+      autofill::ServerFieldType type) override;
   std::vector<EditorField> GetFieldDefinitions() override;
   base::string16 GetInitialValueForType(
       autofill::ServerFieldType type) override;
@@ -57,6 +62,7 @@
  protected:
   // PaymentRequestSheetController:
   base::string16 GetSheetTitle() override;
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
  private:
   class CreditCardValidationDelegate : public ValidationDelegate {
@@ -90,6 +96,11 @@
 
   bool GetSheetId(DialogViewID* sheet_id) override;
 
+  // Called when a new address was created to be used as the billing address.
+  // The lifespan of |profile| beyond this call is undefined but it's OK, it's
+  // simply propagated to the address combobox model.
+  void AddAndSelectNewBillingAddress(const autofill::AutofillProfile& profile);
+
   // Called when |credit_card_to_edit_| was successfully edited.
   base::OnceClosure on_edited_;
   // Called when a new card was added. The const reference is short-lived, and
@@ -100,6 +111,9 @@
   // controller.
   autofill::CreditCard* credit_card_to_edit_;
 
+  // The value to use for the add billing address button tag.
+  int add_billing_address_button_tag_;
+
   DISALLOW_COPY_AND_ASSIGN(CreditCardEditorViewController);
 };
 
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
index 8b16a299..3b8aa9b 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -20,6 +21,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/views/controls/combobox/combobox.h"
 
 namespace payments {
 
@@ -54,6 +56,10 @@
   EXPECT_EQ(0U, request->state()->available_instruments().size());
   EXPECT_EQ(nullptr, request->state()->selected_instrument());
 
+  // But there must be at least one address available for billing.
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(billing_profile);
+
   OpenCreditCardEditorScreen();
 
   SetEditorTextfieldValue(base::ASCIIToUTF16("Bob Jones"),
@@ -63,6 +69,7 @@
   SetComboboxValue(base::ASCIIToUTF16("05"), autofill::CREDIT_CARD_EXP_MONTH);
   SetComboboxValue(base::ASCIIToUTF16("2026"),
                    autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
+  SelectBillingAddress(billing_profile.guid());
 
   // Verifying the data is in the DB.
   autofill::PersonalDataManager* personal_data_manager = GetDataManager();
@@ -97,6 +104,10 @@
   autofill::TestAutofillClock test_clock;
   test_clock.SetNow(kJune2017);
 
+  // An address is needed so that the UI can choose it as a billing address.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+
   InvokePaymentRequestUI();
 
   // No instruments are available.
@@ -113,6 +124,7 @@
   SetComboboxValue(base::ASCIIToUTF16("05"), autofill::CREDIT_CARD_EXP_MONTH);
   SetComboboxValue(base::ASCIIToUTF16("2026"),
                    autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
+  SelectBillingAddress(billing_address.guid());
 
   // Verifying the data is in the DB.
   autofill::PersonalDataManager* personal_data_manager = GetDataManager();
@@ -287,6 +299,8 @@
                        EnteringInvalidCardNumber_AndFixingIt) {
   autofill::TestAutofillClock test_clock;
   test_clock.SetNow(kJune2017);
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(billing_profile);
 
   InvokePaymentRequestUI();
 
@@ -302,6 +316,7 @@
   SetComboboxValue(base::ASCIIToUTF16("05"), autofill::CREDIT_CARD_EXP_MONTH);
   SetComboboxValue(base::ASCIIToUTF16("2026"),
                    autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
+  SelectBillingAddress(billing_profile.guid());
 
   ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
 
@@ -346,6 +361,9 @@
   card.set_use_date(kJanuary2017);
   card.SetExpirationMonth(1);
   card.SetExpirationYear(2017);
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(billing_profile);
+  card.set_billing_address_id(billing_profile.guid());
   AddCreditCard(card);
   autofill::TestAutofillClock test_clock;
   test_clock.SetNow(kJune2017);
@@ -407,6 +425,158 @@
             request->state()->selected_instrument());
 }
 
+IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest,
+                       EditingCardWithoutBillingAddress) {
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  // Make sure to clear billing address.
+  card.set_billing_address_id("");
+  AddCreditCard(card);
+
+  autofill::TestAutofillClock test_clock;
+  test_clock.SetNow(kJune2017);
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(billing_profile);
+
+  InvokePaymentRequestUI();
+
+  // One instrument is available, but it's not selected.
+  PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
+  EXPECT_EQ(1U, request->state()->available_instruments().size());
+  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+
+  OpenPaymentMethodScreen();
+
+  ResetEventObserver(DialogEvent::CREDIT_CARD_EDITOR_OPENED);
+  ClickOnChildInListViewAndWait(/*child_index=*/0, /*num_children=*/1,
+                                DialogViewID::PAYMENT_METHOD_SHEET_LIST_VIEW);
+
+  // Fixing the billing address.
+  SelectBillingAddress(billing_profile.guid());
+
+  // Verifying the data is in the DB.
+  autofill::PersonalDataManager* personal_data_manager = GetDataManager();
+  personal_data_manager->AddObserver(&personal_data_observer_);
+
+  ResetEventObserver(DialogEvent::BACK_TO_PAYMENT_SHEET_NAVIGATION);
+
+  // Wait until the web database has been updated and the notification sent.
+  base::RunLoop data_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+      .WillOnce(QuitMessageLoop(&data_loop));
+  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  data_loop.Run();
+
+  EXPECT_EQ(1u, personal_data_manager->GetCreditCards().size());
+  autofill::CreditCard* credit_card =
+      personal_data_manager->GetCreditCards()[0];
+  EXPECT_EQ(billing_profile.guid(), credit_card->billing_address_id());
+  // It retains other properties.
+  EXPECT_EQ(card.guid(), credit_card->guid());
+  EXPECT_EQ(base::ASCIIToUTF16("4111111111111111"), credit_card->number());
+  EXPECT_EQ(base::ASCIIToUTF16("Test User"),
+            credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
+
+  // Still have one instrument, but now it's selected.
+  EXPECT_EQ(1U, request->state()->available_instruments().size());
+  EXPECT_EQ(request->state()->available_instruments().back().get(),
+            request->state()->selected_instrument());
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest,
+                       CreateNewBillingAddress) {
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  // Make sure to clear billing address.
+  card.set_billing_address_id("");
+  AddCreditCard(card);
+
+  autofill::TestAutofillClock test_clock;
+  test_clock.SetNow(kJune2017);
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(billing_profile);
+
+  InvokePaymentRequestUI();
+
+  // One instrument is available, but it's not selected.
+  PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
+  EXPECT_EQ(1U, request->state()->available_instruments().size());
+  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+
+  OpenPaymentMethodScreen();
+
+  ResetEventObserver(DialogEvent::CREDIT_CARD_EDITOR_OPENED);
+  ClickOnChildInListViewAndWait(/*child_index=*/0, /*num_children=*/1,
+                                DialogViewID::PAYMENT_METHOD_SHEET_LIST_VIEW);
+  // Click to open the address editor
+  ResetEventObserver(DialogEvent::SHIPPING_ADDRESS_EDITOR_OPENED);
+  ClickOnDialogViewAndWait(DialogViewID::ADD_BILLING_ADDRESS_BUTTON);
+
+  // Set valid address values.
+  SetEditorTextfieldValue(base::ASCIIToUTF16("Bob"), autofill::NAME_FULL);
+  SetEditorTextfieldValue(base::ASCIIToUTF16("42 BobStreet"),
+                          autofill::ADDRESS_HOME_STREET_ADDRESS);
+  SetEditorTextfieldValue(base::ASCIIToUTF16("BobCity"),
+                          autofill::ADDRESS_HOME_CITY);
+  SetEditorTextfieldValue(base::ASCIIToUTF16("BobZip"),
+                          autofill::ADDRESS_HOME_ZIP);
+
+  // Come back to credit card editor.
+  ResetEventObserver(DialogEvent::BACK_NAVIGATION);
+  ClickOnDialogViewAndWait(DialogViewID::SAVE_ADDRESS_BUTTON);
+
+  // And then save credit card state and come back to payment sheet.
+  ResetEventObserver(DialogEvent::BACK_TO_PAYMENT_SHEET_NAVIGATION);
+
+  // Verifying the data is in the DB.
+  autofill::PersonalDataManager* personal_data_manager = GetDataManager();
+  personal_data_manager->AddObserver(&personal_data_observer_);
+
+  // Wait until the web database has been updated and the notification sent.
+  base::RunLoop data_loop;
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+      .WillOnce(QuitMessageLoop(&data_loop));
+  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  data_loop.Run();
+
+  // Still have one instrument, but now it's selected.
+  EXPECT_EQ(1U, request->state()->available_instruments().size());
+  EXPECT_EQ(request->state()->available_instruments().back().get(),
+            request->state()->selected_instrument());
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest,
+                       NonexistentBillingAddres) {
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  // Set a billing address that is not yet added to the personal data.
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  card.set_billing_address_id(billing_profile.guid());
+  AddCreditCard(card);
+
+  autofill::TestAutofillClock test_clock;
+  test_clock.SetNow(kJune2017);
+
+  InvokePaymentRequestUI();
+
+  // One instrument is available, but it's not selected.
+  PaymentRequest* request = GetPaymentRequests(GetActiveWebContents()).front();
+  EXPECT_EQ(1U, request->state()->available_instruments().size());
+  EXPECT_EQ(nullptr, request->state()->selected_instrument());
+
+  // Now add the billing address to the personal data.
+  AddAutofillProfile(billing_profile);
+
+  // Go back and re-invoke.
+  ResetEventObserver(DialogEvent::DIALOG_CLOSED);
+  ClickOnDialogViewAndWait(DialogViewID::CANCEL_BUTTON,
+                           /*wait_for_animation=*/false);
+  InvokePaymentRequestUI();
+
+  // Still have one instrument, but now it's selected.
+  request = GetPaymentRequests(GetActiveWebContents()).front();
+  EXPECT_EQ(1U, request->state()->available_instruments().size());
+  EXPECT_EQ(request->state()->available_instruments().back().get(),
+            request->state()->selected_instrument());
+}
+
 IN_PROC_BROWSER_TEST_F(PaymentRequestCreditCardEditorTest, EnteringEmptyData) {
   InvokePaymentRequestUI();
 
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc
index d90ef78..d98471fa9 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc
@@ -30,7 +30,8 @@
 
   std::unique_ptr<CreditCardEditorViewController> view_controller(
       new CreditCardEditorViewController(
-          nullptr, nullptr, nullptr, base::OnceClosure(),
+          nullptr, nullptr, nullptr, BackNavigationType::kPaymentSheet, 0,
+          base::OnceClosure(),
           base::OnceCallback<void(const autofill::CreditCard&)>(), nullptr));
 
   std::unique_ptr<ui::ComboboxModel> model =
@@ -61,7 +62,8 @@
 
   std::unique_ptr<CreditCardEditorViewController> view_controller(
       new CreditCardEditorViewController(
-          nullptr, nullptr, nullptr, base::OnceClosure(),
+          nullptr, nullptr, nullptr, BackNavigationType::kPaymentSheet, 0,
+          base::OnceClosure(),
           base::OnceCallback<void(const autofill::CreditCard&)>(), nullptr));
 
   std::unique_ptr<ui::ComboboxModel> model =
@@ -91,7 +93,8 @@
 
   std::unique_ptr<CreditCardEditorViewController> view_controller(
       new CreditCardEditorViewController(
-          nullptr, nullptr, nullptr, base::OnceClosure(),
+          nullptr, nullptr, nullptr, BackNavigationType::kPaymentSheet, 0,
+          base::OnceClosure(),
           base::OnceCallback<void(const autofill::CreditCard&)>(), nullptr));
 
   std::unique_ptr<ui::ComboboxModel> model =
diff --git a/chrome/browser/ui/views/payments/cvc_unmask_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/cvc_unmask_view_controller_browsertest.cc
index d6e111dd..ca45140 100644
--- a/chrome/browser/ui/views/payments/cvc_unmask_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/cvc_unmask_view_controller_browsertest.cc
@@ -22,7 +22,11 @@
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCvcUnmaskViewControllerTest,
                        CvcSentToResponse) {
-  AddCreditCard(autofill::test::GetCreditCard());  // Visa.
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(autofill::test::GetCreditCard());  // Visa card.
+  card.set_billing_address_id(profile.guid());
+  AddCreditCard(card);
 
   InvokePaymentRequestUI();
   ResetEventObserver(DialogEvent::DIALOG_CLOSED);
@@ -35,7 +39,11 @@
 // does not crash.
 IN_PROC_BROWSER_TEST_F(PaymentRequestCvcUnmaskViewControllerTest,
                        OpenGoBackOpenPay) {
-  AddCreditCard(autofill::test::GetCreditCard());  // Visa.
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(autofill::test::GetCreditCard());  // Visa card.
+  card.set_billing_address_id(profile.guid());
+  AddCreditCard(card);
 
   InvokePaymentRequestUI();
   OpenCVCPromptWithCVC(base::ASCIIToUTF16("012"));
@@ -50,10 +58,19 @@
 
 IN_PROC_BROWSER_TEST_F(PaymentRequestCvcUnmaskViewControllerTest,
                        EnterAcceleratorConfirmsCvc) {
-  AddCreditCard(autofill::test::GetCreditCard());  // Visa.
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(autofill::test::GetCreditCard());  // Visa card.
+  card.set_billing_address_id(profile.guid());
+  AddCreditCard(card);
 
   InvokePaymentRequestUI();
+
   ResetEventObserver(DialogEvent::DIALOG_CLOSED);
+  // This prevents a timeout in error cases where PAY_BUTTON is disabled.
+  ASSERT_TRUE(dialog_view()
+                  ->GetViewByID(static_cast<int>(DialogViewID::PAY_BUTTON))
+                  ->enabled());
   OpenCVCPromptWithCVC(base::ASCIIToUTF16("012"));
 
   ResetEventObserver(DialogEvent::DIALOG_CLOSED);
diff --git a/chrome/browser/ui/views/payments/editor_view_controller.cc b/chrome/browser/ui/views/payments/editor_view_controller.cc
index fa3ab44..6e8d0a1 100644
--- a/chrome/browser/ui/views/payments/editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/editor_view_controller.cc
@@ -72,11 +72,14 @@
 
 }  // namespace
 
-EditorViewController::EditorViewController(PaymentRequestSpec* spec,
-                                           PaymentRequestState* state,
-                                           PaymentRequestDialogView* dialog)
+EditorViewController::EditorViewController(
+    PaymentRequestSpec* spec,
+    PaymentRequestState* state,
+    PaymentRequestDialogView* dialog,
+    BackNavigationType back_navigation_type)
     : PaymentRequestSheetController(spec, state, dialog),
-      first_field_view_(nullptr) {}
+      first_field_view_(nullptr),
+      back_navigation_type_(back_navigation_type) {}
 
 EditorViewController::~EditorViewController() {}
 
@@ -94,6 +97,15 @@
   RelayoutPane();
 }
 
+std::unique_ptr<views::View> EditorViewController::CreateHeaderView() {
+  return nullptr;
+}
+
+std::unique_ptr<views::View> EditorViewController::CreateCustomFieldView(
+    autofill::ServerFieldType type) {
+  return nullptr;
+}
+
 std::unique_ptr<views::Button> EditorViewController::CreatePrimaryButton() {
   std::unique_ptr<views::Button> button(
       views::MdTextButton::CreateSecondaryUiBlueButton(
@@ -113,7 +125,9 @@
   // No insets. Child views below are responsible for their padding.
 
   // An editor can optionally have a header view specific to it.
-  content_view->AddChildView(CreateHeaderView().release());
+  std::unique_ptr<views::View> header_view = CreateHeaderView();
+  if (header_view.get())
+    content_view->AddChildView(header_view.release());
 
   // The heart of the editor dialog: all the input fields with their labels.
   content_view->AddChildView(CreateEditorView().release());
@@ -154,8 +168,16 @@
                                          const ui::Event& event) {
   switch (sender->tag()) {
     case static_cast<int>(EditorViewControllerTags::SAVE_BUTTON):
-      if (ValidateModelAndSave())
-        dialog()->GoBackToPaymentSheet();
+      if (ValidateModelAndSave()) {
+        switch (back_navigation_type_) {
+          case BackNavigationType::kOneStep:
+            dialog()->GoBack();
+            break;
+          case BackNavigationType::kPaymentSheet:
+            dialog()->GoBackToPaymentSheet();
+            break;
+        }
+      }
       break;
     default:
       PaymentRequestSheetController::ButtonPressed(sender, event);
@@ -201,13 +223,13 @@
   columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0,
                      views::GridLayout::USE_PREF, 0, 0);
 
-  // The LayoutManager needs to be set before input fields are created, so we
-  // keep a handle to it before we release it to the view.
-  views::GridLayout* layout_handle = editor_layout.get();
   editor_view->SetLayoutManager(editor_layout.release());
   std::vector<EditorField> fields = GetFieldDefinitions();
-  for (const auto& field : fields)
-    CreateInputField(layout_handle, field);
+  for (const auto& field : fields) {
+    CreateInputField(
+        static_cast<views::GridLayout*>(editor_view->GetLayoutManager()),
+        field);
+  }
 
   return editor_view;
 }
@@ -226,9 +248,7 @@
 
   std::unique_ptr<views::Label> label = base::MakeUnique<views::Label>(
       field.required ? field.label + base::ASCIIToUTF16("*") : field.label);
-  // A very long label will wrap. Value picked so that left + right label
-  // padding bring the label to half-way in the dialog (~225).
-  constexpr int kMaximumLabelWidth = 192;
+
   label->SetMultiLine(true);
   label->SetMaximumWidth(kMaximumLabelWidth);
   layout->AddView(label.release());
@@ -267,7 +287,9 @@
     layout->AddView(combobox, 1, 1, views::GridLayout::FILL,
                     views::GridLayout::FILL, 0, kInputFieldHeight);
   } else {
-    NOTREACHED();
+    // Custom field view will now be owned by |row|. And it must be valid since
+    // the derived class specified a custom view for this field.
+    layout->AddView(CreateCustomFieldView(field.type).release());
   }
 
   layout->StartRow(0, 0);
diff --git a/chrome/browser/ui/views/payments/editor_view_controller.h b/chrome/browser/ui/views/payments/editor_view_controller.h
index e6533bf..0a1a28c 100644
--- a/chrome/browser/ui/views/payments/editor_view_controller.h
+++ b/chrome/browser/ui/views/payments/editor_view_controller.h
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string16.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
 #include "chrome/browser/ui/views/payments/payment_request_sheet_controller.h"
 #include "chrome/browser/ui/views/payments/validation_delegate.h"
 #include "components/autofill/core/browser/field_types.h"
@@ -37,14 +38,13 @@
 
 class PaymentRequestSpec;
 class PaymentRequestState;
-class PaymentRequestDialogView;
 class ValidatingCombobox;
 class ValidatingTextfield;
 
 // Field definition for an editor field, used to build the UI.
 struct EditorField {
   enum class LengthHint : int { HINT_LONG, HINT_SHORT };
-  enum class ControlType : int { TEXTFIELD, COMBOBOX };
+  enum class ControlType : int { TEXTFIELD, COMBOBOX, CUSTOMFIELD };
 
   EditorField(autofill::ServerFieldType type,
               base::string16 label,
@@ -89,9 +89,13 @@
       std::map<const EditorField, views::View*, EditorField::Compare>;
 
   // Does not take ownership of the arguments, which should outlive this object.
+  // |back_navigation_type| identifies what sort of back navigation should be
+  // done when editing is successful. This is independent of the back arrow
+  // which always goes back one step.
   EditorViewController(PaymentRequestSpec* spec,
                        PaymentRequestState* state,
-                       PaymentRequestDialogView* dialog);
+                       PaymentRequestDialogView* dialog,
+                       BackNavigationType back_navigation_type);
   ~EditorViewController() override;
 
   // Will display |error_message| alongside the input field represented by
@@ -103,7 +107,13 @@
   const TextFieldsMap& text_fields() const { return text_fields_; }
 
  protected:
-  virtual std::unique_ptr<views::View> CreateHeaderView() = 0;
+  // A very long label will wrap. Value picked so that left + right label
+  // padding bring the label to half-way in the dialog (~225).
+  static constexpr int kMaximumLabelWidth = 192;
+
+  virtual std::unique_ptr<views::View> CreateHeaderView();
+  virtual std::unique_ptr<views::View> CreateCustomFieldView(
+      autofill::ServerFieldType type);
   // Returns the field definitions used to build the UI.
   virtual std::vector<EditorField> GetFieldDefinitions() = 0;
   virtual base::string16 GetInitialValueForType(
@@ -131,11 +141,11 @@
   // UpdateEditorView.
   virtual void UpdateEditorView();
 
- private:
   // PaymentRequestSheetController:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
   views::View* GetFirstFocusedView() override;
 
+ private:
   // views::TextfieldController:
   void ContentsChanged(views::Textfield* sender,
                        const base::string16& new_contents) override;
@@ -162,6 +172,9 @@
   // The first label in the editor, used to set the initial focus.
   views::View* first_field_view_;
 
+  // Identifies where to go back when the editing completes successfully.
+  BackNavigationType back_navigation_type_;
+
   DISALLOW_COPY_AND_ASSIGN(EditorViewController);
 };
 
diff --git a/chrome/browser/ui/views/payments/error_message_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/error_message_view_controller_browsertest.cc
index cc11c0e7..770b81f 100644
--- a/chrome/browser/ui/views/payments/error_message_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/error_message_view_controller_browsertest.cc
@@ -27,7 +27,11 @@
 
 // Testing the use of the complete('fail') JS API and the error message.
 IN_PROC_BROWSER_TEST_F(PaymentRequestErrorMessageTest, CompleteFail) {
-  AddCreditCard(autofill::test::GetCreditCard());  // Visa
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(billing_profile);
+  autofill::CreditCard card(autofill::test::GetCreditCard());  // Visa
+  card.set_billing_address_id(billing_profile.guid());
+  AddCreditCard(card);
   InvokePaymentRequestUI();
 
   // We are ready to pay.
diff --git a/chrome/browser/ui/views/payments/payment_method_view_controller.cc b/chrome/browser/ui/views/payments/payment_method_view_controller.cc
index 87c05d47..20dfc7f 100644
--- a/chrome/browser/ui/views/payments/payment_method_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_method_view_controller.cc
@@ -44,6 +44,8 @@
   // The tag for the button that triggers the "add card" flow. Starts at
   // |kFirstTagValue| not to conflict with tags common to all views.
   ADD_CREDIT_CARD_BUTTON = kFirstTagValue,
+  // This value is passed to inner views so they can use it as a starting tag.
+  MAX_TAG,
 };
 
 class PaymentMethodListItem : public payments::PaymentRequestItemList::Item {
@@ -126,9 +128,11 @@
       case PaymentInstrument::Type::AUTOFILL:
         // Since we are a list item, we only care about the on_edited callback.
         dialog_->ShowCreditCardEditor(
-            /*on_edited=*/base::BindOnce(
-                &PaymentRequestState::SetSelectedInstrument,
-                base::Unretained(state()), instrument_),
+            BackNavigationType::kPaymentSheet,
+            static_cast<int>(PaymentMethodViewControllerTags::MAX_TAG),
+            /*on_edited=*/
+            base::BindOnce(&PaymentRequestState::SetSelectedInstrument,
+                           base::Unretained(state()), instrument_),
             /*on_added=*/
             base::OnceCallback<void(const autofill::CreditCard&)>(),
             static_cast<AutofillPaymentInstrument*>(instrument_)
@@ -204,6 +208,8 @@
         PaymentMethodViewControllerTags::ADD_CREDIT_CARD_BUTTON):
       // Only provide the |on_added| callback, in response to this button.
       dialog()->ShowCreditCardEditor(
+          BackNavigationType::kPaymentSheet,
+          static_cast<int>(PaymentMethodViewControllerTags::MAX_TAG),
           /*on_edited=*/base::OnceClosure(),
           /*on_added=*/
           base::BindOnce(&PaymentRequestState::AddAutofillPaymentInstrument,
diff --git a/chrome/browser/ui/views/payments/payment_method_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/payment_method_view_controller_browsertest.cc
index a95524e..33c2d55 100644
--- a/chrome/browser/ui/views/payments/payment_method_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_method_view_controller_browsertest.cc
@@ -24,7 +24,10 @@
 };
 
 IN_PROC_BROWSER_TEST_F(PaymentMethodViewControllerTest, OneCardSelected) {
-  const autofill::CreditCard card = autofill::test::GetCreditCard();
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(billing_profile);
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_profile.guid());
   AddCreditCard(card);
 
   InvokePaymentRequestUI();
@@ -47,7 +50,12 @@
 
 IN_PROC_BROWSER_TEST_F(PaymentMethodViewControllerTest,
                        OneCardSelectedOutOfMany) {
+  autofill::AutofillProfile billing_profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(billing_profile);
+
   autofill::CreditCard card1 = autofill::test::GetCreditCard();
+  card1.set_billing_address_id(billing_profile.guid());
+
   // Ensure that this card is the first suggestion.
   card1.set_use_count(5U);
   AddCreditCard(card1);
@@ -55,6 +63,7 @@
   // Slightly different visa.
   autofill::CreditCard card2 = autofill::test::GetCreditCard();
   card2.SetNumber(base::ASCIIToUTF16("4111111111111112"));
+  card2.set_billing_address_id(billing_profile.guid());
   card2.set_use_count(1U);
   AddCreditCard(card2);
 
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
index 915f4897..25448ea21 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/ui/views/payments/validating_textfield.h"
 #include "chrome/browser/ui/views/payments/view_stack.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/autofill/core/browser/address_combobox_model.h"
 #include "components/autofill/core/browser/autofill_profile.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
@@ -51,6 +52,10 @@
 
 namespace payments {
 
+namespace {
+const auto kBillingAddressType = autofill::ADDRESS_BILLING_LINE1;
+}  // namespace
+
 PersonalDataLoadedObserverMock::PersonalDataLoadedObserverMock() {}
 PersonalDataLoadedObserverMock::~PersonalDataLoadedObserverMock() {}
 
@@ -574,6 +579,17 @@
   combobox->OnBlur();
 }
 
+void PaymentRequestBrowserTestBase::SelectBillingAddress(
+    const std::string& billing_address_id) {
+  views::Combobox* address_combobox(static_cast<views::Combobox*>(
+      dialog_view()->GetViewByID(static_cast<int>(kBillingAddressType))));
+  ASSERT_NE(address_combobox, nullptr);
+  autofill::AddressComboboxModel* address_combobox_model(
+      static_cast<autofill::AddressComboboxModel*>(address_combobox->model()));
+  address_combobox->SetSelectedIndex(
+      address_combobox_model->GetIndexOfIdentifier(billing_address_id));
+}
+
 bool PaymentRequestBrowserTestBase::IsEditorTextfieldInvalid(
     autofill::ServerFieldType type) {
   ValidatingTextfield* textfield = static_cast<ValidatingTextfield*>(
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.h b/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
index 35abd42c..595c8e9 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.h
@@ -189,6 +189,9 @@
   base::string16 GetComboboxValue(autofill::ServerFieldType type);
   void SetComboboxValue(const base::string16& value,
                         autofill::ServerFieldType type);
+  // Special case for the billing address since the interesting value is not
+  // the visible one accessible directly on the base combobox model.
+  void SelectBillingAddress(const std::string& billing_address_id);
 
   // Whether the editor textfield/combobox for the given |type| is currently in
   // an invalid state.
diff --git a/chrome/browser/ui/views/payments/payment_request_can_make_payment_metrics_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_can_make_payment_metrics_browsertest.cc
index 3c75b6e82..5027856 100644
--- a/chrome/browser/ui/views/payments/payment_request_can_make_payment_metrics_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_can_make_payment_metrics_browsertest.cc
@@ -139,6 +139,10 @@
                        Called_False_Shown_Completed) {
   base::HistogramTester histogram_tester;
 
+  // An address is needed so that the UI can choose it as a billing address.
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+
   // Don't add a card on file, so CanMakePayment returns false.
   // Start the Payment Request and expect CanMakePayment to be called before the
   // Payment Request is shown.
@@ -156,6 +160,7 @@
   SetComboboxValue(base::UTF8ToUTF16("05"), autofill::CREDIT_CARD_EXP_MONTH);
   SetComboboxValue(base::UTF8ToUTF16("2026"),
                    autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
+  SelectBillingAddress(billing_address.guid());
   ResetEventObserver(DialogEvent::BACK_TO_PAYMENT_SHEET_NAVIGATION);
   ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
 
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
index 8752e2a..1363aae 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
@@ -238,14 +238,17 @@
 }
 
 void PaymentRequestDialogView::ShowCreditCardEditor(
+    BackNavigationType back_navigation_type,
+    int next_ui_tag,
     base::OnceClosure on_edited,
     base::OnceCallback<void(const autofill::CreditCard&)> on_added,
     autofill::CreditCard* credit_card) {
   view_stack_->Push(
       CreateViewAndInstallController(
           base::MakeUnique<CreditCardEditorViewController>(
-              request_->spec(), request_->state(), this, std::move(on_edited),
-              std::move(on_added), credit_card),
+              request_->spec(), request_->state(), this, back_navigation_type,
+              next_ui_tag, std::move(on_edited), std::move(on_added),
+              credit_card),
           &controller_map_),
       /* animate = */ true);
   if (observer_for_testing_)
@@ -253,24 +256,28 @@
 }
 
 void PaymentRequestDialogView::ShowShippingAddressEditor(
+    BackNavigationType back_navigation_type,
     base::OnceClosure on_edited,
     base::OnceCallback<void(const autofill::AutofillProfile&)> on_added,
     autofill::AutofillProfile* profile) {
-  view_stack_->Push(CreateViewAndInstallController(
-                        base::MakeUnique<ShippingAddressEditorViewController>(
-                            request_->spec(), request_->state(), this,
-                            std::move(on_edited), std::move(on_added), profile),
-                        &controller_map_),
-                    /* animate = */ true);
+  view_stack_->Push(
+      CreateViewAndInstallController(
+          base::MakeUnique<ShippingAddressEditorViewController>(
+              request_->spec(), request_->state(), this, back_navigation_type,
+              std::move(on_edited), std::move(on_added), profile),
+          &controller_map_),
+      /* animate = */ true);
   if (observer_for_testing_)
     observer_for_testing_->OnShippingAddressEditorOpened();
 }
 
 void PaymentRequestDialogView::ShowContactInfoEditor(
+    BackNavigationType back_navigation_type,
     autofill::AutofillProfile* profile) {
   view_stack_->Push(CreateViewAndInstallController(
                         base::MakeUnique<ContactInfoEditorViewController>(
-                            request_->spec(), request_->state(), this, profile),
+                            request_->spec(), request_->state(), this,
+                            back_navigation_type, profile),
                         &controller_map_),
                     /* animate = */ true);
   if (observer_for_testing_)
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.h b/chrome/browser/ui/views/payments/payment_request_dialog_view.h
index 96b6be01..4f29f63 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view.h
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.h
@@ -32,6 +32,11 @@
 using ControllerMap =
     std::map<views::View*, std::unique_ptr<PaymentRequestSheetController>>;
 
+enum class BackNavigationType {
+  kOneStep = 0,
+  kPaymentSheet,
+};
+
 // The dialog delegate that represents a desktop WebPayments dialog. This class
 // is responsible for displaying the view associated with the current state of
 // the WebPayments flow and managing the transition between those states.
@@ -108,7 +113,12 @@
   // |on_edited| is called when |credit_card| was successfully edited, and
   // |on_added| is called when a new credit card was added (the reference is
   // short-lived; callee should make a copy of the CreditCard object).
+  // |back_navigation_type| identifies the type of navigation to execute once
+  // the editor has completed successfully. |next_ui_tag| is the lowest value
+  // that the credit card editor can use to assign to custom controls.
   void ShowCreditCardEditor(
+      BackNavigationType back_navigation_type,
+      int next_ui_tag,
       base::OnceClosure on_edited,
       base::OnceCallback<void(const autofill::CreditCard&)> on_added,
       autofill::CreditCard* credit_card = nullptr);
@@ -116,12 +126,18 @@
   // |on_edited| is called when |profile| was successfully edited, and
   // |on_added| is called when a new profile was added (the reference is
   // short-lived; callee should make a copy of the profile object).
+  // |back_navigation_type| identifies the type of navigation to execute once
+  // the editor has completed successfully.
   void ShowShippingAddressEditor(
+      BackNavigationType back_navigation_type,
       base::OnceClosure on_edited,
       base::OnceCallback<void(const autofill::AutofillProfile&)> on_added,
       autofill::AutofillProfile* profile);
   // |profile| is the profile to be edited, or nullptr for adding a profile.
-  void ShowContactInfoEditor(autofill::AutofillProfile* profile = nullptr);
+  // |back_navigation_type| identifies the type of navigation to execute once
+  // the editor has completed successfully.
+  void ShowContactInfoEditor(BackNavigationType back_navigation_type,
+                             autofill::AutofillProfile* profile = nullptr);
   void EditorViewUpdated();
 
   void ShowCvcUnmaskPrompt(
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h b/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h
index 267ed3b..5c60f2d 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h
@@ -29,6 +29,8 @@
   CANCEL_BUTTON,
   BACK_BUTTON,
   CVC_PROMPT_CONFIRM_BUTTON,
+  ADD_BILLING_ADDRESS_BUTTON,
+  SAVE_ADDRESS_BUTTON,
 
   // The following are buttons that are displayed inline in the Payment Sheet
   // sections when no option is selected or available.
@@ -74,6 +76,9 @@
   CREDIT_CARD_EDITOR_SHEET,
   CVC_UNMASK_SHEET,
   SHIPPING_ADDRESS_EDITOR_SHEET,
+
+  // The combobox to choose a billing address to associate to a credit card.
+  CREDIT_CARD_BILLING_ADDRESS,
 };
 
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
index 5caf7e9..fae08bf 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
@@ -65,7 +65,8 @@
   SHOW_CONTACT_INFO_BUTTON,     // Navigate to the contact info screen
   ADD_CONTACT_INFO_BUTTON,      // Navigate to the contact info editor
   SHOW_SHIPPING_OPTION_BUTTON,  // Navigate to the shipping options screen
-  PAY_BUTTON
+  PAY_BUTTON,
+  MAX_TAG,  // Always keep last.
 };
 
 // A class that ensures proper elision of labels in the form
@@ -485,6 +486,7 @@
 
     case static_cast<int>(PaymentSheetViewControllerTags::ADD_SHIPPING_BUTTON):
       dialog()->ShowShippingAddressEditor(
+          BackNavigationType::kPaymentSheet,
           /*on_edited=*/base::OnceClosure(),  // This is always an add.
           /*on_added=*/
           base::BindOnce(&PaymentRequestState::AddAutofillShippingProfile,
@@ -500,6 +502,8 @@
     case static_cast<int>(
         PaymentSheetViewControllerTags::ADD_PAYMENT_METHOD_BUTTON):
       dialog()->ShowCreditCardEditor(
+          BackNavigationType::kPaymentSheet,
+          static_cast<int>(PaymentSheetViewControllerTags::MAX_TAG),
           /*on_edited=*/base::OnceClosure(),  // This is always an add.
           /*on_added=*/
           base::BindOnce(&PaymentRequestState::AddAutofillPaymentInstrument,
@@ -515,7 +519,7 @@
 
     case static_cast<int>(
         PaymentSheetViewControllerTags::ADD_CONTACT_INFO_BUTTON):
-      dialog()->ShowContactInfoEditor();
+      dialog()->ShowContactInfoEditor(BackNavigationType::kPaymentSheet);
       break;
 
     case static_cast<int>(
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc
index 8ff3e3ef..95f66ead 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc
@@ -36,7 +36,11 @@
 // With a supported card (Visa) present, the pay button should be enabled.
 IN_PROC_BROWSER_TEST_F(PaymentSheetViewControllerNoShippingTest,
                        SupportedCard) {
-  AddCreditCard(autofill::test::GetCreditCard());  // Visa card.
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(autofill::test::GetCreditCard());  // Visa card.
+  card.set_billing_address_id(profile.guid());
+  AddCreditCard(card);
 
   InvokePaymentRequestUI();
   EXPECT_TRUE(IsPayButtonEnabled());
@@ -105,8 +109,11 @@
 // enough information to enable the pay button.
 IN_PROC_BROWSER_TEST_F(PaymentSheetViewControllerContactDetailsTest,
                        SupportedCard_CompleteContactInfo) {
-  AddCreditCard(autofill::test::GetCreditCard());  // Visa card.
-  AddAutofillProfile(autofill::test::GetFullProfile());
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(autofill::test::GetCreditCard());  // Visa card.
+  card.set_billing_address_id(profile.guid());
+  AddCreditCard(card);
 
   InvokePaymentRequestUI();
   EXPECT_TRUE(IsPayButtonEnabled());
@@ -163,8 +170,11 @@
 
 IN_PROC_BROWSER_TEST_F(PaymentSheetViewControllerContactDetailsTest,
                        AllClickableRowsPresent) {
-  AddCreditCard(autofill::test::GetCreditCard());  // Visa card.
-  AddAutofillProfile(autofill::test::GetFullProfile());
+  autofill::AutofillProfile profile(autofill::test::GetFullProfile());
+  AddAutofillProfile(profile);
+  autofill::CreditCard card(autofill::test::GetCreditCard());  // Visa card.
+  card.set_billing_address_id(profile.guid());
+  AddCreditCard(card);
   InvokePaymentRequestUI();
 
   EXPECT_NE(nullptr, dialog_view()->GetViewByID(static_cast<int>(
diff --git a/chrome/browser/ui/views/payments/profile_list_view_controller.cc b/chrome/browser/ui/views/payments/profile_list_view_controller.cc
index 04e4303..f30561a6 100644
--- a/chrome/browser/ui/views/payments/profile_list_view_controller.cc
+++ b/chrome/browser/ui/views/payments/profile_list_view_controller.cc
@@ -135,9 +135,10 @@
 
   void ShowEditor(autofill::AutofillProfile* profile) override {
     dialog()->ShowShippingAddressEditor(
-        /*on_edited=*/base::BindOnce(
-            &PaymentRequestState::SetSelectedShippingProfile,
-            base::Unretained(state()), profile),
+        BackNavigationType::kPaymentSheet,
+        /*on_edited=*/
+        base::BindOnce(&PaymentRequestState::SetSelectedShippingProfile,
+                       base::Unretained(state()), profile),
         /*on_added=*/
         base::BindOnce(&PaymentRequestState::AddAutofillShippingProfile,
                        base::Unretained(state()), /*selected=*/true),
@@ -244,7 +245,7 @@
   }
 
   void ShowEditor(autofill::AutofillProfile* profile) override {
-    dialog()->ShowContactInfoEditor(profile);
+    dialog()->ShowContactInfoEditor(BackNavigationType::kPaymentSheet, profile);
   }
 
   autofill::AutofillProfile* GetSelectedProfile() override {
diff --git a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
index 8c9983b..9dc6b2d4 100644
--- a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.cc
@@ -13,6 +13,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
 #include "chrome/browser/ui/views/payments/validating_combobox.h"
 #include "chrome/browser/ui/views/payments/validating_textfield.h"
 #include "chrome/grit/generated_resources.h"
@@ -68,10 +69,11 @@
     PaymentRequestSpec* spec,
     PaymentRequestState* state,
     PaymentRequestDialogView* dialog,
+    BackNavigationType back_navigation_type,
     base::OnceClosure on_edited,
     base::OnceCallback<void(const autofill::AutofillProfile&)> on_added,
     autofill::AutofillProfile* profile)
-    : EditorViewController(spec, state, dialog),
+    : EditorViewController(spec, state, dialog, back_navigation_type),
       on_edited_(std::move(on_edited)),
       on_added_(std::move(on_added)),
       profile_to_edit_(profile),
@@ -82,11 +84,6 @@
 
 ShippingAddressEditorViewController::~ShippingAddressEditorViewController() {}
 
-std::unique_ptr<views::View>
-ShippingAddressEditorViewController::CreateHeaderView() {
-  return base::MakeUnique<views::View>();
-}
-
 std::vector<EditorField>
 ShippingAddressEditorViewController::GetFieldDefinitions() {
   return editor_fields_;
@@ -113,7 +110,6 @@
   autofill::AutofillProfile profile;
   if (!SaveFieldsToProfile(&profile, /*ignore_errors=*/false))
     return false;
-
   if (!profile_to_edit_) {
     // Add the profile (will not add a duplicate).
     profile.set_origin(autofill::kSettingsOrigin);
@@ -223,6 +219,14 @@
                           : l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_ADDRESS);
 }
 
+std::unique_ptr<views::Button>
+ShippingAddressEditorViewController::CreatePrimaryButton() {
+  std::unique_ptr<views::Button> button(
+      EditorViewController::CreatePrimaryButton());
+  button->set_id(static_cast<int>(DialogViewID::SAVE_ADDRESS_BUTTON));
+  return button;
+}
+
 void ShippingAddressEditorViewController::UpdateEditorFields() {
   editor_fields_.clear();
   std::string chosen_country_code;
@@ -417,7 +421,6 @@
     controller_->DisplayErrorMessageForField(field_, base::ASCIIToUTF16(""));
     return true;
   }
-
   bool is_required_valid = !field_.required;
   const base::string16 displayed_message =
       is_required_valid ? base::ASCIIToUTF16("")
diff --git a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h
index 8aaf6f0..e9be3ef 100644
--- a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h
+++ b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller.h
@@ -36,13 +36,13 @@
       PaymentRequestSpec* spec,
       PaymentRequestState* state,
       PaymentRequestDialogView* dialog,
+      BackNavigationType back_navigation_type,
       base::OnceClosure on_edited,
       base::OnceCallback<void(const autofill::AutofillProfile&)> on_added,
       autofill::AutofillProfile* profile);
   ~ShippingAddressEditorViewController() override;
 
   // EditorViewController:
-  std::unique_ptr<views::View> CreateHeaderView() override;
   std::vector<EditorField> GetFieldDefinitions() override;
   base::string16 GetInitialValueForType(
       autofill::ServerFieldType type) override;
@@ -56,6 +56,7 @@
 
   // PaymentRequestSheetController:
   base::string16 GetSheetTitle() override;
+  std::unique_ptr<views::Button> CreatePrimaryButton() override;
 
  private:
   bool GetSheetId(DialogViewID* sheet_id) override;
diff --git a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller_browsertest.cc b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller_browsertest.cc
index 77386fe..90a3468 100644
--- a/chrome/browser/ui/views/payments/shipping_address_editor_view_controller_browsertest.cc
+++ b/chrome/browser/ui/views/payments/shipping_address_editor_view_controller_browsertest.cc
@@ -166,7 +166,7 @@
   base::RunLoop data_loop;
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
       .WillOnce(QuitMessageLoop(&data_loop));
-  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  ClickOnDialogViewAndWait(DialogViewID::SAVE_ADDRESS_BUTTON);
   data_loop.Run();
 
   ASSERT_EQ(1UL, personal_data_manager->GetProfiles().size());
@@ -249,7 +249,7 @@
   base::RunLoop data_loop;
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
       .WillOnce(QuitMessageLoop(&data_loop));
-  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  ClickOnDialogViewAndWait(DialogViewID::SAVE_ADDRESS_BUTTON);
   data_loop.Run();
 
   ASSERT_EQ(1UL, personal_data_manager->GetProfiles().size());
@@ -389,7 +389,7 @@
   base::RunLoop data_loop;
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
       .WillOnce(QuitMessageLoop(&data_loop));
-  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  ClickOnDialogViewAndWait(DialogViewID::SAVE_ADDRESS_BUTTON);
   data_loop.Run();
 
   ASSERT_EQ(1UL, personal_data_manager->GetProfiles().size());
@@ -428,7 +428,7 @@
   base::RunLoop data_loop;
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
       .WillOnce(QuitMessageLoop(&data_loop));
-  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  ClickOnDialogViewAndWait(DialogViewID::SAVE_ADDRESS_BUTTON);
   data_loop.Run();
 
   ASSERT_EQ(1UL, personal_data_manager->GetProfiles().size());
@@ -446,7 +446,7 @@
   autofill::AutofillProfile profile;
   profile.SetInfo(autofill::AutofillType(autofill::NAME_FULL),
                   base::ASCIIToUTF16(kNameFull), "fr_CA");
-  PaymentRequestBrowserTestBase::AddAutofillProfile(profile);
+  AddAutofillProfile(profile);
 
   InvokePaymentRequestUI();
 
@@ -479,7 +479,7 @@
   base::RunLoop data_loop;
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
       .WillOnce(QuitMessageLoop(&data_loop));
-  ClickOnDialogViewAndWait(DialogViewID::EDITOR_SAVE_BUTTON);
+  ClickOnDialogViewAndWait(DialogViewID::SAVE_ADDRESS_BUTTON);
   data_loop.Run();
 
   ExpectExistingRequiredFields(/*unset_types=*/nullptr);
diff --git a/chrome/renderer/autofill/OWNERS b/chrome/renderer/autofill/OWNERS
index 4afcbe9..86e92db 100644
--- a/chrome/renderer/autofill/OWNERS
+++ b/chrome/renderer/autofill/OWNERS
@@ -4,4 +4,7 @@
 sebsg@chromium.org
 vabr@chromium.org
 
+per-file *password*=dvadym@chromium.org
+per-file *password*=vasilii@chromium.org
+
 # COMPONENT: UI>Browser>Autofill
diff --git a/chrome/test/data/extensions/signin_screen_test_app/README b/chrome/test/data/extensions/signin_screen_test_app/README
new file mode 100644
index 0000000..1d0ade0
--- /dev/null
+++ b/chrome/test/data/extensions/signin_screen_test_app/README
@@ -0,0 +1,8 @@
+The CRX file must be a one signed by WebStore, in order for the app to have the
+expected ID which is whitelisted in Chrome - "bjaiihebfngildkcjkjckolinodhliff".
+
+This app will also be used for the manual testing of the apps on the Chrome OS
+sign-in screen.
+
+In case a new version of the test app has to be published on WebStore, please
+reach out to the managed-devices@ mailing list.
diff --git a/chrome/test/data/extensions/signin_screen_test_app/app.crx b/chrome/test/data/extensions/signin_screen_test_app/app.crx
new file mode 100644
index 0000000..deb82fa
--- /dev/null
+++ b/chrome/test/data/extensions/signin_screen_test_app/app.crx
Binary files differ
diff --git a/chrome/test/data/extensions/signin_screen_test_app/app/background.js b/chrome/test/data/extensions/signin_screen_test_app/app/background.js
new file mode 100644
index 0000000..bab4458
--- /dev/null
+++ b/chrome/test/data/extensions/signin_screen_test_app/app/background.js
@@ -0,0 +1,5 @@
+// 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.
+
+console.log('The sign-in screen test app loaded');
diff --git a/chrome/test/data/extensions/signin_screen_test_app/app/manifest.json b/chrome/test/data/extensions/signin_screen_test_app/app/manifest.json
new file mode 100644
index 0000000..d331322
--- /dev/null
+++ b/chrome/test/data/extensions/signin_screen_test_app/app/manifest.json
@@ -0,0 +1,11 @@
+{
+  "manifest_version": 2,
+  "name": "Sign-in Screen Test App",
+  "description": "The app for testing the apps installation in the Chrome OS sign-in profile",
+  "version": "2.0",
+  "app": {
+    "background": {
+      "scripts": ["background.js"]
+    }
+  }
+}
diff --git a/chrome/test/data/extensions/signin_screen_test_app/update_manifest.xml b/chrome/test/data/extensions/signin_screen_test_app/update_manifest.xml
new file mode 100644
index 0000000..85871e022
--- /dev/null
+++ b/chrome/test/data/extensions/signin_screen_test_app/update_manifest.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  This update manifest points to the ./app.crx file. It is meant to be used with
+  a URLRequestMockHTTPJob, and the hostname must be kept in sync.
+-->
+<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+  <app appid='bjaiihebfngildkcjkjckolinodhliff'>
+    <updatecheck
+        codebase='http://mock.http/extensions/signin_screen_test_app/app.crx'
+        version='2.0' />
+   </app>
+</gupdate>
diff --git a/chrome/test/data/extensions/trivial_extension/README b/chrome/test/data/extensions/trivial_extension/README
new file mode 100644
index 0000000..6ea167455
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_extension/README
@@ -0,0 +1,9 @@
+A minimal extension that requests no additional permissions and has only a
+trivial event page.
+
+The extension must be packed to ./extension.crx using the private key
+./extension.pem. For the instructions, refer to:
+<https://developer.chrome.com/extensions/packaging#creating>.
+
+The extension ID that corresponds to the ./extension.pem private key is
+"mockepjebcnmhmhcahfddgfcdgkdifnc".
diff --git a/chrome/test/data/extensions/trivial_extension/extension.crx b/chrome/test/data/extensions/trivial_extension/extension.crx
new file mode 100644
index 0000000..cc8c54d
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_extension/extension.crx
Binary files differ
diff --git a/chrome/test/data/extensions/trivial_extension/extension.pem b/chrome/test/data/extensions/trivial_extension/extension.pem
new file mode 100644
index 0000000..c878acf
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_extension/extension.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC1evMMzHebzwIc
+USJumXLu2cOgIRG49gwqBhnPDd1tVn1pRv++7ZJJMRMU9BtgRgELL2/6tPxxv6gv
+xNYOq+T68A3BUGGVMf0lweUXnAJEArBgnLIUiqeX+IfJdClIJ6KL+TuNL8jahB/k
+VErQ9ieT6ot+PAqxFmWWa1Ojn0dq8fMK3DOBZ/7WnTSMJi+GJk15PO75UjbhwMaW
+BDrQ/p1lZn3Ig3MA4GSWbtosumCp6DhGUddzW9pGtjr7NaCFEuD2kVCjCxFIPU9L
+kxR64c085HD7M0ZYNVbrhIRxhkEuUNXY92FVYVKTNrv1TNxehCEh4ijExJpg3b1p
+IIVBXltVAgMBAAECggEARKkWqaH2IsnIF6SOrV96/2j+PWglh1/3rDKQsyigvMw7
+OIh3G+pQLUbW3Feav+rcXDn/WM/+rp5/aEtFXVmlkVcfLSNXOBYom49nOG564PZP
+es3uBh2LWNDbttkvATCVq/WIlVsd16ajVkSZ5cMrqCfVzNW0idlaZ/WFOGN7prbt
+tbZNefby6SE+lG9ioFhb/Be8MiZN1sGVHsfNnS39d2kZaSwkqBR5PjBcJBuaOJBs
+vBIjYNgyWb4LfL3uFnpQyZK7LSo5a2YEwrolkSz2GvuU2jq/e3fh6eWoeGAF2MsF
+q6Yb7ROT5xsb+dkowfmgQDRcH7C3vJrN61zPaT2a5QKBgQDq0WVliqvc1e4CvFxl
+eunoRcRatAYSXilzEOhAamRsitotLKb1C2jqMFjuP272NvLvaxKPdN/g3wCyWlgL
+ol7NH2bsZcVlyQlOpj1In+ybRogsfGimt8cyW6rQNDKSLIu245ujo8/vkcNW18rq
+ceWQ/tzALwJOf7LhuNFCGFDnzwKBgQDF2dZBDNCg6aC2SmzItvygdb93IBeu4nB/
+GLtq9d8UpIDDb9oT7BnUgW3vmQG3CSJOr0viDG1NBnJb7L9c8TyD4ocWxkQVjkMl
+rDZazAM2kVY6tZ6cy6VAfg4btoXj4aQZDl3xzN8XIgusLiuSrYrEsOq/MrSvIJDJ
+w2xhVr0vmwKBgFo0hpczZkI3jG9QnM3/IcwwwPV3Ir37nkCtxgq41Es+LAIenSSV
+dYBBgmhDxgSfp0OCBmdFpptkD4h0XE5b2b9CyO8XSXWIfSLRiclUZjhgJ/+u6yTg
+hI072aqR4rt0kVd4qFl8nGyTuk6qJzFY/69ruLG7XU96juVXckvJu4rRAoGAX6U5
+YGUTjlai7Lz57eYYJnIFx07qZA7vl2GbaM/HsjD8Vn6CDFN9ATA4tzVekN+HiEZS
+Bss/I5dUPYAr2oB8Yr1l1OqwC0Okzld8R1SM0RpddzBJpz8DejP2GguYmluAeuRp
+IfWKe6JnxirjwZrHlOJ5+UhH9MlndUJV6JCIELUCgYBoMeQUv4LRioV29/8tF6XT
+QhiIbEWl3Zbld5N4ak5qZrqlC2QHU19L3OIvr1dYlwIvaT4PP8R90EIXKQ1ys6+7
+wDkuZLJZifnFYijgW5raJW13bcUZIc66nZ2+CJ5DNTZn5qMVZ6oEedhwfbMga8xB
+DDaSSvEVRfMApJWl3aDonA==
+-----END PRIVATE KEY-----
diff --git a/chrome/test/data/extensions/trivial_extension/extension/background.js b/chrome/test/data/extensions/trivial_extension/extension/background.js
new file mode 100644
index 0000000..022f51e
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_extension/extension/background.js
@@ -0,0 +1,5 @@
+// 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.
+
+console.log('The Trivial Test Extension background page loaded');
diff --git a/chrome/test/data/extensions/trivial_extension/extension/manifest.json b/chrome/test/data/extensions/trivial_extension/extension/manifest.json
new file mode 100644
index 0000000..b8e1e7a
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_extension/extension/manifest.json
@@ -0,0 +1,9 @@
+{
+  "name": "Trivial Test Extension",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "A minimal extension for testing purposes.",
+  "background": {
+    "scripts": ["background.js"]
+  }
+}
diff --git a/chrome/test/data/extensions/trivial_extension/update_manifest.xml b/chrome/test/data/extensions/trivial_extension/update_manifest.xml
new file mode 100644
index 0000000..86bc6f6
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_extension/update_manifest.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  This update manifest points to the ./extension.crx file. It is meant to be
+  used with a URLRequestMockHTTPJob, and the hostname must be kept in sync.
+-->
+<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+  <app appid='mockepjebcnmhmhcahfddgfcdgkdifnc'>
+    <updatecheck
+        codebase='http://mock.http/extensions/trivial_extension/extension.crx'
+        version='1.0' />
+   </app>
+</gupdate>
diff --git a/chrome/test/data/extensions/trivial_platform_app/README b/chrome/test/data/extensions/trivial_platform_app/README
new file mode 100644
index 0000000..1260376
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_platform_app/README
@@ -0,0 +1,8 @@
+A minimal platform app that requests no additional permissions and has only a
+trivial event page.
+
+The app must be packed to ./app.crx using the private key ./app.pem. For the
+instructions, refer to: <https://developer.chrome.com/apps/packaging#creating>.
+
+The app ID that corresponds to the ./app.pem private key is
+"mockapnacjbcdncmpkjngjalkhphojek".
diff --git a/chrome/test/data/extensions/trivial_platform_app/app.crx b/chrome/test/data/extensions/trivial_platform_app/app.crx
new file mode 100644
index 0000000..80f24f6
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_platform_app/app.crx
Binary files differ
diff --git a/chrome/test/data/extensions/trivial_platform_app/app.pem b/chrome/test/data/extensions/trivial_platform_app/app.pem
new file mode 100644
index 0000000..8694cc72
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_platform_app/app.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDg1G8BXj5b4ZZm
+pq4e/YWMLrahLD+0XJLqqlStXZV1jAQilXO1pbMsBfNS2p+MpgKaoFPtAD+95kJV
+fJSbSeNQEZtJl7XrEG5EtIOo4SsPw8/wrUdul3fOCTaanx+FS4PAoq3vUtyAKFoE
+x6YdXfPCkCpaf8Fl7JUV47mvbXCZrDsEKc/pf2GBgADIYGSMeWqbiZMDN7ALFggD
+Ztj/hKwNzi2pdrQlWj3cbJ5aaCuGxF+SwXFwk6w4W64O1c3VGP10nlkIVxwEitI5
+R84Lccde47SBzzLFKkwpwF7xKtxpUHjjGr++gegkIBfoty6f8UNqpKi/z+3fKsuw
+Rfm3OzFXAgMBAAECggEACN/53cj1lzTpdfXga7LAQc9aQDR580mwNvkwTIOkMbas
+Nx1FKwd2cidt+nhM0gV/ltD7XyCiY4GiXrODPjoFchSv15DYioXkhGvHwrPYuk+V
+dr+wUSpDfWuZrzLW0LExazpAuXDA6YIuQ4AKC6C4zZZZRadDYB2dqPaLTuR4DGjQ
+HsRsCt09h5FgYMf34AszdrdelMhGK+tUWhK/VXSuyD5sWFFYKkSLXJEklsInIm6k
+bFxVxTfttrHjO7LoIIuB7BDDWmGNoa5eNDNITL7T+UXzZzOgvGFNZNC9sQUDa68s
+h8oZtlRsmufx44tlxcguY4Zunv5RRQ+MYwgtFhrW+QKBgQDwiUZ/9OuABWtnwD6P
+2iRoi0hGv5V0a2kQSejvLjaDDF4izyREKcUBF8CieuZODhN5J1ua+bmNJcrFAFJ+
+bgmDN8uLueheJ3nloUQ4b9UM9tXniF+cTbaakDt2gNTpE8/IPOW9iVHwErDnBBSa
+lWYzJBcgZPPng4kPEsKuyxzAywKBgQDvSKnWaKAcx0vrcmCBE8vnQSfvhvA3DXez
+zPn23BcEmNIMwjUZPIbs5kRcdtZJE/m/CNx5MVDG1ztR/caEy1bgsxt+UzEXMqnS
+tcBxScM//MrwMA6EjgKb6qZnbGgda5wtu9DqBRTDMVfCNiWQqK4TxKQUiK6tQoPO
+xAzUivN8JQKBgBk32ztZG0Otzcfv4N9GGKEZUTeRqIKvLDG3/gLtjmuI5Wmy2/OF
+iZEGw3yFdBXk5efC15jU674/oxWqZfu5X6fxNpbso88TRnVrxfJAz05vppeCtbt2
+blmgG4veD/RrzlIJCHEpr3yDCSb/R4W5eiCMfRw5WVUBdmItLloTE22nAoGBANuS
+SBbxhwshbm8tU5Tef5tDVMwdfMxn1VHuY+m86HpVaVo/083GluFII67ddaE1bVV5
+Yibrs/3ifxKr1sEAfkZO1InE+SUaXm92TJMoZryO0gI5dvj3/zjnx67b8WYd2wC0
+ee5knzrGSotyeGIYBzUompAlJHTktmLD0CoxH2DpAoGBAJhdbttjro8o7x+63Q40
+fsHA0Wk9mzEAKHTOeQg1P1YIXBVhjkKJyhpioT8y3eV3rFVVY0Leq6vE9st+omuR
+BE/rcMqv/R+IIVdKiX8JDkcFRKR+fi5mZMvdz+xbSHo8pc2CeoL3KvbLR1+caIUQ
+A/6ko09GAb5+BqFXjjDS6EL9
+-----END PRIVATE KEY-----
diff --git a/chrome/test/data/extensions/trivial_platform_app/app/background.js b/chrome/test/data/extensions/trivial_platform_app/app/background.js
new file mode 100644
index 0000000..2ff741b
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_platform_app/app/background.js
@@ -0,0 +1,5 @@
+// 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.
+
+console.log('The Trivial Test App background page loaded');
diff --git a/chrome/test/data/extensions/trivial_platform_app/app/manifest.json b/chrome/test/data/extensions/trivial_platform_app/app/manifest.json
new file mode 100644
index 0000000..6f8e7cfb
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_platform_app/app/manifest.json
@@ -0,0 +1,11 @@
+{
+  "name": "Trivial Test Platform App",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "The minimal app for testing purposes.",
+  "app": {
+    "background": {
+      "scripts": ["background.js"]
+    }
+  }
+}
diff --git a/chrome/test/data/extensions/trivial_platform_app/update_manifest.xml b/chrome/test/data/extensions/trivial_platform_app/update_manifest.xml
new file mode 100644
index 0000000..14071838
--- /dev/null
+++ b/chrome/test/data/extensions/trivial_platform_app/update_manifest.xml
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  This update manifest points to the ./app.crx file. It is meant to be used with
+  a URLRequestMockHTTPJob, and the hostname must be kept in sync.
+-->
+<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+  <app appid='mockapnacjbcdncmpkjngjalkhphojek'>
+    <updatecheck
+        codebase='http://mock.http/extensions/trivial_platform_app/app.crx'
+        version='1.0' />
+   </app>
+</gupdate>
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index f9f4f57..f30d035 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -8,6 +8,8 @@
   sources = [
     "address.cc",
     "address.h",
+    "address_combobox_model.cc",
+    "address_combobox_model.h",
     "address_field.cc",
     "address_field.h",
     "address_i18n.cc",
@@ -315,6 +317,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "address_combobox_model_unittest.cc",
     "address_field_unittest.cc",
     "address_i18n_unittest.cc",
     "address_rewriter_unittest.cc",
diff --git a/components/autofill/core/browser/address_combobox_model.cc b/components/autofill/core/browser/address_combobox_model.cc
new file mode 100644
index 0000000..21559089
--- /dev/null
+++ b/components/autofill/core/browser/address_combobox_model.cc
@@ -0,0 +1,130 @@
+// 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/autofill/core/browser/address_combobox_model.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/combobox_model_observer.h"
+#include "ui/gfx/text_elider.h"
+
+namespace autofill {
+
+namespace {
+// There's one header entry to prompt the user to select an address, and a
+// separator.
+int kNbHeaderEntries = 2;
+}  // namespace
+
+AddressComboboxModel::AddressComboboxModel(
+    const PersonalDataManager& personal_data_manager,
+    const std::string& app_locale)
+    : app_locale_(app_locale) {
+  for (const auto* profile : personal_data_manager.GetProfilesToSuggest()) {
+    profiles_cache_.push_back(base::MakeUnique<AutofillProfile>(*profile));
+  }
+  UpdateAddresses();
+}
+
+AddressComboboxModel::~AddressComboboxModel() {}
+
+int AddressComboboxModel::GetItemCount() const {
+  // When there are not addresses, a special entry is shown to prompt the user
+  // to add addresses, but nothing else is shown, since there are no address to
+  // select from, and no need for a separator.
+  if (addresses_.size() == 0)
+    return 1;
+  // If there are addresses to choose from, but none is selected, add extra
+  // items for the "Select" entry and a separator.
+  return addresses_.size() + kNbHeaderEntries;
+}
+
+base::string16 AddressComboboxModel::GetItemAt(int index) {
+  DCHECK_GE(index, 0);
+  // A special entry is always added at index 0 and a separator at index 1.
+  DCHECK_LT(static_cast<size_t>(index), addresses_.size() + kNbHeaderEntries);
+
+  // Special entry when no profiles have been created yet.
+  if (addresses_.empty())
+    return l10n_util::GetStringUTF16(IDS_AUTOFILL_ADD_BILLING_ADDRESS);
+
+  // Always show the "Select" entry at the top, default selection position.
+  if (index == 0)
+    return l10n_util::GetStringUTF16(IDS_AUTOFILL_SELECT);
+
+  // Always show the "Select" entry at the top, default selection position.
+  if (index == 1)
+    return base::ASCIIToUTF16("---");
+
+  return addresses_[index - kNbHeaderEntries].second;
+}
+
+bool AddressComboboxModel::IsItemSeparatorAt(int index) {
+  // The only separator is between the "Select" entry at 0 and the first address
+  // at index 2. So there must be at least one address for a separator to be
+  // shown.
+  DCHECK(index <= kNbHeaderEntries || !addresses_.empty());
+  return index == 1;
+}
+
+void AddressComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void AddressComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+int AddressComboboxModel::AddNewProfile(const AutofillProfile& profile) {
+  profiles_cache_.push_back(base::MakeUnique<AutofillProfile>(profile));
+  UpdateAddresses();
+  DCHECK_GT(addresses_.size(), 0UL);
+  return addresses_.size() + kNbHeaderEntries - 1;
+}
+
+std::string AddressComboboxModel::GetItemIdentifierAt(int index) {
+  // The first two indices are special entries, with no addresses.
+  if (index < kNbHeaderEntries)
+    return std::string();
+  DCHECK_LT(static_cast<size_t>(index), addresses_.size() + kNbHeaderEntries);
+  return addresses_[index - kNbHeaderEntries].first;
+}
+
+int AddressComboboxModel::GetIndexOfIdentifier(const std::string& identifier) {
+  for (size_t i = 0; i < addresses_.size(); ++i) {
+    if (addresses_[i].first == identifier)
+      return i + kNbHeaderEntries;
+  }
+  return -1;
+}
+
+void AddressComboboxModel::UpdateAddresses() {
+  addresses_.clear();
+  std::vector<base::string16> labels;
+  // CreateDifferentiatingLabels is expecting a pointer vector and we keep
+  // profiles as unique_ptr.
+  std::vector<AutofillProfile*> profiles;
+  for (const auto& profile : profiles_cache_) {
+    profiles.push_back(profile.get());
+  }
+  AutofillProfile::CreateDifferentiatingLabels(profiles, app_locale_, &labels);
+  DCHECK_EQ(labels.size(), profiles_cache_.size());
+
+  for (size_t i = 0; i < profiles_cache_.size(); ++i) {
+    // Skip showing auxiliary profiles (e.g. Mac Contacts).
+    if (profiles_cache_[i]->record_type() == AutofillProfile::AUXILIARY_PROFILE)
+      continue;
+
+    addresses_.push_back(std::make_pair(profiles_cache_[i]->guid(), labels[i]));
+  }
+
+  for (auto& observer : observers_) {
+    observer.OnComboboxModelChanged(this);
+  }
+}
+}  // namespace autofill
diff --git a/components/autofill/core/browser/address_combobox_model.h b/components/autofill/core/browser/address_combobox_model.h
new file mode 100644
index 0000000..48edb33
--- /dev/null
+++ b/components/autofill/core/browser/address_combobox_model.h
@@ -0,0 +1,75 @@
+// 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_AUTOFILL_CORE_BROWSER_ADDRESS_COMBOBOX_MODEL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_COMBOBOX_MODEL_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/strings/string16.h"
+#include "ui/base/models/combobox_model.h"
+
+namespace autofill {
+
+class AutofillProfile;
+class PersonalDataManager;
+
+// A combobox model for listing addresses by a generated user visible string and
+// have a unique id to identify the one selected by the user.
+class AddressComboboxModel : public ui::ComboboxModel {
+ public:
+  // Enumerate the profiles from |personal_data_manager| to expose them in a
+  // combobox using |app_locale| for proper format.
+  AddressComboboxModel(const PersonalDataManager& personal_data_manager,
+                       const std::string& app_locale);
+  ~AddressComboboxModel() override;
+
+  // ui::ComboboxModel implementation:
+  int GetItemCount() const override;
+  base::string16 GetItemAt(int index) override;
+  bool IsItemSeparatorAt(int index) override;
+  void AddObserver(ui::ComboboxModelObserver* observer) override;
+  void RemoveObserver(ui::ComboboxModelObserver* observer) override;
+
+  // Adds |profile| to model and return its combobox index. The lifespan of
+  // |profile| beyond this call is undefined so a copy must be made.
+  int AddNewProfile(const AutofillProfile& profile);
+
+  // Returns the unique identifier of the profile at |index|, unless |index|
+  // refers to a special entry, in which case an empty string is returned.
+  std::string GetItemIdentifierAt(int index);
+
+  // Returns the combobox index of the item with the given id or -1 if it's not
+  // found.
+  int GetIndexOfIdentifier(const std::string& identifier);
+
+ private:
+  // Update |addresses_| based on |profiles_cache_| and notify observers.
+  void UpdateAddresses();
+
+  // List of <id, user visible string> pairs for the addresses extracted from
+  // the |personal_data_manager| passed in the constructor.
+  std::vector<std::pair<std::string, base::string16>> addresses_;
+
+  // A cached copy of all profiles to allow rebuilding the differentiating
+  // labels when new profiles are added.
+  std::vector<std::unique_ptr<AutofillProfile>> profiles_cache_;
+
+  // Application locale, also needed when a new profile is added.
+  std::string app_locale_;
+
+  // To be called when the data for the given country code was loaded.
+  base::ObserverList<ui::ComboboxModelObserver> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(AddressComboboxModel);
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_COMBOBOX_MODEL_H_
diff --git a/components/autofill/core/browser/address_combobox_model_unittest.cc b/components/autofill/core/browser/address_combobox_model_unittest.cc
new file mode 100644
index 0000000..2543a9b3
--- /dev/null
+++ b/components/autofill/core/browser/address_combobox_model_unittest.cc
@@ -0,0 +1,89 @@
+// 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/autofill/core/browser/address_combobox_model.h"
+
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/region_data.h"
+
+namespace autofill {
+
+namespace {
+const char kAppLocale[] = "fr-CA";
+}
+
+TEST(AddressComboboxModelTest, Empty) {
+  TestPersonalDataManager test_personal_data_manager;
+
+  AddressComboboxModel model(test_personal_data_manager, kAppLocale);
+  EXPECT_EQ(1, model.GetItemCount());
+  EXPECT_FALSE(model.IsItemSeparatorAt(0));
+  EXPECT_TRUE(model.GetItemIdentifierAt(0).empty());
+  EXPECT_EQ(-1, model.GetIndexOfIdentifier("Anything"));
+}
+
+TEST(AddressComboboxModelTest, OneAddress) {
+  TestPersonalDataManager test_personal_data_manager;
+  AutofillProfile profile1(test::GetFullProfile());
+  test_personal_data_manager.AddTestingProfile(&profile1);
+
+  AddressComboboxModel model(test_personal_data_manager, kAppLocale);
+  EXPECT_EQ(3, model.GetItemCount());
+  EXPECT_FALSE(model.IsItemSeparatorAt(0));
+  EXPECT_TRUE(model.IsItemSeparatorAt(1));
+  EXPECT_TRUE(model.GetItemIdentifierAt(0).empty());
+  EXPECT_TRUE(model.GetItemIdentifierAt(1).empty());
+  EXPECT_EQ(-1, model.GetIndexOfIdentifier("Anything"));
+  EXPECT_EQ(profile1.guid(), model.GetItemIdentifierAt(2));
+  EXPECT_EQ(2, model.GetIndexOfIdentifier(profile1.guid()));
+}
+
+TEST(AddressComboboxModelTest, TwoAddresses) {
+  TestPersonalDataManager test_personal_data_manager;
+  AutofillProfile profile1(test::GetFullProfile());
+  AutofillProfile profile2(test::GetFullProfile2());
+
+  // Force |profile1| to be shown first in the combobox.
+  profile1.set_use_count(100);
+  test_personal_data_manager.AddTestingProfile(&profile1);
+  test_personal_data_manager.AddTestingProfile(&profile2);
+
+  AddressComboboxModel model(test_personal_data_manager, kAppLocale);
+  EXPECT_EQ(4, model.GetItemCount());
+  EXPECT_FALSE(model.IsItemSeparatorAt(0));
+  EXPECT_TRUE(model.IsItemSeparatorAt(1));
+  EXPECT_TRUE(model.GetItemIdentifierAt(0).empty());
+  EXPECT_TRUE(model.GetItemIdentifierAt(1).empty());
+  EXPECT_EQ(-1, model.GetIndexOfIdentifier("Anything"));
+  EXPECT_EQ(profile1.guid(), model.GetItemIdentifierAt(2));
+  EXPECT_EQ(profile2.guid(), model.GetItemIdentifierAt(3));
+  EXPECT_EQ(2, model.GetIndexOfIdentifier(profile1.guid()));
+  EXPECT_EQ(3, model.GetIndexOfIdentifier(profile2.guid()));
+}
+
+TEST(AddressComboboxModelTest, AddAnAddress) {
+  TestPersonalDataManager test_personal_data_manager;
+  AutofillProfile profile1(test::GetFullProfile());
+  test_personal_data_manager.AddTestingProfile(&profile1);
+
+  AddressComboboxModel model(test_personal_data_manager, kAppLocale);
+  EXPECT_EQ(3, model.GetItemCount());
+  EXPECT_EQ(profile1.guid(), model.GetItemIdentifierAt(2));
+  EXPECT_EQ(2, model.GetIndexOfIdentifier(profile1.guid()));
+
+  AutofillProfile profile2(test::GetFullProfile2());
+  int new_profile_index = model.AddNewProfile(profile2);
+  EXPECT_EQ(3, new_profile_index);
+  EXPECT_EQ(4, model.GetItemCount());
+  EXPECT_EQ(profile2.guid(), model.GetItemIdentifierAt(3));
+  EXPECT_EQ(3, model.GetIndexOfIdentifier(profile2.guid()));
+
+  // First profile shouldn't have changed, here the order is guaranteed.
+  EXPECT_EQ(profile1.guid(), model.GetItemIdentifierAt(2));
+  EXPECT_EQ(2, model.GetIndexOfIdentifier(profile1.guid()));
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_assistant_unittest.cc b/components/autofill/core/browser/autofill_assistant_unittest.cc
index d5b27e1..d7fb2f02 100644
--- a/components/autofill/core/browser/autofill_assistant_unittest.cc
+++ b/components/autofill/core/browser/autofill_assistant_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/autofill/core/browser/autofill_assistant.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/callback.h"
 #include "base/feature_list.h"
@@ -231,7 +232,8 @@
 
   // Create a valid card for the assist.
   CreditCard card;
-  test::SetCreditCardInfo(&card, "John Doe", "4111111111111111", "05", "2999");
+  test::SetCreditCardInfo(&card, "John Doe", "4111111111111111", "05", "2999",
+                          "1");
 
   // FillCreditCardForm should not be called if the user cancelled the CVC.
   EXPECT_CALL(autofill_manager_, FillCreditCardForm(_, _, _, _, _)).Times(0);
@@ -253,7 +255,8 @@
 
   // Create a valid card for the assist.
   CreditCard card;
-  test::SetCreditCardInfo(&card, "John Doe", "4111111111111111", "05", "2999");
+  test::SetCreditCardInfo(&card, "John Doe", "4111111111111111", "05", "2999",
+                          "1");
 
   // FillCreditCardForm ends up being called after user has accepted the
   // prompt.
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index 97bd64b3..11f7ce9 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -690,7 +690,7 @@
 // credit card.
 TEST_F(AutofillExternalDelegateUnitTest, FillCreditCardForm) {
   CreditCard card;
-  test::SetCreditCardInfo(&card, "Alice", "4111", "1", "3000");
+  test::SetCreditCardInfo(&card, "Alice", "4111", "1", "3000", "1");
   EXPECT_CALL(*autofill_manager_,
       FillCreditCardForm(_, _, _, CreditCardMatches(card), base::string16()));
   external_delegate_->OnCreditCardScanned(card);
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index be3d39fd..99e626a 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -241,7 +241,7 @@
     std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>();
     test::SetCreditCardInfo(credit_card.get(), "Elvis Presley",
                             "4234 5678 9012 3456",  // Visa
-                            "04", "2999");
+                            "04", "2999", "1");
     credit_card->set_guid("00000000-0000-0000-0000-000000000008");
     local_credit_cards_.push_back(std::move(credit_card));
   }
@@ -252,7 +252,7 @@
     std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>();
     test::SetCreditCardInfo(credit_card.get(), "Elvis Presley",
                             "4234-5678-9012-3456",  // Visa
-                            "04", "2999");
+                            "04", "2999", "1");
     credit_card->set_guid("00000000-0000-0000-0000-000000000009");
     local_credit_cards_.push_back(std::move(credit_card));
   }
@@ -262,7 +262,7 @@
     std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>();
     test::SetCreditCardInfo(credit_card.get(), "Miku Hatsune",
                             "4234567890654321",  // Visa
-                            month, year);
+                            month, year, "1");
     credit_card->set_guid("00000000-0000-0000-0000-000000000007");
     local_credit_cards_.push_back(std::move(credit_card));
   }
@@ -272,7 +272,7 @@
     std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>();
     test::SetCreditCardInfo(credit_card.get(), "Homer Simpson",
                             "4234567890654321",  // Visa
-                            "05", "2000");
+                            "05", "2000", "1");
     credit_card->set_guid("00000000-0000-0000-0000-000000000009");
     local_credit_cards_.push_back(std::move(credit_card));
   }
@@ -306,7 +306,7 @@
     std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>();
     test::SetCreditCardInfo(credit_card.get(), "Elvis Presley",
                             "4234567890123456",  // Visa
-                            "04", "2999");
+                            "04", "2999", "1");
     credit_card->set_guid("00000000-0000-0000-0000-000000000004");
     credit_card->set_use_count(10);
     credit_card->set_use_date(base::Time::Now() - base::TimeDelta::FromDays(5));
@@ -315,14 +315,14 @@
     credit_card = base::MakeUnique<CreditCard>();
     test::SetCreditCardInfo(credit_card.get(), "Buddy Holly",
                             "5187654321098765",  // Mastercard
-                            "10", "2998");
+                            "10", "2998", "1");
     credit_card->set_guid("00000000-0000-0000-0000-000000000005");
     credit_card->set_use_count(5);
     credit_card->set_use_date(base::Time::Now() - base::TimeDelta::FromDays(4));
     credit_cards->push_back(std::move(credit_card));
 
     credit_card = base::MakeUnique<CreditCard>();
-    test::SetCreditCardInfo(credit_card.get(), "", "", "", "");
+    test::SetCreditCardInfo(credit_card.get(), "", "", "", "", "");
     credit_card->set_guid("00000000-0000-0000-0000-000000000006");
     credit_cards->push_back(std::move(credit_card));
   }
@@ -1036,7 +1036,7 @@
     FormsSeen(std::vector<FormData>(1, *form));
     *card = CreditCard(CreditCard::MASKED_SERVER_CARD, "a123");
     test::SetCreditCardInfo(card, "John Dillinger", "1881" /* Visa */, "01",
-                            "2017");
+                            "2017", "1");
     card->SetNetworkForMaskedCard(kVisaCard);
 
     EXPECT_CALL(*autofill_driver_, SendFormDataToRenderer(_, _, _))
@@ -1675,7 +1675,7 @@
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "John Smith",
                           "5255667890123123",  // Mastercard
-                          "08", "2017");
+                          "08", "2017", "1");
   credit_card.set_guid("00000000-0000-0000-0000-000000000007");
   autofill_manager_->AddCreditCard(credit_card);
 
@@ -1959,7 +1959,7 @@
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "Elvis Presley",
                           "5231567890123456",  // Mastercard
-                          "05", "2999");
+                          "05", "2999", "1");
   credit_card.set_guid("00000000-0000-0000-0000-000000000007");
   credit_card.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(15));
   autofill_manager_->AddCreditCard(credit_card);
@@ -3911,7 +3911,7 @@
   std::vector<CreditCard> credit_cards;
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "John Doe", "4234-5678-9012-3456", "04",
-                          "2999");
+                          "2999", "1");
   credit_card.set_guid("00000000-0000-0000-0000-000000000003");
   credit_cards.push_back(credit_card);
 
@@ -4098,7 +4098,7 @@
   std::vector<CreditCard> credit_cards;
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "Elvis Presley", "4234-5678-9012-3456",
-                          "04", "2999");
+                          "04", "2999", "1");
   credit_card.set_guid("00000000-0000-0000-0000-000000000003");
   credit_cards.push_back(credit_card);
 
@@ -5956,7 +5956,8 @@
   // Add a masked credit card whose |TypeAndLastFourDigits| matches what we will
   // below.
   CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
-  test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", "2017");
+  test::SetCreditCardInfo(&credit_card, "Flo Master", "1111", "11", "2017",
+                          "1");
   credit_card.SetNetworkForMaskedCard(kVisaCard);
   personal_data_.AddServerCreditCard(credit_card);
 
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 4399101..ea8e737 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -170,7 +170,7 @@
       std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>(
           "10000000-0000-0000-0000-000000000001", std::string());
       test::SetCreditCardInfo(credit_card.get(), nullptr, "4111111111111111",
-                              "12", "24");
+                              "12", "24", "1");
       local_credit_cards_.push_back(std::move(credit_card));
     }
     if (include_masked_server_credit_card) {
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index 991b907..a851aeb 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -227,14 +227,14 @@
 CreditCard GetCreditCard() {
   CreditCard credit_card(base::GenerateGUID(), "http://www.example.com");
   SetCreditCardInfo(&credit_card, "Test User", "4111111111111111" /* Visa */,
-                    "11", "2022");
+                    "11", "2022", "1");
   return credit_card;
 }
 
 CreditCard GetCreditCard2() {
   CreditCard credit_card(base::GenerateGUID(), "https://www.example.com");
   SetCreditCardInfo(&credit_card, "Someone Else", "378282246310005" /* AmEx */,
-                    "07", "2022");
+                    "07", "2022", "1");
   return credit_card;
 }
 
@@ -253,15 +253,15 @@
 CreditCard GetMaskedServerCard() {
   CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123");
   test::SetCreditCardInfo(&credit_card, "Bonnie Parker",
-                          "2109" /* Mastercard */, "12", "2020");
+                          "2109" /* Mastercard */, "12", "2020", "1");
   credit_card.SetNetworkForMaskedCard(kMasterCard);
   return credit_card;
 }
 
 CreditCard GetMaskedServerCardAmex() {
   CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "b456");
-  test::SetCreditCardInfo(&credit_card, "Justin Thyme",
-                          "8431" /* Amex */, "9", "2020");
+  test::SetCreditCardInfo(&credit_card, "Justin Thyme", "8431" /* Amex */, "9",
+                          "2020", "1");
   credit_card.SetNetworkForMaskedCard(kAmericanExpressCard);
   return credit_card;
 }
@@ -300,12 +300,16 @@
 }
 
 void SetCreditCardInfo(CreditCard* credit_card,
-    const char* name_on_card, const char* card_number,
-    const char* expiration_month, const char* expiration_year) {
+                       const char* name_on_card,
+                       const char* card_number,
+                       const char* expiration_month,
+                       const char* expiration_year,
+                       const std::string& billing_address_id) {
   check_and_set(credit_card, CREDIT_CARD_NAME_FULL, name_on_card);
   check_and_set(credit_card, CREDIT_CARD_NUMBER, card_number);
   check_and_set(credit_card, CREDIT_CARD_EXP_MONTH, expiration_month);
   check_and_set(credit_card, CREDIT_CARD_EXP_4_DIGIT_YEAR, expiration_year);
+  credit_card->set_billing_address_id(billing_address_id);
 }
 
 void DisableSystemServices(PrefService* prefs) {
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index 3e6be716..6bd447e6 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_TEST_UTILS_H_
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "components/autofill/core/browser/field_types.h"
@@ -107,8 +108,11 @@
 // tests.  |SetCreditCardInfo| provides a quick way to populate a credit card
 // with c-strings.
 void SetCreditCardInfo(CreditCard* credit_card,
-    const char* name_on_card, const char* card_number,
-    const char* expiration_month, const char* expiration_year);
+                       const char* name_on_card,
+                       const char* card_number,
+                       const char* expiration_month,
+                       const char* expiration_year,
+                       const std::string& billing_address_id);
 
 // TODO(isherman): We should do this automatically for all tests, not manually
 // on a per-test basis: http://crbug.com/57221
diff --git a/components/autofill/core/browser/credit_card.cc b/components/autofill/core/browser/credit_card.cc
index 0d35c34..2b8d2a03 100644
--- a/components/autofill/core/browser/credit_card.cc
+++ b/components/autofill/core/browser/credit_card.cc
@@ -678,7 +678,9 @@
   if ((!name_on_card_.empty() && name_on_card_ != other.name_on_card_) ||
       (expiration_month_ != 0 &&
        expiration_month_ != other.expiration_month_) ||
-      (expiration_year_ != 0 && expiration_year_ != other.expiration_year_)) {
+      (expiration_year_ != 0 && expiration_year_ != other.expiration_year_) ||
+      (!billing_address_id_.empty() &&
+       billing_address_id_ != other.billing_address_id_)) {
     return false;
   }
 
diff --git a/components/autofill/core/browser/credit_card_unittest.cc b/components/autofill/core/browser/credit_card_unittest.cc
index ff5a80f..2636b12 100644
--- a/components/autofill/core/browser/credit_card_unittest.cc
+++ b/components/autofill/core/browser/credit_card_unittest.cc
@@ -83,7 +83,7 @@
 
   // Case 00: Empty credit card with empty strings.
   CreditCard credit_card00(base::GenerateGUID(), "https://www.example.com/");
-  test::SetCreditCardInfo(&credit_card00, "John Dillinger", "", "", "");
+  test::SetCreditCardInfo(&credit_card00, "John Dillinger", "", "", "", "");
   base::string16 summary00 = credit_card00.Label();
   EXPECT_EQ(base::string16(ASCIIToUTF16("John Dillinger")), summary00);
   base::string16 obfuscated00 = credit_card00.NetworkAndLastFourDigits();
@@ -91,7 +91,8 @@
 
   // Case 1: No credit card number.
   CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com/");
-  test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2010");
+  test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2010",
+                          "1");
   base::string16 summary1 = credit_card1.Label();
   EXPECT_EQ(base::string16(ASCIIToUTF16("John Dillinger")), summary1);
   base::string16 obfuscated1 = credit_card1.NetworkAndLastFourDigits();
@@ -99,8 +100,8 @@
 
   // Case 2: No month.
   CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/");
-  test::SetCreditCardInfo(
-      &credit_card2, "John Dillinger", "5105 1051 0510 5100", "", "2010");
+  test::SetCreditCardInfo(&credit_card2, "John Dillinger",
+                          "5105 1051 0510 5100", "", "2010", "1");
   base::string16 summary2 = credit_card2.Label();
   EXPECT_EQ(
       UTF8ToUTF16(std::string("MasterCard") + kUTF8MidlineEllipsis + "5100"),
@@ -112,8 +113,8 @@
 
   // Case 3: No year.
   CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com/");
-  test::SetCreditCardInfo(
-      &credit_card3, "John Dillinger", "5105 1051 0510 5100", "01", "");
+  test::SetCreditCardInfo(&credit_card3, "John Dillinger",
+                          "5105 1051 0510 5100", "01", "", "1");
   base::string16 summary3 = credit_card3.Label();
   EXPECT_EQ(
       UTF8ToUTF16(std::string("MasterCard") + kUTF8MidlineEllipsis + "5100"),
@@ -125,8 +126,8 @@
 
   // Case 4: Have everything.
   CreditCard credit_card4(base::GenerateGUID(), "https://www.example.com/");
-  test::SetCreditCardInfo(
-      &credit_card4, "John Dillinger", "5105 1051 0510 5100", "01", "2010");
+  test::SetCreditCardInfo(&credit_card4, "John Dillinger",
+                          "5105 1051 0510 5100", "01", "2010", "1");
   base::string16 summary4 = credit_card4.Label();
   EXPECT_EQ(UTF8ToUTF16(std::string("MasterCard") + kUTF8MidlineEllipsis +
                         "5100, 01/2010"),
@@ -139,9 +140,9 @@
   // Case 5: Very long credit card
   CreditCard credit_card5(base::GenerateGUID(), "https://www.example.com/");
   test::SetCreditCardInfo(
-      &credit_card5,
-      "John Dillinger",
-      "0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010");
+      &credit_card5, "John Dillinger",
+      "0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010",
+      "1");
   base::string16 summary5 = credit_card5.Label();
   EXPECT_EQ(
       UTF8ToUTF16(std::string("Card") + kUTF8MidlineEllipsis + "5100, 01/2010"),
@@ -153,7 +154,8 @@
 
 TEST(CreditCardTest, AssignmentOperator) {
   CreditCard a(base::GenerateGUID(), "some origin");
-  test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010");
+  test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010",
+                          "1");
 
   // Result of assignment should be logically equal to the original profile.
   CreditCard b(base::GenerateGUID(), "some other origin");
@@ -251,7 +253,8 @@
 
 TEST(CreditCardTest, Copy) {
   CreditCard a(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010");
+  test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010",
+                          base::GenerateGUID());
 
   // Clone should be logically equal to the original.
   CreditCard b(a);
@@ -264,12 +267,14 @@
   const char* first_card_number;
   const char* first_card_exp_mo;
   const char* first_card_exp_yr;
+  const char* first_billing_address_id;
 
   CreditCard::RecordType second_card_record_type;
   const char* second_card_name;
   const char* second_card_number;
   const char* second_card_exp_mo;
   const char* second_card_exp_yr;
+  const char* second_billing_address_id;
   const char* second_card_issuer_network;
 
   bool is_local_duplicate;
@@ -284,13 +289,15 @@
   a.set_record_type(test_case.first_card_record_type);
   test::SetCreditCardInfo(
       &a, test_case.first_card_name, test_case.first_card_number,
-      test_case.first_card_exp_mo, test_case.first_card_exp_yr);
+      test_case.first_card_exp_mo, test_case.first_card_exp_yr,
+      test_case.first_billing_address_id);
 
   CreditCard b(base::GenerateGUID(), std::string());
   b.set_record_type(test_case.second_card_record_type);
   test::SetCreditCardInfo(
       &b, test_case.second_card_name, test_case.second_card_number,
-      test_case.second_card_exp_mo, test_case.second_card_exp_yr);
+      test_case.second_card_exp_mo, test_case.second_card_exp_yr,
+      test_case.second_billing_address_id);
 
   if (test_case.second_card_record_type == CreditCard::MASKED_SERVER_CARD)
     b.SetNetworkForMaskedCard(test_case.second_card_issuer_network);
@@ -303,39 +310,43 @@
     CreditCardTest,
     IsLocalDuplicateOfServerCardTest,
     testing::Values(
-        IsLocalDuplicateOfServerCardTestCase{LOCAL_CARD, "", "", "", "",
-                                             LOCAL_CARD, "", "", "", "",
+        IsLocalDuplicateOfServerCardTestCase{LOCAL_CARD, "", "", "", "", "",
+                                             LOCAL_CARD, "", "", "", "", "",
                                              nullptr, false},
-        IsLocalDuplicateOfServerCardTestCase{LOCAL_CARD, "", "", "", "",
+        IsLocalDuplicateOfServerCardTestCase{LOCAL_CARD, "", "", "", "", "",
                                              FULL_SERVER_CARD, "", "", "", "",
-                                             nullptr, true},
+                                             "", nullptr, true},
         IsLocalDuplicateOfServerCardTestCase{FULL_SERVER_CARD, "", "", "", "",
-                                             FULL_SERVER_CARD, "", "", "", "",
-                                             nullptr, false},
+                                             "", FULL_SERVER_CARD, "", "", "",
+                                             "", "", nullptr, false},
         IsLocalDuplicateOfServerCardTestCase{
-            LOCAL_CARD, "John Dillinger", "423456789012", "01", "2010",
+            LOCAL_CARD, "John Dillinger", "423456789012", "01", "2010", "1",
             FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010",
-            nullptr, true},
+            "1", nullptr, true},
         IsLocalDuplicateOfServerCardTestCase{
-            LOCAL_CARD, "J Dillinger", "423456789012", "01", "2010",
+            LOCAL_CARD, "J Dillinger", "423456789012", "01", "2010", "1",
             FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010",
-            nullptr, false},
+            "1", nullptr, false},
         IsLocalDuplicateOfServerCardTestCase{
-            LOCAL_CARD, "", "423456789012", "01", "2010", FULL_SERVER_CARD,
-            "John Dillinger", "423456789012", "01", "2010", nullptr, true},
+            LOCAL_CARD, "", "423456789012", "01", "2010", "1", FULL_SERVER_CARD,
+            "John Dillinger", "423456789012", "01", "2010", "1", nullptr, true},
         IsLocalDuplicateOfServerCardTestCase{
-            LOCAL_CARD, "", "423456789012", "", "", FULL_SERVER_CARD,
-            "John Dillinger", "423456789012", "01", "2010", nullptr, true},
+            LOCAL_CARD, "", "423456789012", "", "", "1", FULL_SERVER_CARD,
+            "John Dillinger", "423456789012", "01", "2010", "1", nullptr, true},
         IsLocalDuplicateOfServerCardTestCase{
-            LOCAL_CARD, "", "423456789012", "", "", MASKED_SERVER_CARD,
-            "John Dillinger", "9012", "01", "2010", kVisaCard, true},
+            LOCAL_CARD, "", "423456789012", "", "", "1", MASKED_SERVER_CARD,
+            "John Dillinger", "9012", "01", "2010", "1", kVisaCard, true},
         IsLocalDuplicateOfServerCardTestCase{
-            LOCAL_CARD, "", "423456789012", "", "", MASKED_SERVER_CARD,
-            "John Dillinger", "9012", "01", "2010", kMasterCard, false},
+            LOCAL_CARD, "", "423456789012", "", "", "1", MASKED_SERVER_CARD,
+            "John Dillinger", "9012", "01", "2010", "1", kMasterCard, false},
         IsLocalDuplicateOfServerCardTestCase{
-            LOCAL_CARD, "John Dillinger", "4234-5678-9012", "01", "2010",
+            LOCAL_CARD, "John Dillinger", "4234-5678-9012", "01", "2010", "1",
             FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010",
-            nullptr, true}));
+            "1", nullptr, true},
+        IsLocalDuplicateOfServerCardTestCase{
+            LOCAL_CARD, "John Dillinger", "4234-5678-9012", "01", "2010", "1",
+            FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010",
+            "2", nullptr, false}));
 
 TEST(CreditCardTest, HasSameNumberAs) {
   CreditCard a(base::GenerateGUID(), std::string());
@@ -390,8 +401,8 @@
   EXPECT_EQ(0, a.Compare(b));
 
   // Different values produce non-zero results.
-  test::SetCreditCardInfo(&a, "Jimmy", NULL, NULL, NULL);
-  test::SetCreditCardInfo(&b, "Ringo", NULL, NULL, NULL);
+  test::SetCreditCardInfo(&a, "Jimmy", NULL, NULL, NULL, "");
+  test::SetCreditCardInfo(&b, "Ringo", NULL, NULL, NULL, "");
   EXPECT_GT(0, a.Compare(b));
   EXPECT_LT(0, b.Compare(a));
 }
@@ -422,8 +433,8 @@
 
 TEST(CreditCardTest, UpdateFromImportedCard) {
   CreditCard original_card(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(
-      &original_card, "John Dillinger", "123456789012", "09", "2017");
+  test::SetCreditCardInfo(&original_card, "John Dillinger", "123456789012",
+                          "09", "2017", "1");
 
   CreditCard a = original_card;
 
@@ -578,8 +589,8 @@
 TEST(CreditCardTest, SetRawInfoCreditCardNumber) {
   CreditCard card(base::GenerateGUID(), "https://www.example.com/");
 
-  test::SetCreditCardInfo(&card, "Bob Dylan",
-                          "4321-5432-6543-xxxx", "07", "2013");
+  test::SetCreditCardInfo(&card, "Bob Dylan", "4321-5432-6543-xxxx", "07",
+                          "2013", "1");
   EXPECT_EQ(ASCIIToUTF16("4321-5432-6543-xxxx"),
             card.GetRawInfo(CREDIT_CARD_NUMBER));
 }
@@ -851,8 +862,8 @@
   CreditCard card(base::GenerateGUID(), "https://www.example.com/");
   ASSERT_EQ(base::string16(), card.LastFourDigits());
 
-  test::SetCreditCardInfo(&card, "Baby Face Nelson",
-                          "5212341234123489", "01", "2010");
+  test::SetCreditCardInfo(&card, "Baby Face Nelson", "5212341234123489", "01",
+                          "2010", "1");
   ASSERT_EQ(base::ASCIIToUTF16("3489"), card.LastFourDigits());
 
   card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("3489"));
@@ -1022,12 +1033,13 @@
   credit_card0.set_use_count(1);
   credit_card0.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(1));
   test::SetCreditCardInfo(&credit_card0, "John Dillinger",
-                          "423456789012" /* Visa */, "01", "2021");
+                          "423456789012" /* Visa */, "01", "2021", "1");
 
   // Test for last used date.
   CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card1, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2021");
+                          "347666888555" /* American Express */, "04", "2021",
+                          "1");
   credit_card1.set_use_count(10);
   credit_card1.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(10));
 
@@ -1036,7 +1048,7 @@
   credit_card2.set_use_count(5);
   credit_card2.set_use_date(kArbitraryTime - base::TimeDelta::FromDays(366));
   test::SetCreditCardInfo(&credit_card2, "Bonnie Parker",
-                          "518765432109" /* Mastercard */, "12", "2021");
+                          "518765432109" /* Mastercard */, "12", "2021", "1");
 
   static const struct {
     const char* show_expiration_date;
diff --git a/components/autofill/core/browser/payments/full_card_request_unittest.cc b/components/autofill/core/browser/payments/full_card_request_unittest.cc
index a9455bee..5d01e9b 100644
--- a/components/autofill/core/browser/payments/full_card_request_unittest.cc
+++ b/components/autofill/core/browser/payments/full_card_request_unittest.cc
@@ -167,7 +167,7 @@
               OnUnmaskVerificationResult(AutofillClient::SUCCESS));
 
   CreditCard card;
-  test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050");
+  test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050", "1");
   request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL,
                          result_delegate()->AsWeakPtr(),
                          ui_delegate()->AsWeakPtr());
@@ -188,7 +188,8 @@
               OnUnmaskVerificationResult(AutofillClient::SUCCESS));
 
   CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "server_id");
-  test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "12", "2050");
+  test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "12", "2050",
+                          "1");
   request()->GetFullCard(full_server_card, AutofillClient::UNMASK_FOR_AUTOFILL,
                          result_delegate()->AsWeakPtr(),
                          ui_delegate()->AsWeakPtr());
@@ -212,7 +213,8 @@
               OnUnmaskVerificationResult(AutofillClient::SUCCESS));
 
   CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "server_id");
-  test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "12", "2050");
+  test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "12", "2050",
+                          "1");
   full_server_card.SetServerStatus(CreditCard::EXPIRED);
   request()->GetFullCard(full_server_card, AutofillClient::UNMASK_FOR_AUTOFILL,
                          result_delegate()->AsWeakPtr(),
@@ -242,7 +244,8 @@
   base::Time::Now().LocalExplode(&today);
   CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "server_id");
   test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "12",
-                          base::StringPrintf("%d", today.year - 1).c_str());
+                          base::StringPrintf("%d", today.year - 1).c_str(),
+                          "1");
   full_server_card.SetServerStatus(CreditCard::OK);
   request()->GetFullCard(full_server_card, AutofillClient::UNMASK_FOR_AUTOFILL,
                          result_delegate()->AsWeakPtr(),
@@ -286,7 +289,7 @@
       .Times(2);
 
   CreditCard card;
-  test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050");
+  test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050", "1");
   request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL,
                          result_delegate()->AsWeakPtr(),
                          ui_delegate()->AsWeakPtr());
@@ -435,7 +438,8 @@
               OnUnmaskVerificationResult(AutofillClient::SUCCESS));
 
   CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "server_id");
-  test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "10", "2000");
+  test::SetCreditCardInfo(&full_server_card, nullptr, "4111", "10", "2000",
+                          "1");
   request()->GetFullCard(full_server_card, AutofillClient::UNMASK_FOR_AUTOFILL,
                          result_delegate()->AsWeakPtr(),
                          ui_delegate()->AsWeakPtr());
@@ -465,7 +469,8 @@
   base::Time::Now().LocalExplode(&today);
   CreditCard card;
   test::SetCreditCardInfo(&card, nullptr, "4111", "10",
-                          base::StringPrintf("%d", today.year - 1).c_str());
+                          base::StringPrintf("%d", today.year - 1).c_str(),
+                          "1");
   request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL,
                          result_delegate()->AsWeakPtr(),
                          ui_delegate()->AsWeakPtr());
@@ -574,7 +579,7 @@
   EXPECT_FALSE(request()->IsGettingFullCard());
 
   CreditCard card;
-  test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050");
+  test::SetCreditCardInfo(&card, nullptr, "4111", "12", "2050", "1");
   request()->GetFullCard(card, AutofillClient::UNMASK_FOR_AUTOFILL,
                          result_delegate()->AsWeakPtr(),
                          ui_delegate()->AsWeakPtr());
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index cb68a582..52e396e 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -210,8 +210,8 @@
     CreditCard credit_card0("287151C8-6AB1-487C-9095-28E80BE5DA15",
                             "https://www.example.com");
     test::SetCreditCardInfo(&credit_card0, "Clyde Barrow",
-                            "347666888555" /* American Express */, "04",
-                            "2999");
+                            "347666888555" /* American Express */, "04", "2999",
+                            "1");
     credit_card0.set_use_count(3);
     credit_card0.set_use_date(AutofillClock::Now() -
                               base::TimeDelta::FromDays(1));
@@ -223,7 +223,7 @@
     credit_card1.set_use_date(AutofillClock::Now() -
                               base::TimeDelta::FromDays(10));
     test::SetCreditCardInfo(&credit_card1, "John Dillinger",
-                            "423456789012" /* Visa */, "01", "2999");
+                            "423456789012" /* Visa */, "01", "2999", "1");
     personal_data_->AddCreditCard(credit_card1);
 
     CreditCard credit_card2("002149C1-EE28-4213-A3B9-DA243FFF021B",
@@ -232,7 +232,7 @@
     credit_card2.set_use_date(AutofillClock::Now() -
                               base::TimeDelta::FromDays(1));
     test::SetCreditCardInfo(&credit_card2, "Bonnie Parker",
-                            "518765432109" /* Mastercard */, "12", "2999");
+                            "518765432109" /* Mastercard */, "12", "2999", "1");
     personal_data_->AddCreditCard(credit_card2);
 
     EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
@@ -281,7 +281,7 @@
 
     CreditCard expected(base::GenerateGUID(), "https://www.example.com");
     test::SetCreditCardInfo(&expected, exp_name, exp_cc_num, exp_cc_month,
-                            exp_cc_year);
+                            exp_cc_year, "");
     const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
     ASSERT_EQ(1U, results.size());
     EXPECT_EQ(0, expected.Compare(*results[0]));
@@ -503,16 +503,17 @@
 TEST_F(PersonalDataManagerTest, AddUpdateRemoveCreditCards) {
   EnableWalletCardImport();
   CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&credit_card0,
-      "John Dillinger", "423456789012" /* Visa */, "01", "2999");
+  test::SetCreditCardInfo(&credit_card0, "John Dillinger",
+                          "423456789012" /* Visa */, "01", "2999", "1");
 
   CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card1, "Bonnie Parker",
-                          "518765432109" /* Mastercard */, "12", "2999");
+                          "518765432109" /* Mastercard */, "12", "2999", "1");
 
   CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card2, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
 
   // Add two test credit cards to the database.
   personal_data_->AddCreditCard(credit_card0);
@@ -558,7 +559,7 @@
   // Add a full server card.
   CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card3, "Jane Doe",
-                          "4111111111111111" /* Visa */, "04", "2999");
+                          "4111111111111111" /* Visa */, "04", "2999", "1");
   credit_card3.set_record_type(CreditCard::FULL_SERVER_CARD);
   credit_card3.set_server_id("server_id");
 
@@ -598,7 +599,7 @@
   // Add a credit card to the database.
   CreditCard credit_card(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card, "John Dillinger",
-                          "423456789012" /* Visa */, "01", "2999");
+                          "423456789012" /* Visa */, "01", "2999", "1");
   personal_data_->AddCreditCard(credit_card);
 
   // Reload the database.
@@ -625,8 +626,8 @@
   EXPECT_FALSE(profile.IsVerified());
 
   CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/");
-  test::SetCreditCardInfo(&credit_card,
-      "John Dillinger", "423456789012" /* Visa */, "01", "2999");
+  test::SetCreditCardInfo(&credit_card, "John Dillinger",
+                          "423456789012" /* Visa */, "01", "2999", "1");
   EXPECT_FALSE(credit_card.IsVerified());
 
   // Add the data to the database.
@@ -708,7 +709,8 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
   test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
   test::SetServerCreditCards(autofill_table_, server_cards);
   personal_data_->Refresh();
 
@@ -736,7 +738,8 @@
 
   CreditCard server_card(CreditCard::FULL_SERVER_CARD, "c789");
   test::SetCreditCardInfo(&server_card, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
 
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
       .WillOnce(QuitMainMessageLoop());
@@ -767,17 +770,18 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "9012" /* Visa */, "01", "2999");
+                          "9012" /* Visa */, "01", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
 
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456"));
   test::SetCreditCardInfo(&server_cards.back(), "Bonnie Parker",
-                          "2109" /* Mastercard */, "12", "2999");
+                          "2109" /* Mastercard */, "12", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kMasterCard);
 
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
   test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
 
   test::SetServerCreditCards(autofill_table_, server_cards);
   personal_data_->Refresh();
@@ -844,12 +848,12 @@
       "US", "19482937549");
 
   CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&credit_card0,
-      "John Dillinger", "423456789012" /* Visa */, "01", "2999");
+  test::SetCreditCardInfo(&credit_card0, "John Dillinger",
+                          "423456789012" /* Visa */, "01", "2999", "1");
 
   CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card1, "Bonnie Parker",
-                          "518765432109" /* Mastercard */, "12", "2999");
+                          "518765432109" /* Mastercard */, "12", "2999", "1");
 
   // Add two test profiles to the database.
   personal_data_->AddProfile(profile0);
@@ -987,7 +991,7 @@
 
 TEST_F(PersonalDataManagerTest, SetEmptyCreditCard) {
   CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&credit_card0, "", "", "", "");
+  test::SetCreditCardInfo(&credit_card0, "", "", "", "", "");
 
   // Add the empty credit card to the database.
   personal_data_->AddCreditCard(credit_card0);
@@ -2225,7 +2229,7 @@
 
   CreditCard expected(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected, "Biggie Smalls", "4111111111111111", "01",
-                          "2999");
+                          "2999", "");  // Imported cards have not billing info.
   const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results.size());
   EXPECT_EQ(0, expected.Compare(*results[0]));
@@ -2293,7 +2297,7 @@
   // See that the invalid option text was converted to the right value.
   CreditCard expected(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected, "Biggie Smalls", "4111111111111111", "02",
-                          "2999");
+                          "2999", "");  // Imported cards have not billing info.
   const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results.size());
   EXPECT_EQ(0, expected.Compare(*results[0]));
@@ -2323,7 +2327,7 @@
 
   CreditCard expected(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected, "Biggie Smalls", "4111111111111111", "01",
-                          "2999");
+                          "2999", "");  // Imported cards have not billing info.
   const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results.size());
   EXPECT_EQ(0, expected.Compare(*results[0]));
@@ -2348,7 +2352,8 @@
   base::RunLoop().Run();
 
   CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&expected2, "", "5500000000000004", "02", "2999");
+  test::SetCreditCardInfo(&expected2, "", "5500000000000004", "02", "2999",
+                          "");  // Imported cards have not billing info.
   std::vector<CreditCard*> cards;
   cards.push_back(&expected);
   cards.push_back(&expected2);
@@ -2439,7 +2444,7 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "1111" /* Visa */, "01", "2999");
+                          "1111" /* Visa */, "01", "2999", "");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
   test::SetServerCreditCards(autofill_table_, server_cards);
 
@@ -2475,7 +2480,8 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
   test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "");  // Imported cards have not billing info.
   test::SetServerCreditCards(autofill_table_, server_cards);
 
   // Type the same data as the unmasked card into a form.
@@ -2518,7 +2524,7 @@
 
   CreditCard expected(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected, "Biggie Smalls", "4111111111111111", "01",
-                          "2998");
+                          "2998", "");  // Imported cards have not billing info.
   const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results.size());
   EXPECT_EQ(0, expected.Compare(*results[0]));
@@ -2546,7 +2552,7 @@
   // updated to "2999".
   CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected2, "Biggie Smalls", "4111111111111111", "01",
-                          "2999");
+                          "2999", "");  // Imported cards have not billing info.
   const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results2.size());
   EXPECT_EQ(0, expected2.Compare(*results2[0]));
@@ -2576,7 +2582,7 @@
 
   CreditCard expected(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected, "Biggie Smalls", "4111111111111111", "01",
-                          "2998");
+                          "2998", "");  // Imported cards have not billing info.
   const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results.size());
   EXPECT_EQ(0, expected.Compare(*results[0]));
@@ -2607,7 +2613,7 @@
   // updated to "2999".
   CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected2, "Biggie Smalls", "4111111111111111", "01",
-                          "2999");
+                          "2999", "");  // Imported cards have not billing info.
   const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results2.size());
   EXPECT_EQ(0, expected2.Compare(*results2[0]));
@@ -2637,7 +2643,7 @@
 
   CreditCard expected(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected, "Biggie Smalls", "4111111111111111", "01",
-                          "2998");
+                          "2998", "");  // Imported cards have not billing info.
   const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results.size());
   EXPECT_EQ(0, expected.Compare(*results[0]));
@@ -2662,7 +2668,7 @@
   // No change is expected.
   CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected2, "Biggie Smalls", "4111111111111111", "01",
-                          "2998");
+                          "2998", "");
   const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results2.size());
   EXPECT_EQ(0, expected2.Compare(*results2[0]));
@@ -2691,8 +2697,8 @@
   base::RunLoop().Run();
 
   CreditCard expected(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&expected,
-      "Biggie Smalls", "4111111111111111", "01", "2999");
+  test::SetCreditCardInfo(&expected, "Biggie Smalls", "4111111111111111", "01",
+                          "2999", "");
   const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results.size());
   EXPECT_EQ(0, expected.Compare(*results[0]));
@@ -2717,8 +2723,8 @@
 
   // No change is expected.
   CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&expected2,
-      "Biggie Smalls", "4111111111111111", "01", "2999");
+  test::SetCreditCardInfo(&expected2, "Biggie Smalls", "4111111111111111", "01",
+                          "2999", "");
   const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results2.size());
   EXPECT_EQ(0, expected2.Compare(*results2[0]));
@@ -2743,8 +2749,8 @@
 
   // No change is expected.
   CreditCard expected3(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&expected3,
-      "Biggie Smalls", "4111111111111111", "01", "2999");
+  test::SetCreditCardInfo(&expected3, "Biggie Smalls", "4111111111111111", "01",
+                          "2999", "");
   const std::vector<CreditCard*>& results3 = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results3.size());
   EXPECT_EQ(0, expected3.Compare(*results3[0]));
@@ -2755,7 +2761,7 @@
   // Note the empty name.
   CreditCard saved_credit_card(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&saved_credit_card, "", "4111111111111111" /* Visa */,
-                          "01", "2998");
+                          "01", "2998", "1");
   personal_data_->AddCreditCard(saved_credit_card);
 
   // Verify that the web database has been updated and the notification sent.
@@ -2791,7 +2797,7 @@
   // added to the existing credit card.
   CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected2, "Biggie Smalls", "4111111111111111", "01",
-                          "2999");
+                          "2999", "1");
   const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results2.size());
   EXPECT_EQ(0, expected2.Compare(*results2[0]));
@@ -2803,8 +2809,8 @@
   // Start with a single valid credit card stored via the preferences.
   // Note the separators in the credit card number.
   CreditCard saved_credit_card(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&saved_credit_card,
-      "Biggie Smalls", "4111 1111 1111 1111" /* Visa */, "01", "2999");
+  test::SetCreditCardInfo(&saved_credit_card, "Biggie Smalls",
+                          "4111 1111 1111 1111" /* Visa */, "01", "2999", "");
   personal_data_->AddCreditCard(saved_credit_card);
 
   // Verify that the web database has been updated and the notification sent.
@@ -2847,7 +2853,7 @@
   // Start with a verified credit card.
   CreditCard credit_card(base::GenerateGUID(), kSettingsOrigin);
   test::SetCreditCardInfo(&credit_card, "Biggie Smalls",
-                          "4111 1111 1111 1111" /* Visa */, "01", "2998");
+                          "4111 1111 1111 1111" /* Visa */, "01", "2998", "");
   EXPECT_TRUE(credit_card.IsVerified());
 
   // Add the credit card to the database.
@@ -2943,7 +2949,7 @@
   // Test that the credit card has also been saved.
   CreditCard expected_card(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected_card, "Biggie Smalls", "4111111111111111",
-                          "01", "2999");
+                          "01", "2999", "");
   const std::vector<CreditCard*>& results_cards =
       personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results_cards.size());
@@ -3017,7 +3023,7 @@
   // Test that the credit card has been saved.
   CreditCard expected_card(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&expected_card, "Biggie Smalls", "4111111111111111",
-                          "01", "2999");
+                          "01", "2999", "");
   const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
   ASSERT_EQ(1U, results.size());
   EXPECT_EQ(0, expected_card.Compare(*results[0]));
@@ -3072,8 +3078,8 @@
 TEST_F(PersonalDataManagerTest, SaveImportedCreditCardWithVerifiedData) {
   // Start with a verified credit card.
   CreditCard credit_card(base::GenerateGUID(), kSettingsOrigin);
-  test::SetCreditCardInfo(&credit_card,
-      "Biggie Smalls", "4111 1111 1111 1111" /* Visa */, "01", "2999");
+  test::SetCreditCardInfo(&credit_card, "Biggie Smalls",
+                          "4111 1111 1111 1111" /* Visa */, "01", "2999", "");
   EXPECT_TRUE(credit_card.IsVerified());
 
   // Add the credit card to the database.
@@ -3187,9 +3193,8 @@
 
   // Test with credit card information also stored.
   CreditCard credit_card(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(&credit_card,
-                          "John Dillinger", "423456789012" /* Visa */,
-                          "01", "2999");
+  test::SetCreditCardInfo(&credit_card, "John Dillinger",
+                          "423456789012" /* Visa */, "01", "2999", "");
   personal_data_->AddCreditCard(credit_card);
 
   // Verify that the web database has been updated and the notification sent.
@@ -3241,8 +3246,8 @@
   personal_data_->AddProfile(steve_jobs);
 
   CreditCard bill_gates(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(
-      &bill_gates, "William H. Gates", "5555555555554444", "1", "2020");
+  test::SetCreditCardInfo(&bill_gates, "William H. Gates", "5555555555554444",
+                          "1", "2020", "1");
   personal_data_->AddCreditCard(bill_gates);
 
   // The personal data manager should be able to read existing profiles in an
@@ -3258,8 +3263,8 @@
   personal_data_->AddProfile(test::GetFullProfile());
 
   CreditCard larry_page(base::GenerateGUID(), "https://www.example.com");
-  test::SetCreditCardInfo(
-      &larry_page, "Lawrence Page", "4111111111111111", "10", "2025");
+  test::SetCreditCardInfo(&larry_page, "Lawrence Page", "4111111111111111",
+                          "10", "2025", "1");
   personal_data_->AddCreditCard(larry_page);
 
   ResetPersonalDataManager(USER_MODE_INCOGNITO);
@@ -3619,7 +3624,7 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b459"));
   test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton", "2110", "12",
-                          "2999");
+                          "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
 
   test::SetServerCreditCards(autofill_table_, server_cards);
@@ -3670,7 +3675,7 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b459"));
   test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton", "2110", "12",
-                          "2999");
+                          "2999", "1");
   server_cards.back().set_use_count(2);
   server_cards.back().set_use_date(AutofillClock::Now() -
                                    base::TimeDelta::FromDays(1));
@@ -3678,7 +3683,7 @@
 
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "b460"));
   test::SetCreditCardInfo(&server_cards.back(), "Jesse James", "2109", "12",
-                          "2999");
+                          "2999", "1");
   server_cards.back().set_use_count(6);
   server_cards.back().set_use_date(AutofillClock::Now() -
                                    base::TimeDelta::FromDays(1));
@@ -3712,14 +3717,15 @@
   CreditCard credit_card0("002149C1-EE28-4213-A3B9-DA243FFF021B",
                           "https://www.example.com");
   test::SetCreditCardInfo(&credit_card0, "Bonnie Parker",
-                          "518765432109" /* Mastercard */, "04", "2999");
+                          "518765432109" /* Mastercard */, "04", "2999", "1");
   personal_data_->AddCreditCard(credit_card0);
 
   // Add an expired card with a higher frecency score.
   CreditCard credit_card1("287151C8-6AB1-487C-9095-28E80BE5DA15",
                           "https://www.example.com");
   test::SetCreditCardInfo(&credit_card1, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "1999");
+                          "347666888555" /* American Express */, "04", "1999",
+                          "1");
   credit_card1.set_use_count(300);
   credit_card1.set_use_date(AutofillClock::Now() -
                             base::TimeDelta::FromDays(10));
@@ -3732,7 +3738,7 @@
   credit_card2.set_use_date(AutofillClock::Now() -
                             base::TimeDelta::FromDays(1));
   test::SetCreditCardInfo(&credit_card2, "John Dillinger",
-                          "423456789012" /* Visa */, "01", "1998");
+                          "423456789012" /* Visa */, "01", "1998", "1");
   personal_data_->AddCreditCard(credit_card2);
 
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
@@ -3764,7 +3770,8 @@
   CreditCard credit_card0("287151C8-6AB1-487C-9095-28E80BE5DA15",
                           "https://www.example.com");
   test::SetCreditCardInfo(&credit_card0, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
   credit_card0.set_use_count(3);
   credit_card0.set_use_date(AutofillClock::Now() -
                             base::TimeDelta::FromDays(1));
@@ -3775,7 +3782,8 @@
   credit_card1.set_use_count(300);
   credit_card1.set_use_date(AutofillClock::Now() -
                             base::TimeDelta::FromDays(10));
-  test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2999");
+  test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2999",
+                          "1");
   personal_data_->AddCreditCard(credit_card1);
 
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
@@ -3810,7 +3818,7 @@
   // suggestions since the locally saved card takes precedence.
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "9012" /* Visa */, "01", "2999");
+                          "9012" /* Visa */, "01", "2999", "1");
   server_cards.back().set_use_count(2);
   server_cards.back().set_use_date(AutofillClock::Now() -
                                    base::TimeDelta::FromDays(15));
@@ -3820,7 +3828,7 @@
   // card type. Not a dupe and therefore both should appear in the suggestions.
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456"));
   test::SetCreditCardInfo(&server_cards.back(), "Bonnie Parker", "2109", "12",
-                          "2999");
+                          "2999", "1");
   server_cards.back().set_use_count(3);
   server_cards.back().set_use_date(AutofillClock::Now() -
                                    base::TimeDelta::FromDays(15));
@@ -3831,7 +3839,8 @@
   // precedence over local cards.
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
   test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
   server_cards.back().set_use_count(1);
   server_cards.back().set_use_date(AutofillClock::Now() -
                                    base::TimeDelta::FromDays(15));
@@ -3881,7 +3890,8 @@
   // the local card should appear in the suggestions.
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
   test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
 
   test::SetServerCreditCards(autofill_table_, server_cards);
   personal_data_->Refresh();
@@ -3899,7 +3909,7 @@
   // of more than one local card.
   CreditCard credit_card3("4141084B-72D7-4B73-90CF-3D6AC154673B",
                           "https://www.example.com");
-  test::SetCreditCardInfo(&credit_card3, "Clyde Barrow", "", "04", "");
+  test::SetCreditCardInfo(&credit_card3, "Clyde Barrow", "", "04", "", "");
   personal_data_->AddCreditCard(credit_card3);
 
   EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
@@ -3922,7 +3932,7 @@
   CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15",
                         "https://www.example.com");
   test::SetCreditCardInfo(&local_card, "Homer Simpson",
-                          "423456789012" /* Visa */, "01", "2999");
+                          "423456789012" /* Visa */, "01", "2999", "1");
   local_card.set_use_count(3);
   local_card.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(1));
   credit_cards.push_back(&local_card);
@@ -3930,7 +3940,7 @@
   // Create a full server card that is a duplicate of one of the local cards.
   CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "c789");
   test::SetCreditCardInfo(&full_server_card, "Homer Simpson",
-                          "423456789012" /* Visa */, "01", "2999");
+                          "423456789012" /* Visa */, "01", "2999", "1");
   full_server_card.set_use_count(1);
   full_server_card.set_use_date(AutofillClock::Now() -
                                 base::TimeDelta::FromDays(15));
@@ -3953,13 +3963,13 @@
   local_card.set_use_count(300);
   local_card.set_use_date(AutofillClock::Now() - base::TimeDelta::FromDays(10));
   test::SetCreditCardInfo(&local_card, "Homer Simpson",
-                          "423456789012" /* Visa */, "01", "2999");
+                          "423456789012" /* Visa */, "01", "2999", "1");
   credit_cards.push_back(&local_card);
 
   // Create a masked server card that is a duplicate of a local card.
   CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
   test::SetCreditCardInfo(&masked_card, "Homer Simpson", "9012" /* Visa */,
-                          "01", "2999");
+                          "01", "2999", "1");
   masked_card.set_use_count(2);
   masked_card.set_use_date(AutofillClock::Now() -
                            base::TimeDelta::FromDays(15));
@@ -3980,7 +3990,7 @@
   // Create a full server card that is a duplicate of one of the local cards.
   CreditCard full_server_card(CreditCard::FULL_SERVER_CARD, "c789");
   test::SetCreditCardInfo(&full_server_card, "Homer Simpson",
-                          "423456789012" /* Visa */, "01", "2999");
+                          "423456789012" /* Visa */, "01", "2999", "1");
   full_server_card.set_use_count(1);
   full_server_card.set_use_date(AutofillClock::Now() -
                                 base::TimeDelta::FromDays(15));
@@ -3989,7 +3999,7 @@
   // Create a masked server card that is a duplicate of a local card.
   CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
   test::SetCreditCardInfo(&masked_card, "Homer Simpson", "9012" /* Visa */,
-                          "01", "2999");
+                          "01", "2999", "1");
   masked_card.set_use_count(2);
   masked_card.set_use_date(AutofillClock::Now() -
                            base::TimeDelta::FromDays(15));
@@ -4011,12 +4021,13 @@
   credit_card2.set_use_date(AutofillClock::Now() -
                             base::TimeDelta::FromDays(1));
   test::SetCreditCardInfo(&credit_card2, "Homer Simpson",
-                          "518765432109" /* Mastercard */, "", "");
+                          "518765432109" /* Mastercard */, "", "", "");
   credit_cards.push_back(&credit_card2);
 
   // Create a masked server card that is slightly different of the local card.
   CreditCard credit_card4(CreditCard::MASKED_SERVER_CARD, "b456");
-  test::SetCreditCardInfo(&credit_card4, "Homer Simpson", "2109", "12", "2999");
+  test::SetCreditCardInfo(&credit_card4, "Homer Simpson", "2109", "12", "2999",
+                          "1");
   credit_card4.set_use_count(3);
   credit_card4.set_use_date(AutofillClock::Now() -
                             base::TimeDelta::FromDays(15));
@@ -4027,7 +4038,8 @@
   // cards.
   CreditCard credit_card5(CreditCard::FULL_SERVER_CARD, "c789");
   test::SetCreditCardInfo(&credit_card5, "Homer Simpson",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
   credit_card5.set_use_count(1);
   credit_card5.set_use_date(AutofillClock::Now() -
                             base::TimeDelta::FromDays(15));
@@ -4050,7 +4062,7 @@
 
   CreditCard credit_card(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card, "John Dillinger",
-                          "423456789012" /* Visa */, "01", "2999");
+                          "423456789012" /* Visa */, "01", "2999", "1");
   EXPECT_EQ(1U, credit_card.use_count());
   EXPECT_EQ(kArbitraryTime, credit_card.use_date());
   EXPECT_EQ(kArbitraryTime, credit_card.modification_date());
@@ -4106,17 +4118,18 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "9012" /* Visa */, "01", "2999");
+                          "9012" /* Visa */, "01", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
 
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b456"));
   test::SetCreditCardInfo(&server_cards.back(), "Bonnie Parker",
-                          "4444" /* Mastercard */, "12", "2999");
+                          "4444" /* Mastercard */, "12", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kMasterCard);
 
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
   test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
 
   // Create the test clock and set the time to a specific value.
   TestAutofillClock test_clock;
@@ -4228,7 +4241,7 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "9012" /* Visa */, "01", "2999");
+                          "9012" /* Visa */, "01", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
   test::SetServerCreditCards(autofill_table_, server_cards);
   personal_data_->Refresh();
@@ -4253,12 +4266,13 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "1881" /* Visa */, "01", "2999");
+                          "1881" /* Visa */, "01", "2999", "");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
 
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
   test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "");
 
   test::SetServerCreditCards(autofill_table_, server_cards);
   personal_data_->Refresh();
@@ -4300,7 +4314,7 @@
 
   CreditCard local_card(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&local_card, "John Dillinger", "4012888888881881",
-                          "01", "2999");
+                          "01", "2999", "");
   const std::vector<CreditCard*>& results =
       personal_data_->GetLocalCreditCards();
   ASSERT_EQ(1U, results.size());
@@ -4314,12 +4328,13 @@
   std::vector<CreditCard> server_cards;
   server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "a123"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "1881" /* Visa */, "01", "2999");
+                          "1881" /* Visa */, "01", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
 
   server_cards.push_back(CreditCard(CreditCard::FULL_SERVER_CARD, "c789"));
   test::SetCreditCardInfo(&server_cards.back(), "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
 
   test::SetServerCreditCards(autofill_table_, server_cards);
   personal_data_->Refresh();
@@ -5140,17 +5155,18 @@
   // verifying results.
   CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card1, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
   credit_card1.set_use_count(10);
 
   CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card2, "John Dillinger",
-                          "423456789012" /* Visa */, "01", "2999");
+                          "423456789012" /* Visa */, "01", "2999", "1");
   credit_card2.set_use_count(5);
 
   CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com");
   test::SetCreditCardInfo(&credit_card3, "Bonnie Parker",
-                          "518765432109" /* Mastercard */, "12", "2999");
+                          "518765432109" /* Mastercard */, "12", "2999", "1");
   credit_card3.set_use_count(1);
 
   // Associate the first card with profile1.
@@ -5931,7 +5947,8 @@
   CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15",
                         "https://www.example.com");
   test::SetCreditCardInfo(&local_card, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
   local_card.set_billing_address_id(kServerAddressId);
   personal_data_->AddCreditCard(local_card);
 
@@ -5939,7 +5956,7 @@
   server_cards.push_back(
       CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card1"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "1111" /* Visa */, "01", "2999");
+                          "1111" /* Visa */, "01", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
   server_cards.back().set_billing_address_id(kServerAddressId);
   test::SetServerCreditCards(autofill_table_, server_cards);
@@ -6039,7 +6056,8 @@
   CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15",
                         "https://www.example.com");
   test::SetCreditCardInfo(&local_card, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
   local_card.set_billing_address_id(kServerAddressId);
   personal_data_->AddCreditCard(local_card);
 
@@ -6047,7 +6065,7 @@
   server_cards.push_back(
       CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card1"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "1111" /* Visa */, "01", "2999");
+                          "1111" /* Visa */, "01", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
   server_cards.back().set_billing_address_id(kServerAddressId);
   test::SetServerCreditCards(autofill_table_, server_cards);
@@ -6214,7 +6232,8 @@
   CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15",
                         "https://www.example.com");
   test::SetCreditCardInfo(&local_card, "Clyde Barrow",
-                          "347666888555" /* American Express */, "04", "2999");
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
   local_card.set_billing_address_id(kServerAddressId);
   personal_data_->AddCreditCard(local_card);
 
@@ -6222,7 +6241,7 @@
   server_cards.push_back(
       CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card1"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "1111" /* Visa */, "01", "2999");
+                          "1111" /* Visa */, "01", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
   server_cards.back().set_billing_address_id(kServerAddressId2);
   test::SetServerCreditCards(autofill_table_, server_cards);
@@ -6319,7 +6338,7 @@
   server_cards.push_back(
       CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card1"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "1111" /* Visa */, "01", "2999");
+                          "1111" /* Visa */, "01", "2999", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
   server_cards.back().set_billing_address_id(kServerAddressId);
   test::SetServerCreditCards(autofill_table_, server_cards);
@@ -6357,7 +6376,7 @@
   server_cards.push_back(
       CreditCard(CreditCard::MASKED_SERVER_CARD, "server_card2"));
   test::SetCreditCardInfo(&server_cards.back(), "John Dillinger",
-                          "1112" /* Visa */, "01", "2888");
+                          "1112" /* Visa */, "01", "2888", "1");
   server_cards.back().SetNetworkForMaskedCard(kVisaCard);
   server_cards.back().set_billing_address_id(kServerAddressId);
   test::SetServerCreditCards(autofill_table_, server_cards);
diff --git a/components/autofill/core/browser/validation.cc b/components/autofill/core/browser/validation.cc
index cbab0989..a177b28 100644
--- a/components/autofill/core/browser/validation.cc
+++ b/components/autofill/core/browser/validation.cc
@@ -15,6 +15,7 @@
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/autofill_regex_constants.h"
 #include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/state_names.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_regexes.h"
@@ -127,7 +128,8 @@
 
 CreditCardCompletionStatus GetCompletionStatusForCard(
     const CreditCard& card,
-    const std::string& app_locale) {
+    const std::string& app_locale,
+    const std::vector<AutofillProfile*> billing_addresses) {
   CreditCardCompletionStatus status = CREDIT_CARD_COMPLETE;
   if (card.IsExpired(autofill::AutofillClock::Now()))
     status |= CREDIT_CARD_EXPIRED;
@@ -141,6 +143,12 @@
     status |= CREDIT_CARD_NO_CARDHOLDER;
   }
 
+  if (card.billing_address_id().empty() ||
+      !autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
+          card.billing_address_id(), billing_addresses)) {
+    status |= CREDIT_CARD_NO_BILLING_ADDRESS;
+  }
+
   return status;
 }
 
@@ -156,6 +164,9 @@
     case CREDIT_CARD_NO_NUMBER:
       return l10n_util::GetStringUTF16(
           IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE);
+    case CREDIT_CARD_NO_BILLING_ADDRESS:
+      return l10n_util::GetStringUTF16(
+          IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED);
     default:
       // Multiple things are missing
       return l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED);
diff --git a/components/autofill/core/browser/validation.h b/components/autofill/core/browser/validation.h
index cfc0f90..531cf27 100644
--- a/components/autofill/core/browser/validation.h
+++ b/components/autofill/core/browser/validation.h
@@ -7,6 +7,7 @@
 
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/strings/string16.h"
 #include "base/strings/string_piece_forward.h"
@@ -19,6 +20,7 @@
 namespace autofill {
 
 class CreditCard;
+class AutofillProfile;
 
 // Constants for the length of a CVC.
 static const size_t GENERAL_CVC_LENGTH = 3;
@@ -30,6 +32,7 @@
 static const CreditCardCompletionStatus CREDIT_CARD_EXPIRED = 1 << 0;
 static const CreditCardCompletionStatus CREDIT_CARD_NO_CARDHOLDER = 1 << 1;
 static const CreditCardCompletionStatus CREDIT_CARD_NO_NUMBER = 1 << 2;
+static const CreditCardCompletionStatus CREDIT_CARD_NO_BILLING_ADDRESS = 1 << 3;
 
 // Returns true if |year| and |month| describe a date later than |now|.
 // |year| must have 4 digits.
@@ -56,10 +59,10 @@
 
 // Returns the credit card's completion status. If equal to
 // CREDIT_CARD_COMPLETE, then the card is ready to be used for Payment Request.
-// TODO(crbug.com/709776): Check for billing address association.
 CreditCardCompletionStatus GetCompletionStatusForCard(
     const CreditCard& credit_card,
-    const std::string& app_locale);
+    const std::string& app_locale,
+    const std::vector<AutofillProfile*> billing_addresses);
 
 // Return the message to be displayed to the user, indicating what's missing
 // to make the credit card complete for payment. If more than one thing is
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 2ab82162..ec15bd7 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -346,10 +346,16 @@
   <message name="IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC" desc="The placeholder/label text for credit card verification code in the requestAutocomplete dialog.">
     CVC
   </message>
-  <message name="IDS_AUTOFILL_LOADING_REGIONS" desc="The string to display in the regions combo box while loading the region data.">
+  <message name="IDS_AUTOFILL_LOADING_REGIONS" desc="The string to display in the regions combobox while loading the region data.">
     Loading...
   </message>
   <message name="IDS_AUTOFILL_FAILED_LOADING_REGIONS" desc="The string to display in the regions combo box when loading the region data failed.">
     Failed loading regions data
   </message>
+  <message name="IDS_AUTOFILL_SELECT" desc="The string to display in comboboxes when nothing got selected yet.">
+    Select
+  </message>
+  <message name="IDS_AUTOFILL_ADD_BILLING_ADDRESS" desc="The string to display in comboboxes when no billing addresses are available.">
+    Add a billing address
+  </message>
 </grit-part>
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
index 10f161ec..c20c7ea 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol.cc
@@ -13,9 +13,9 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
 #include "net/proxy/proxy_config.h"
 #include "net/proxy/proxy_info.h"
 #include "net/proxy/proxy_list.h"
@@ -176,7 +176,7 @@
 
   // Retry if block-once was specified or if method is idempotent.
   return bypass_type == BYPASS_EVENT_TYPE_CURRENT ||
-         util::IsMethodIdempotent(request->method());
+         net::HttpUtil::IsMethodIdempotent(request->method());
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
index f083a5b..ef62c61 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_protocol_unittest.cc
@@ -25,7 +25,6 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_util.h"
 #include "net/base/completion_callback.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
@@ -34,6 +33,7 @@
 #include "net/base/proxy_delegate.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_transaction_test_util.h"
+#include "net/http/http_util.h"
 #include "net/proxy/proxy_server.h"
 #include "net/proxy/proxy_service.h"
 #include "net/socket/socket_test_util.h"
@@ -346,7 +346,7 @@
         TRAFFIC_ANNOTATION_FOR_TESTS));
     request->set_method(tests[i].method);
     EXPECT_EQ(tests[i].expected_result,
-              util::IsMethodIdempotent(request->method()));
+              net::HttpUtil::IsMethodIdempotent(request->method()));
   }
 }
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc
index 9af46dd..ed6759db 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_util.cc
@@ -13,6 +13,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/url_util.h"
 #include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
 #include "net/proxy/proxy_config.h"
 #include "net/proxy/proxy_info.h"
 #include "net/url_request/url_request.h"
@@ -131,11 +132,6 @@
   }
 }
 
-bool IsMethodIdempotent(const std::string& method) {
-  return method == "GET" || method == "OPTIONS" || method == "HEAD" ||
-         method == "PUT" || method == "DELETE" || method == "TRACE";
-}
-
 GURL AddApiKeyToUrl(const GURL& url) {
   GURL new_url = url;
 #if defined(USE_GOOGLE_API_KEYS)
@@ -151,7 +147,7 @@
                                    const GURL& url,
                                    const std::string& method) {
   return proxy_info.is_direct() && proxy_info.proxy_list().size() == 1 &&
-         !url.SchemeIsWSOrWSS() && IsMethodIdempotent(method);
+         !url.SchemeIsWSOrWSS() && net::HttpUtil::IsMethodIdempotent(method);
 }
 
 bool ApplyProxyConfigToProxyInfo(const net::ProxyConfig& proxy_config,
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h
index be0a16d..811faa9 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_util.h
@@ -66,9 +66,6 @@
 // Get the human-readable version of |client|.
 const char* GetStringForClient(Client client);
 
-// Returns true if the request method is idempotent.
-bool IsMethodIdempotent(const std::string& method);
-
 GURL AddApiKeyToUrl(const GURL& url);
 
 // Returns whether this is valid for data reduction proxy use. |proxy_info|
diff --git a/components/os_crypt/os_crypt.h b/components/os_crypt/os_crypt.h
index 10945309..b03d726 100644
--- a/components/os_crypt/os_crypt.h
+++ b/components/os_crypt/os_crypt.h
@@ -83,6 +83,10 @@
 // If all parameters are |nullptr|, the real implementation is restored.
 void UseMockKeyStorageForTesting(KeyStorageLinux* (*get_key_storage_mock)(),
                                  std::string* (*get_password_v11_mock)());
+
+// Clears any caching and most lazy initialisations performed by the production
+// code. Should be used after any test which required a password.
+void ClearCacheForTesting();
 #endif  // defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(UNIT_TEST)
 
 #endif  // COMPONENTS_OS_CRYPT_OS_CRYPT_H_
diff --git a/components/os_crypt/os_crypt_linux.cc b/components/os_crypt/os_crypt_linux.cc
index ac8d5c4..514f18f 100644
--- a/components/os_crypt/os_crypt_linux.cc
+++ b/components/os_crypt/os_crypt_linux.cc
@@ -253,6 +253,14 @@
   return g_get_password[Version::V11]();
 }
 
+void ClearCacheForTesting() {
+  g_cache.Get().key_storage_cache.reset();
+  g_cache.Get().password_v10_cache.reset();
+  g_cache.Get().password_v11_cache.reset();
+  g_cache.Get().is_key_storage_cached = false;
+  g_cache.Get().is_password_v11_cached = false;
+}
+
 void UseMockKeyStorageForTesting(KeyStorageLinux* (*get_key_storage_mock)(),
                                  std::string* (*get_password_v11_mock)()) {
   // Save the real implementation to restore it later.
diff --git a/components/os_crypt/os_crypt_mocker_linux.cc b/components/os_crypt/os_crypt_mocker_linux.cc
index a222c88..d4dc0ac 100644
--- a/components/os_crypt/os_crypt_mocker_linux.cc
+++ b/components/os_crypt/os_crypt_mocker_linux.cc
@@ -55,4 +55,5 @@
 // static
 void OSCryptMockerLinux::TearDown() {
   UseMockKeyStorageForTesting(nullptr, nullptr);
+  ClearCacheForTesting();
 }
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 8a6b730..dac01e50 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -246,11 +246,9 @@
                    [](const std::unique_ptr<PaymentInstrument>& instrument) {
                      return instrument->IsCompleteForPayment();
                    });
-
   selected_instrument_ = first_complete_instrument == instruments.end()
                              ? nullptr
                              : first_complete_instrument->get();
-
   UpdateIsReadyToPayAndNotifyObservers();
 }
 
diff --git a/components/payments/core/autofill_payment_instrument.cc b/components/payments/core/autofill_payment_instrument.cc
index d32149a1..08502b4e8 100644
--- a/components/payments/core/autofill_payment_instrument.cc
+++ b/components/payments/core/autofill_payment_instrument.cc
@@ -55,8 +55,6 @@
   delegate_ = delegate;
 
   // Get the billing address.
-  // TODO(crbug.com/709776): Make sure the billing address is valid before
-  // getting here.
   if (!credit_card_.billing_address_id().empty()) {
     autofill::AutofillProfile* billing_address =
         autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
@@ -84,18 +82,21 @@
 }
 
 bool AutofillPaymentInstrument::IsCompleteForPayment() {
-  return autofill::GetCompletionStatusForCard(credit_card_, app_locale_) ==
+  return autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
+                                              billing_profiles_) ==
          autofill::CREDIT_CARD_COMPLETE;
 }
 
 base::string16 AutofillPaymentInstrument::GetMissingInfoLabel() {
   return autofill::GetCompletionMessageForCard(
-      autofill::GetCompletionStatusForCard(credit_card_, app_locale_));
+      autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
+                                           billing_profiles_));
 }
 
 bool AutofillPaymentInstrument::IsValidForCanMakePayment() {
   autofill::CreditCardCompletionStatus status =
-      autofill::GetCompletionStatusForCard(credit_card_, app_locale_);
+      autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
+                                           billing_profiles_);
   // Card has to have a cardholder name and number for the purposes of
   // CanMakePayment. An expired card is still valid at this stage.
   return !(status & autofill::CREDIT_CARD_NO_CARDHOLDER ||
diff --git a/components/payments/core/autofill_payment_instrument_unittest.cc b/components/payments/core/autofill_payment_instrument_unittest.cc
index 892c1969..b6afe02 100644
--- a/components/payments/core/autofill_payment_instrument_unittest.cc
+++ b/components/payments/core/autofill_payment_instrument_unittest.cc
@@ -208,6 +208,35 @@
             instrument.GetMissingInfoLabel());
 }
 
+// A local card with no billing address id is not a valid instrument for
+// payment.
+TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoBillinbAddressId) {
+  autofill::CreditCard& card = local_credit_card();
+  card.set_billing_address_id("");
+  base::string16 missing_info;
+  AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
+                                       "en-US", nullptr);
+  EXPECT_FALSE(instrument.IsCompleteForPayment());
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
+      instrument.GetMissingInfoLabel());
+}
+
+// A local card with an invalid billing address id is not a valid instrument for
+// payment.
+TEST_F(AutofillPaymentInstrumentTest,
+       IsCompleteForPayment_InvalidBillinbAddressId) {
+  autofill::CreditCard& card = local_credit_card();
+  card.set_billing_address_id("InvalidBillingAddressId");
+  base::string16 missing_info;
+  AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
+                                       "en-US", nullptr);
+  EXPECT_FALSE(instrument.IsCompleteForPayment());
+  EXPECT_EQ(
+      l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
+      instrument.GetMissingInfoLabel());
+}
+
 // A local card with no name and no number is not a valid instrument for
 // payment.
 TEST_F(AutofillPaymentInstrumentTest,
@@ -226,6 +255,8 @@
 // A Masked (server) card is a valid instrument for payment.
 TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_MaskedCard) {
   autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+  ASSERT_GT(billing_profiles().size(), 0UL);
+  card.set_billing_address_id(billing_profiles()[0]->guid());
   AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
                                        "en-US", nullptr);
   EXPECT_TRUE(instrument.IsCompleteForPayment());
@@ -235,6 +266,8 @@
 // An expired masked (server) card is not a valid instrument for payment.
 TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_ExpiredMaskedCard) {
   autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+  ASSERT_GT(billing_profiles().size(), 0UL);
+  card.set_billing_address_id(billing_profiles()[0]->guid());
   card.SetExpirationYear(2016);  // Expired.
   AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
                                        "en-US", nullptr);
diff --git a/components/payments_strings.grdp b/components/payments_strings.grdp
index 5a7d166b..7c6d20cc 100644
--- a/components/payments_strings.grdp
+++ b/components/payments_strings.grdp
@@ -175,6 +175,9 @@
   <message name="IDS_PAYMENTS_NAME_ON_CARD_REQUIRED" desc="The label to indicate the cardholder name (the name of the owner or “holder” of the credit card)  must be entered." formatter_data="android_java">
     Cardholder name required
   </message>
+  <message name="IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED" desc="The label to indicate the billing address of the credit card  must be entered." formatter_data="android_java">
+    Card billing address required
+  </message>
   <message name="IDS_PAYMENTS_MORE_INFORMATION_REQUIRED" desc="The label to indicate more information is required for payment card or shipping address or contact info." formatter_data="android_java">
     More information required
   </message>
diff --git a/components/session_manager/core/session_manager.cc b/components/session_manager/core/session_manager.cc
index d238745..def0fa65 100644
--- a/components/session_manager/core/session_manager.cc
+++ b/components/session_manager/core/session_manager.cc
@@ -62,6 +62,14 @@
   session_started_ = true;
 }
 
+bool SessionManager::HasSessionForAccountId(
+    const AccountId& user_account_id) const {
+  return std::find_if(sessions_.begin(), sessions_.end(),
+                      [user_account_id](const Session& session) {
+                        return session.user_account_id == user_account_id;
+                      }) != sessions_.end();
+}
+
 bool SessionManager::IsInSecondaryLoginScreen() const {
   return session_state_ == SessionState::LOGIN_SECONDARY;
 }
@@ -99,11 +107,7 @@
 void SessionManager::CreateSessionInternal(const AccountId& user_account_id,
                                            const std::string& user_id_hash,
                                            bool browser_restart) {
-  DCHECK(std::find_if(sessions_.begin(), sessions_.end(),
-                      [user_account_id](const Session& session) {
-                        return session.user_account_id == user_account_id;
-                      }) == sessions_.end());
-
+  DCHECK(!HasSessionForAccountId(user_account_id));
   sessions_.push_back({next_id_++, user_account_id});
   NotifyUserLoggedIn(user_account_id, user_id_hash, browser_restart);
 }
diff --git a/components/session_manager/core/session_manager.h b/components/session_manager/core/session_manager.h
index 27dc34b719..666cc73 100644
--- a/components/session_manager/core/session_manager.h
+++ b/components/session_manager/core/session_manager.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SESSION_MANAGER_CORE_SESSION_MANAGER_H_
 #define COMPONENTS_SESSION_MANAGER_CORE_SESSION_MANAGER_H_
 
+#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -49,6 +50,9 @@
   // before the session has been started.
   virtual void SessionStarted();
 
+  // Returns true if the session for the given user was started.
+  bool HasSessionForAccountId(const AccountId& user_account_id) const;
+
   // Convenience wrapps of session state.
   bool IsInSecondaryLoginScreen() const;
   bool IsScreenLocked() const;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 0cfb950b..503c0ad 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -84,7 +84,6 @@
     "//device/power_save_blocker",
     "//device/screen_orientation/public/interfaces",
     "//device/vr",
-    "//device/vr:mojo_bindings",
     "//device/vr/features",
     "//device/wake_lock/public/interfaces",
     "//google_apis",
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index df9b63c..9acdb735 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -643,6 +643,9 @@
     "//url/mojo:url_mojom_origin",
   ]
 
+  overridden_deps = [ "//ipc:mojom" ]
+  component_deps = [ "//ipc" ]
+
   export_class_attribute = "CONTENT_EXPORT"
   export_define = "CONTENT_IMPLEMENTATION=1"
   export_header = "content/common/content_export.h"
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn
index f5182ca..a1ed8c4 100644
--- a/device/vr/BUILD.gn
+++ b/device/vr/BUILD.gn
@@ -116,10 +116,7 @@
   }
 }
 
-mojom_component("mojo_bindings") {
-  output_prefix = "device_vr_mojo_bindings"
-  macro_prefix = "DEVICE_VR_MOJO_BINDINGS"
-
+mojom("mojo_bindings") {
   sources = [
     "vr_service.mojom",
   ]
@@ -128,4 +125,8 @@
     "//gpu/ipc/common:interfaces",
     "//mojo/common:common_custom_types",
   ]
+
+  export_class_attribute = "DEVICE_VR_EXPORT"
+  export_define = "DEVICE_VR_IMPLEMENTATION=1"
+  export_header = "device/vr/vr_export.h"
 }
diff --git a/device/wake_lock/wake_lock_service_impl.cc b/device/wake_lock/wake_lock_service_impl.cc
index 6e157180..ba7c2ad 100644
--- a/device/wake_lock/wake_lock_service_impl.cc
+++ b/device/wake_lock/wake_lock_service_impl.cc
@@ -62,11 +62,10 @@
   if (!(*binding_set_.dispatch_context()))
     return;
 
+  DCHECK(num_lock_requests_ > 0);
   *binding_set_.dispatch_context() = false;
-  if (num_lock_requests_ > 0) {
-    num_lock_requests_--;
-    UpdateWakeLock();
-  }
+  num_lock_requests_--;
+  UpdateWakeLock();
 }
 
 void WakeLockServiceImpl::HasWakeLockForTests(
@@ -112,16 +111,13 @@
 }
 
 void WakeLockServiceImpl::OnConnectionError() {
-  DCHECK(binding_set_.dispatch_context());
-
-  // If the error-happening client's wakelock is in outstanding status,
-  // decrease the num_lock_requests and call UpdateWakeLock().
+  // If this client has an outstanding wake lock request, decrease the
+  // num_lock_requests and call UpdateWakeLock().
   if (*binding_set_.dispatch_context() && num_lock_requests_ > 0) {
     num_lock_requests_--;
     UpdateWakeLock();
   }
 
-  // If |binding_set_| is empty, WakeLockServiceImpl should delele itself.
   if (binding_set_.empty())
     base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
 }
diff --git a/extensions/common/api/_behavior_features.json b/extensions/common/api/_behavior_features.json
index b33eec5..2e65a5b 100644
--- a/extensions/common/api/_behavior_features.json
+++ b/extensions/common/api/_behavior_features.json
@@ -38,10 +38,11 @@
   "allow_usb_devices_permission_interface_class": {
     "channel": "stable",
     "extension_types": ["platform_app"],
-    "session_types": ["kiosk"] 
+    "session_types": ["kiosk"]
   },
   "signin_screen": [ {
     "blacklist": [
+      // https://crbug.com/626342
       "9E527CDA9D7C50844E8A5DB964A54A640AE48F98"   // Chrome remote desktop
     ],
     // Allow developers to test new features.
@@ -59,6 +60,8 @@
     "location": "policy",
     "platforms": ["chromeos"],
     "whitelist": [
+      // https://crbug.com/626342
+      "85DA1AC24AF23CDA9F5A19858EB9C6E9E1BA57F6",  // Sign-in Screen Test App
       "EC3DE21E048B67319893889529354DFBFA96FD23",  // Smart Card Connector
       "6B748A5C005F21B7CBCF4170C2F883E435DEB511"   // CSSI Smart Card Middleware
     ]
@@ -69,6 +72,7 @@
     "component_extensions_auto_granted": false,
     "platforms": ["chromeos"],
     "whitelist": [
+      // https://crbug.com/626342
       "E24F1786D842E91E74C27929B0B3715A4689A473",  // Gnubby component extension
       "6F9E349A0561C78A0D3F41496FE521C5151C7F71",  // Gnubby app
       "8EBDF73405D0B84CEABB8C7513C9B9FA9F1DC2CE",  // Genius app (help)
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc
index 47037183..c12786ad 100644
--- a/gpu/ipc/service/direct_composition_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -148,11 +148,17 @@
                                             d3d11_device.Get(), &flags)))
       continue;
 
-    // Direct-only support might be ok in some circumstances, but since the
-    // overlay processor isn't set up to try to distinguish, only try to use
-    // overlays when scaling's enabled.
-    if (flags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING)
+    UMA_HISTOGRAM_SPARSE_SLOWLY("GPU.DirectComposition.OverlaySupportFlags",
+                                flags);
+
+    // Some new Intel drivers only claim to support unscaled overlays, but
+    // scaled overlays still work. Even when scaled overlays aren't actually
+    // supported, presentation using the overlay path should be relatively
+    // efficient.
+    if (flags & (DXGI_OVERLAY_SUPPORT_FLAG_SCALING |
+                 DXGI_OVERLAY_SUPPORT_FLAG_DIRECT)) {
       return true;
+    }
   }
   return false;
 }
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 8298ad6..3291fee 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -444,7 +444,6 @@
   ]
 
   data = [
-    "$root_out_dir/headless_browser_tests.pak",
     "$root_out_dir/headless_lib.pak",
     "//net/tools/testserver/",
     "//third_party/pyftpdlib/",
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 80e0b2a24..25d848f 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1513,6 +1513,9 @@
       <message name="IDS_IOS_SIGNIN_PROMO_NOT" desc="Button that the user can press if they are not the profile that Chrome found (opposite of 'Continue as Joe Doe').">
         Not <ph name="EMAIL">$1<ex>john.doe@example.com</ex>?</ph>
       </message>
+      <message name="IDS_IOS_SYSTEM_MAIL_APP" desc="Name of iOS system-provided Mail app. Note that this is not consistently translated for all locales, e.g. it is still 'Mail' (in English) for French locale, but is translated to '邮件' for Chinese locale. [Length: 10em]">
+        Mail
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.h b/ios/chrome/browser/browser_state/test_chrome_browser_state.h
index b7bd443..ea68d0c4 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.h
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.h
@@ -70,7 +70,6 @@
   // you're recreating the BookmarkModel.
   //
   // NOTE: this does not block until the bookmarks are loaded.
-  // TODO(shreyasv): If needed, write a version that blocks.
   void CreateBookmarkModel(bool delete_file);
 
   // Creates the history service. If |delete_file| is true, the history file is
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 42381ed..2d6de7b 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -10,6 +10,8 @@
     "content_suggestion_extra_builder.mm",
     "content_suggestions_collection_updater.h",
     "content_suggestions_collection_updater.mm",
+    "content_suggestions_collection_utils.h",
+    "content_suggestions_collection_utils.mm",
     "content_suggestions_commands.h",
     "content_suggestions_data_sink.h",
     "content_suggestions_data_source.h",
@@ -21,6 +23,7 @@
     ":resources",
     "//base",
     "//components/strings",
+    "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/collection_view",
     "//ios/chrome/browser/ui/content_suggestions/cells",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
@@ -39,12 +42,15 @@
   testonly = true
   sources = [
     "content_suggestions_collection_updater_unittest.mm",
+    "content_suggestions_collection_utils_unittest.mm",
   ]
   deps = [
     ":content_suggestions",
     "//base",
+    "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/collection_view",
     "//ios/chrome/browser/ui/content_suggestions/identifier",
+    "//ios/chrome/test/base",
     "//testing/gtest",
     "//third_party/ocmock",
   ]
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
new file mode 100644
index 0000000..d47d24d8
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.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 IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COLLECTION_UTILS_H_
+#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COLLECTION_UTILS_H_
+
+#import <UIKit/UIKit.h>
+
+namespace content_suggestions {
+
+// Returns the maximum number of tiles fitting in |availableWidth|, limited to
+// 4.
+NSUInteger numberOfTilesForWidth(CGFloat availableWidth);
+// Returns the spacing between tiles, based on the device.
+CGFloat spacingBetweenTiles();
+
+// iPhone landscape uses a slightly different layout for the doodle and search
+// field frame. Returns the proper frame from |frames| based on orientation,
+// centered in the view of |width|.
+CGRect getOrientationFrame(const CGRect frames[], CGFloat width);
+// Returns x-offset in order to have the tiles centered in a view with a
+// |width|.
+CGFloat centeredTilesMarginForWidth(CGFloat width);
+// Returns the proper frame for the doodle inside a view with a |width|.
+// |logoIsShowing| refers to the Google logo or the doodle.
+CGRect doodleFrame(CGFloat width, BOOL logoIsShowing);
+// Returns the proper frame for the search field inside a view with a |width|.
+// |logoIsShowing| refers to the Google logo or the doodle.
+CGRect searchFieldFrame(CGFloat width, BOOL logoIsShowing);
+// Returns the expected height of the NewTabPageHeaderView inside a view with a
+// |width|. |logoIsShowing| refers to the Google logo or the doodle.
+// |promoCanShow| represents whether a what's new promo can be displayed.
+CGFloat heightForLogoHeader(CGFloat width,
+                            BOOL logoIsShowing,
+                            BOOL promoCanShow);
+
+}  // namespace content_suggestions
+
+#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COLLECTION_UTILS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
new file mode 100644
index 0000000..fc76ca3
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
@@ -0,0 +1,137 @@
+// 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/ui/content_suggestions/content_suggestions_collection_utils.h"
+
+#include "base/logging.h"
+#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+const CGFloat kSpacingIPhone = 16;
+const CGFloat kSpacingIPad = 24;
+
+const CGFloat kMaxSearchFieldFrameMargin = 200;
+const CGFloat kDoodleTopMarginIPadPortrait = 82;
+const CGFloat kDoodleTopMarginIPadLandscape = 82;
+const CGFloat kNTPSearchFieldBottomPadding = 16;
+
+// Height for the doodle frame when Google is not the default search engine.
+const CGFloat kNonGoogleSearchDoodleHeight = 60;
+// Height for the header view on tablet when Google is not the default search
+// engine.
+const CGFloat kNonGoogleSearchHeaderHeightIPad = 10;
+
+enum InterfaceOrientation {
+  ALL = 0,
+  IPHONE_LANDSCAPE = 1,
+};
+
+// Returns the width necessary to fit |numberOfItem| items, with no padding on
+// the side.
+CGFloat widthForNumberOfItem(NSUInteger numberOfItem) {
+  return (numberOfItem - 1) * content_suggestions::spacingBetweenTiles() +
+         numberOfItem * [ContentSuggestionsMostVisitedCell defaultSize].width;
+}
+}
+
+namespace content_suggestions {
+
+NSUInteger numberOfTilesForWidth(CGFloat availableWidth) {
+  if (availableWidth > widthForNumberOfItem(4))
+    return 4;
+  if (availableWidth > widthForNumberOfItem(3))
+    return 3;
+  if (availableWidth > widthForNumberOfItem(2))
+    return 2;
+
+  return 1;
+}
+
+CGFloat spacingBetweenTiles() {
+  return IsIPadIdiom() ? kSpacingIPad : kSpacingIPhone;
+}
+
+CGRect getOrientationFrame(const CGRect frames[], CGFloat width) {
+  BOOL is_portrait = UIInterfaceOrientationIsPortrait(
+      [[UIApplication sharedApplication] statusBarOrientation]);
+  InterfaceOrientation orientation =
+      (IsIPadIdiom() || is_portrait) ? ALL : IPHONE_LANDSCAPE;
+
+  // Calculate width based on screen width and origin x.
+  CGRect frame = frames[orientation];
+  frame.size.width = fmax(width - 2 * frame.origin.x, 50);
+  return frame;
+}
+
+CGFloat centeredTilesMarginForWidth(CGFloat width) {
+  NSUInteger columns = numberOfTilesForWidth(width - 2 * spacingBetweenTiles());
+  CGFloat whitespace =
+      width - columns * [ContentSuggestionsMostVisitedCell defaultSize].width -
+      (columns - 1) * spacingBetweenTiles();
+  CGFloat margin = AlignValueToPixel(whitespace / 2);
+  DCHECK(margin >= spacingBetweenTiles());
+  return margin;
+}
+
+CGRect doodleFrame(CGFloat width, BOOL logoIsShowing) {
+  const CGRect kDoodleFrame[2] = {
+      CGRectMake(0, 66, 0, 120), CGRectMake(0, 56, 0, 120),
+  };
+  CGRect doodleFrame = getOrientationFrame(kDoodleFrame, width);
+  if (!IsIPadIdiom() && !logoIsShowing)
+    doodleFrame.size.height = kNonGoogleSearchDoodleHeight;
+  if (IsIPadIdiom()) {
+    doodleFrame.origin.y = IsPortrait() ? kDoodleTopMarginIPadPortrait
+                                        : kDoodleTopMarginIPadLandscape;
+  }
+  return doodleFrame;
+}
+
+CGRect searchFieldFrame(CGFloat width, BOOL logoIsShowing) {
+  CGFloat y = CGRectGetMaxY(doodleFrame(width, logoIsShowing));
+  CGFloat margin = centeredTilesMarginForWidth(width);
+  if (margin > kMaxSearchFieldFrameMargin)
+    margin = kMaxSearchFieldFrameMargin;
+  const CGRect kSearchFieldFrame[2] = {
+      CGRectMake(margin, y + 32, 0, 50), CGRectMake(margin, y + 16, 0, 50),
+  };
+  CGRect searchFieldFrame = getOrientationFrame(kSearchFieldFrame, width);
+  if (IsIPadIdiom()) {
+    CGFloat iPadTopMargin = IsPortrait() ? kDoodleTopMarginIPadPortrait
+                                         : kDoodleTopMarginIPadLandscape;
+    searchFieldFrame.origin.y += iPadTopMargin - 32;
+  }
+  return searchFieldFrame;
+}
+
+CGFloat heightForLogoHeader(CGFloat width,
+                            BOOL logoIsShowing,
+                            BOOL promoCanShow) {
+  CGFloat headerHeight = CGRectGetMaxY(searchFieldFrame(width, logoIsShowing)) +
+                         kNTPSearchFieldBottomPadding;
+  if (!IsIPadIdiom()) {
+    return headerHeight;
+  }
+  if (!logoIsShowing) {
+    return kNonGoogleSearchHeaderHeightIPad;
+  }
+  if (!promoCanShow) {
+    UIInterfaceOrientation orient =
+        [[UIApplication sharedApplication] statusBarOrientation];
+    const CGFloat kTopSpacingMaterialPortrait = 56;
+    const CGFloat kTopSpacingMaterialLandscape = 32;
+    headerHeight += UIInterfaceOrientationIsPortrait(orient)
+                        ? kTopSpacingMaterialPortrait
+                        : kTopSpacingMaterialLandscape;
+  }
+
+  return headerHeight;
+}
+
+}  // namespace content_suggestions
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
new file mode 100644
index 0000000..1722466
--- /dev/null
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
@@ -0,0 +1,215 @@
+// 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/ui/content_suggestions/content_suggestions_collection_utils.h"
+
+#include "base/memory/ptr_util.h"
+#import "ios/chrome/test/base/scoped_block_swizzler.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace content_suggestions {
+
+class ContentSuggestionsCollectionUtilsTest : public PlatformTest {
+ public:
+  void SetAsIPad() {
+    device_type_swizzler_ = base::MakeUnique<ScopedBlockSwizzler>(
+        [UIDevice class], @selector(userInterfaceIdiom),
+        ^UIUserInterfaceIdiom(id self) {
+          return UIUserInterfaceIdiomPad;
+        });
+  }
+  void SetAsIPhone() {
+    device_type_swizzler_ = base::MakeUnique<ScopedBlockSwizzler>(
+        [UIDevice class], @selector(userInterfaceIdiom),
+        ^UIUserInterfaceIdiom(id self) {
+          return UIUserInterfaceIdiomPhone;
+        });
+  }
+
+ private:
+  std::unique_ptr<ScopedBlockSwizzler> device_type_swizzler_;
+};
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, orientationFramePortrait) {
+  // Setup.
+  CGRect rect1 = CGRectMake(10, 10, 0, 10);
+  CGRect rect2 = CGRectMake(20, 20, 0, 20);
+  const CGRect rects[2] = {rect1, rect2};
+
+  // Action.
+  CGRect result = getOrientationFrame(rects, 400);
+
+  // Tests.
+  rect1.size.width = 380;
+  EXPECT_TRUE(CGRectEqualToRect(rect1, result));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, orientationFrameLandscapeIPad) {
+  // Setup.
+  SetAsIPad();
+  CGRect rect1 = CGRectMake(10, 10, 0, 10);
+  CGRect rect2 = CGRectMake(20, 20, 0, 20);
+  const CGRect rects[2] = {rect1, rect2};
+  std::unique_ptr<ScopedBlockSwizzler> orientation_swizzler =
+      base::MakeUnique<ScopedBlockSwizzler>(
+          [UIApplication class], @selector(statusBarOrientation),
+          ^UIInterfaceOrientation(id self) {
+            return UIInterfaceOrientationLandscapeLeft;
+          });
+
+  // Action.
+  CGRect result = getOrientationFrame(rects, 400);
+
+  // Tests.
+  rect1.size.width = 380;
+  EXPECT_TRUE(CGRectEqualToRect(rect1, result));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, orientationFrameLandscapeIPhone) {
+  // Setup.
+  SetAsIPhone();
+  CGRect rect1 = CGRectMake(10, 10, 0, 10);
+  CGRect rect2 = CGRectMake(20, 20, 0, 20);
+  const CGRect rects[2] = {rect1, rect2};
+  std::unique_ptr<ScopedBlockSwizzler> orientation_swizzler =
+      base::MakeUnique<ScopedBlockSwizzler>(
+          [UIApplication class], @selector(statusBarOrientation),
+          ^UIInterfaceOrientation(id self) {
+            return UIInterfaceOrientationLandscapeLeft;
+          });
+
+  // Action.
+  CGRect result = getOrientationFrame(rects, 400);
+
+  // Tests.
+  rect2.size.width = 360;
+  EXPECT_TRUE(CGRectEqualToRect(rect2, result));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, centeredTilesMarginIPhone6) {
+  // Setup.
+  SetAsIPhone();
+
+  // Action.
+  CGFloat result = centeredTilesMarginForWidth(374);
+
+  // Tests.
+  EXPECT_EQ(17, result);
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, centeredTilesMarginIPad) {
+  // Setup.
+  SetAsIPad();
+
+  // Action.
+  CGFloat result = centeredTilesMarginForWidth(700);
+
+  // Tests.
+  EXPECT_EQ(168, result);
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPad) {
+  // Setup.
+  SetAsIPad();
+
+  // Action.
+  CGRect result = doodleFrame(500, YES);
+
+  // Test.
+  EXPECT_TRUE(CGRectEqualToRect(CGRectMake(0, 82, 500, 120), result));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPhone) {
+  // Setup.
+  SetAsIPhone();
+
+  // Action.
+  CGRect result = doodleFrame(500, YES);
+
+  // Test.
+  EXPECT_TRUE(CGRectEqualToRect(CGRectMake(0, 66, 500, 120), result));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPad) {
+  // Setup.
+  SetAsIPad();
+  CGFloat width = 500;
+  CGFloat margin = centeredTilesMarginForWidth(width);
+
+  // Action.
+  CGRect result = searchFieldFrame(width, YES);
+
+  // Test.
+  EXPECT_TRUE(
+      CGRectEqualToRect(CGRectMake(margin, 284, 500 - 2 * margin, 50), result));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPhone) {
+  // Setup.
+  SetAsIPhone();
+  CGFloat width = 500;
+  CGFloat margin = centeredTilesMarginForWidth(width);
+
+  // Action.
+  CGRect result = searchFieldFrame(width, YES);
+
+  // Test.
+  EXPECT_TRUE(
+      CGRectEqualToRect(CGRectMake(margin, 218, 500 - 2 * margin, 50), result));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, heightForLogoHeaderIPad) {
+  // Setup.
+  SetAsIPad();
+
+  // Action, tests.
+  EXPECT_EQ(350, heightForLogoHeader(500, YES, YES));
+  EXPECT_EQ(406, heightForLogoHeader(500, YES, NO));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, heightForLogoHeaderIPhone) {
+  // Setup.
+  SetAsIPhone();
+
+  // Action, tests.
+  EXPECT_EQ(284, heightForLogoHeader(500, YES, YES));
+  EXPECT_EQ(284, heightForLogoHeader(500, YES, NO));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, SizeIPhone6) {
+  // Setup.
+  SetAsIPhone();
+
+  // Test.
+  EXPECT_EQ(4U, numberOfTilesForWidth(360));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, SizeIPhone5) {
+  // Setup.
+  SetAsIPhone();
+
+  // Test.
+  EXPECT_EQ(3U, numberOfTilesForWidth(320));
+}
+
+// Test for iPad portrait and iPhone landscape.
+TEST_F(ContentSuggestionsCollectionUtilsTest, SizeLarge) {
+  // Test.
+  EXPECT_EQ(4U, numberOfTilesForWidth(720));
+}
+
+TEST_F(ContentSuggestionsCollectionUtilsTest, SizeIPadSplit) {
+  // Setup.
+  SetAsIPad();
+
+  // Test.
+  EXPECT_EQ(3U, numberOfTilesForWidth(360));
+}
+
+}  // namespace content_suggestions
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index de3ad7a..221f8e59 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -202,6 +202,8 @@
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/bookmarks",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/content_suggestions",
+    "//ios/chrome/browser/ui/content_suggestions/cells",
     "//ios/chrome/browser/ui/context_menu",
     "//ios/chrome/browser/ui/ntp/recent_tabs",
     "//ios/chrome/browser/ui/overscroll_actions",
diff --git a/ios/chrome/browser/ui/ntp/google_landing_view_controller.mm b/ios/chrome/browser/ui/ntp/google_landing_view_controller.mm
index 4f33b842..7e6b7fad 100644
--- a/ios/chrome/browser/ui/ntp/google_landing_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/google_landing_view_controller.mm
@@ -13,6 +13,7 @@
 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
+#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/context_menu/context_menu_coordinator.h"
 #import "ios/chrome/browser/ui/ntp/google_landing_data_source.h"
 #import "ios/chrome/browser/ui/ntp/most_visited_cell.h"
@@ -44,32 +45,14 @@
   NumberOfCollectionViewSections,
 };
 
-enum InterfaceOrientation {
-  ALL,
-  IPHONE_LANDSCAPE,
-};
-
 const CGFloat kVoiceSearchButtonWidth = 48;
 const UIEdgeInsets kSearchBoxStretchInsets = {3, 3, 3, 3};
 
-// Height for the doodle frame when Google is not the default search engine.
-const CGFloat kNonGoogleSearchDoodleHeight = 60;
-// Height for the header view on tablet when Google is not the default search
-// engine.
-const CGFloat kNonGoogleSearchHeaderHeightIPad = 10;
-
 const CGFloat kHintLabelSidePadding = 12;
-const CGFloat kNTPSearchFieldBottomPadding = 16;
 const CGFloat kWhatsNewHeaderHiddenHeight = 8;
-const CGFloat kDoodleTopMarginIPadPortrait = 82;
-const CGFloat kDoodleTopMarginIPadLandscape = 82;
 const NSInteger kMaxNumMostVisitedFaviconRows = 2;
-const CGFloat kMaxSearchFieldFrameMargin = 200;
 const CGFloat kShiftTilesDownAnimationDuration = 0.2;
 
-const CGFloat kMostVisitedPaddingIPhone = 16;
-const CGFloat kMostVisitedPaddingIPadFavicon = 24;
-
 }  // namespace
 
 @interface GoogleLandingViewController (UsedByGoogleLandingView)
@@ -204,14 +187,6 @@
 // pushed into the header view.
 @property(nonatomic, assign) BOOL canGoBack;
 
-// iPhone landscape uses a slightly different layout for the doodle and search
-// field frame. Returns the proper frame from |frames| based on orientation,
-// centered in the view.
-- (CGRect)getOrientationFrame:(const CGRect[])frames;
-// Returns the proper frame for the doodle.
-- (CGRect)doodleFrame;
-// Returns the proper frame for the search field.
-- (CGRect)searchFieldFrame;
 // Returns the height to use for the What's New promo view.
 - (CGFloat)promoHeaderHeight;
 // Add fake search field and voice search microphone.
@@ -246,8 +221,6 @@
 - (NSInteger)numberOfNonEmptyTilesShown;
 // Returns the URL for the mosted visited item in |self.mostVisitedData|.
 - (GURL)urlForIndex:(NSUInteger)index;
-// Returns the expected height of the NewTabPageHeaderView.
-- (CGFloat)heightForSectionWithOmnibox;
 // Returns the nearest ancestor view that is kind of |aClass|.
 - (UIView*)nearestAncestorOfView:(UIView*)view withClass:(Class)aClass;
 // Updates the collection view's scroll view offset for the next frame of the
@@ -255,8 +228,6 @@
 - (void)shiftTilesDownAnimationDidFire:(CADisplayLink*)link;
 // Returns the size to use for Most Visited cells in the NTP.
 - (CGSize)mostVisitedCellSize;
-// Returns the padding for use between Most Visited cells.
-- (CGFloat)mostVisitedCellPadding;
 
 @end
 
@@ -362,7 +333,8 @@
     CGFloat smallestDimension =
         viewSize.height > viewSize.width ? viewSize.width : viewSize.height;
     CGFloat cellWidth = AlignValueToPixel(
-        (smallestDimension - 3 * [self mostVisitedCellPadding]) / 2);
+        (smallestDimension - 3 * content_suggestions::spacingBetweenTiles()) /
+        2);
     if (cellWidth < maximumCellSize.width) {
       return CGSizeMake(cellWidth, cellWidth);
     } else {
@@ -373,90 +345,23 @@
   }
 }
 
-- (CGFloat)mostVisitedCellPadding {
-  return IsIPadIdiom() ? kMostVisitedPaddingIPadFavicon
-                       : kMostVisitedPaddingIPhone;
-}
-
 - (CGFloat)viewWidth {
   return [self.view frame].size.width;
 }
 
 - (int)numberOfColumns {
   CGFloat width = [self viewWidth];
-  CGFloat padding = [self mostVisitedCellPadding];
-  // Try to fit 4 columns.
-  if (width >= 5 * padding + _mostVisitedCellSize.width * 4)
-    return 4;
-  // Try to fit 3 columns.
-  if (width >= 4 * padding + _mostVisitedCellSize.width * 3)
-    return 3;
-  // Try to fit 2 columns.
-  if (width >= 3 * padding + _mostVisitedCellSize.width * 2)
-    return 2;
-  // We never want to have a layout with only one column, however: At launch,
-  // the view's size is initialized to the width of 320, which can only fit
-  // one column on iPhone 6 and 6+. TODO(crbug.com/506183): Get rid of the
-  // unecessary resize, and add a NOTREACHED() here.
-  return 1;
-}
 
-- (CGFloat)leftMargin {
-  int columns = [self numberOfColumns];
-  CGFloat whitespace = [self viewWidth] - columns * _mostVisitedCellSize.width -
-                       (columns - 1) * [self mostVisitedCellPadding];
-  CGFloat margin = AlignValueToPixel(whitespace / 2);
-  DCHECK(margin >= [self mostVisitedCellPadding]);
-  return margin;
-}
-
-- (CGRect)doodleFrame {
-  const CGRect kDoodleFrame[2] = {
-      {{0, 66}, {0, 120}}, {{0, 56}, {0, 120}},
-  };
-  CGRect doodleFrame = [self getOrientationFrame:kDoodleFrame];
-  if (!IsIPadIdiom() && !self.logoIsShowing)
-    doodleFrame.size.height = kNonGoogleSearchDoodleHeight;
-  if (IsIPadIdiom()) {
-    doodleFrame.origin.y = IsPortrait() ? kDoodleTopMarginIPadPortrait
-                                        : kDoodleTopMarginIPadLandscape;
-  }
-  return doodleFrame;
-}
-
-- (CGRect)searchFieldFrame {
-  CGFloat y = CGRectGetMaxY([self doodleFrame]);
-  CGFloat leftMargin = [self leftMargin];
-  if (leftMargin > kMaxSearchFieldFrameMargin)
-    leftMargin = kMaxSearchFieldFrameMargin;
-  const CGRect kSearchFieldFrame[2] = {
-      {{leftMargin, y + 32}, {0, 50}}, {{leftMargin, y + 16}, {0, 50}},
-  };
-  CGRect searchFieldFrame = [self getOrientationFrame:kSearchFieldFrame];
-  if (IsIPadIdiom()) {
-    CGFloat iPadTopMargin = IsPortrait() ? kDoodleTopMarginIPadPortrait
-                                         : kDoodleTopMarginIPadLandscape;
-    searchFieldFrame.origin.y += iPadTopMargin - 32;
-  }
-  return searchFieldFrame;
-}
-
-- (CGRect)getOrientationFrame:(const CGRect[])frames {
-  UIInterfaceOrientation orient =
-      [[UIApplication sharedApplication] statusBarOrientation];
-  InterfaceOrientation inter_orient =
-      (IsIPadIdiom() || UIInterfaceOrientationIsPortrait(orient))
-          ? ALL
-          : IPHONE_LANDSCAPE;
-
-  // Calculate width based on screen width and origin x.
-  CGRect frame = frames[inter_orient];
-  frame.size.width = fmax(self.view.bounds.size.width - 2 * frame.origin.x, 50);
-  return frame;
+  NSUInteger columns = content_suggestions::numberOfTilesForWidth(
+      width - 2 * content_suggestions::spacingBetweenTiles());
+  DCHECK(columns > 1);
+  return columns;
 }
 
 - (CGFloat)promoHeaderHeight {
-  CGFloat promoMaxWidth = [self viewWidth] - 2 * [self leftMargin];
+  CGFloat promoMaxWidth =
+      [self viewWidth] -
+      2 * content_suggestions::centeredTilesMarginForWidth([self viewWidth]);
   NSString* text = self.promoText;
   return [WhatsNewHeaderView heightToFitText:text inWidth:promoMaxWidth];
 }
@@ -470,7 +375,8 @@
       // Adjust the height of |_headerView| to fit its content which may have
       // been shifted due to the visibility of the doodle.
       CGRect headerFrame = [_headerView frame];
-      headerFrame.size.height = [self heightForSectionWithOmnibox];
+      headerFrame.size.height = content_suggestions::heightForLogoHeader(
+          [self viewWidth], self.logoIsShowing, self.promoCanShow);
       [_headerView setFrame:headerFrame];
 
       // Adjust vertical positioning of |_promoHeaderView|.
@@ -491,7 +397,8 @@
 
 // Initialize and add a search field tap target and a voice search button.
 - (void)addSearchField {
-  CGRect searchFieldFrame = [self searchFieldFrame];
+  CGRect searchFieldFrame = content_suggestions::searchFieldFrame(
+      [self viewWidth], self.logoIsShowing);
   _searchTapTarget.reset([[UIButton alloc] initWithFrame:searchFieldFrame]);
   if (IsIPadIdiom()) {
     UIImage* searchBoxImage = [[UIImage imageNamed:@"ntp_google_search_box"]
@@ -599,7 +506,8 @@
 }
 
 - (void)setFlowLayoutInset:(UICollectionViewFlowLayout*)layout {
-  CGFloat leftMargin = [self leftMargin];
+  CGFloat leftMargin =
+      content_suggestions::centeredTilesMarginForWidth([self viewWidth]);
   [layout setSectionInset:UIEdgeInsetsMake(0, leftMargin, 0, leftMargin)];
 }
 
@@ -615,11 +523,14 @@
       base::mac::ObjCCastStrict<UICollectionViewFlowLayout>(
           [_mostVisitedView collectionViewLayout]);
   [flowLayout setItemSize:_mostVisitedCellSize];
-  self.logoVendor.view.frame = [self doodleFrame];
+  self.logoVendor.view.frame =
+      content_suggestions::doodleFrame([self viewWidth], self.logoIsShowing);
 
   [self setFlowLayoutInset:flowLayout];
   [flowLayout invalidateLayout];
-  [_promoHeaderView setSideMargin:[self leftMargin]];
+  [_promoHeaderView
+      setSideMargin:content_suggestions::centeredTilesMarginForWidth(
+                        [self viewWidth])];
 
   // On the iPhone 6 Plus, if the app is started in landscape after a fresh
   // install, the UICollectionViewLayout incorrectly sizes the widths of the
@@ -646,7 +557,8 @@
       [self updateSearchField];
     }
   } else {
-    [_searchTapTarget setFrame:[self searchFieldFrame]];
+    [_searchTapTarget setFrame:content_suggestions::searchFieldFrame(
+                                   [self viewWidth], self.logoIsShowing)];
   }
 
   if (!_viewLoaded) {
@@ -668,7 +580,7 @@
   [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
   [flowLayout setItemSize:_mostVisitedCellSize];
   [flowLayout setMinimumInteritemSpacing:8];
-  [flowLayout setMinimumLineSpacing:[self mostVisitedCellPadding]];
+  [flowLayout setMinimumLineSpacing:content_suggestions::spacingBetweenTiles()];
   DCHECK(!_mostVisitedView);
   _mostVisitedView.reset([[UICollectionView alloc]
              initWithFrame:mostVisitedFrame
@@ -698,7 +610,8 @@
   NSArray* constraints =
       @[ _hintLabelLeadingConstraint, _voiceTapTrailingConstraint ];
   [_headerView updateSearchField:_searchTapTarget
-                withInitialFrame:[self searchFieldFrame]
+                withInitialFrame:content_suggestions::searchFieldFrame(
+                                     [self viewWidth], self.logoIsShowing)
               subviewConstraints:constraints
                        forOffset:[_mostVisitedView contentOffset].y];
 }
@@ -856,27 +769,6 @@
   [self.view setNeedsLayout];
 }
 
-- (CGFloat)heightForSectionWithOmnibox {
-  CGFloat headerHeight =
-      CGRectGetMaxY([self searchFieldFrame]) + kNTPSearchFieldBottomPadding;
-  if (IsIPadIdiom()) {
-    if (self.logoIsShowing) {
-      if (!self.promoCanShow) {
-        UIInterfaceOrientation orient =
-            [[UIApplication sharedApplication] statusBarOrientation];
-        const CGFloat kTopSpacingMaterialPortrait = 56;
-        const CGFloat kTopSpacingMaterialLandscape = 32;
-        headerHeight += UIInterfaceOrientationIsPortrait(orient)
-                            ? kTopSpacingMaterialPortrait
-                            : kTopSpacingMaterialLandscape;
-      }
-    } else {
-      headerHeight = kNonGoogleSearchHeaderHeightIPad;
-    }
-  }
-  return headerHeight;
-}
-
 #pragma mark - ToolbarOwner
 
 - (ToolbarController*)relinquishedToolbarController {
@@ -895,7 +787,8 @@
     referenceSizeForHeaderInSection:(NSInteger)section {
   CGFloat headerHeight = 0;
   if (section == SectionWithOmnibox) {
-    headerHeight = [self heightForSectionWithOmnibox];
+    headerHeight = content_suggestions::heightForLogoHeader(
+        [self viewWidth], self.logoIsShowing, self.promoCanShow);
     ((UICollectionViewFlowLayout*)collectionViewLayout).headerReferenceSize =
         CGSizeMake(0, headerHeight);
   } else if (section == SectionWithMostVisited) {
@@ -985,7 +878,9 @@
               UICollectionElementKindSectionHeader
                              withReuseIdentifier:@"whatsNew"
                                     forIndexPath:indexPath] retain]);
-      [_promoHeaderView setSideMargin:[self leftMargin]];
+      [_promoHeaderView
+          setSideMargin:content_suggestions::centeredTilesMarginForWidth(
+                            [self viewWidth])];
       [_promoHeaderView setDelegate:self];
       if (self.promoCanShow) {
         [_promoHeaderView setText:self.promoText];
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index 415852d..40a1fa71 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -9,6 +9,14 @@
   sources = [
     "dom_altering_lock.h",
     "dom_altering_lock.mm",
+    "mailto_handler.h",
+    "mailto_handler.mm",
+    "mailto_handler_gmail.h",
+    "mailto_handler_gmail.mm",
+    "mailto_handler_system_mail.h",
+    "mailto_handler_system_mail.mm",
+    "mailto_url_rewriter.h",
+    "mailto_url_rewriter.mm",
     "navigation_manager_util.h",
     "navigation_manager_util.mm",
     "network_activity_indicator_tab_helper.h",
@@ -22,6 +30,7 @@
     ":sad_tab_tab_helper_delegate",
     "//base",
     "//components/strings",
+    "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/alert_coordinator:alert_coordinator",
     "//ios/chrome/browser/ui/commands:commands",
@@ -30,6 +39,7 @@
     "//ios/web",
     "//ios/web:web_arc",
     "//ui/base",
+    "//url",
   ]
 }
 
@@ -44,6 +54,10 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
+    "mailto_handler_gmail_unittest.mm",
+    "mailto_handler_system_mail_unittest.mm",
+    "mailto_handler_unittest.mm",
+    "mailto_url_rewriter_unittest.mm",
     "navigation_manager_util_unittest.mm",
     "network_activity_indicator_tab_helper_unittest.mm",
     "repost_form_tab_helper_unittest.mm",
@@ -212,8 +226,8 @@
     "//ios/chrome/browser/ssl",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui/commands",
-    "//ios/chrome/browser/ui/overscroll_actions",
     "//ios/chrome/browser/ui/static_content",
+    "//ios/chrome/browser/web",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/voice",
     "//ios/third_party/material_components_ios",
diff --git a/ios/chrome/browser/web/external_app_launcher.mm b/ios/chrome/browser/web/external_app_launcher.mm
index 9489a55ca..d10f3d6 100644
--- a/ios/chrome/browser/web/external_app_launcher.mm
+++ b/ios/chrome/browser/web/external_app_launcher.mm
@@ -11,10 +11,12 @@
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/open_url_util.h"
+#import "ios/chrome/browser/web/mailto_url_rewriter.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "net/base/mac/url_conversions.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
+#include "url/url_constants.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -52,7 +54,7 @@
 }
 
 // Returns whether gURL has the scheme of a URL that initiates a call.
-BOOL UrlHasPhoneCallScheme(const GURL& gURL) {
+bool UrlHasPhoneCallScheme(const GURL& gURL) {
   return gURL.SchemeIs("tel") || gURL.SchemeIs("facetime") ||
          gURL.SchemeIs("facetime-audio");
 }
@@ -186,6 +188,18 @@
     }
   }
 
+  // Replaces |URL| with a rewritten URL if it is of mailto: scheme.
+  if (!experimental_flags::IsNativeAppLauncherEnabled() &&
+      gURL.SchemeIs(url::kMailToScheme)) {
+    MailtoURLRewriter* rewriter =
+        [[MailtoURLRewriter alloc] initWithStandardHandlers];
+    NSString* launchURL = [rewriter rewriteMailtoURL:gURL];
+    if (launchURL) {
+      URL = [NSURL URLWithString:launchURL];
+    }
+    UMA_HISTOGRAM_BOOLEAN("IOS.MailtoURLRewritten", launchURL != nil);
+  }
+
   // If the following call returns YES, an external application is about to be
   // launched and Chrome will go into the background now.
   // TODO(crbug.com/622735): This call still needs to be updated.
diff --git a/ios/chrome/browser/web/mailto_handler.h b/ios/chrome/browser/web/mailto_handler.h
new file mode 100644
index 0000000..53347a32
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler.h
@@ -0,0 +1,46 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_H_
+#define _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_H_
+
+#import <Foundation/Foundation.h>
+
+class GURL;
+
+// MailtoHandler is the base class for Mail client apps that can handle
+// mailto: URLs via custom URL schemes. To add support for Mail clients,
+// subclass from this and add it to MailtoURLRewriter class.
+@interface MailtoHandler : NSObject
+
+// Name of Mail client. This is a name that a user can recognize, e.g. @"Gmail".
+@property(nonatomic, readonly) NSString* appName;
+
+// The iOS App Store ID for the Mail client. This is usually a string of digits.
+@property(nonatomic, readonly) NSString* appStoreID;
+
+// Initializer that subclasses should call from -init.
+- (instancetype)initWithName:(NSString*)appName
+                  appStoreID:(NSString*)appStoreID;
+
+// Returns whether the Mail client app is installed.
+- (BOOL)isAvailable;
+
+// Returns the prefix to use with -rewriteMailtoURL:. This is usually the custom
+// URL scheme plus some operator prefix. Subclasses should override this method.
+- (NSString*)beginningScheme;
+
+// Returns a set of NSString for mailto: parameters supported by this handler.
+- (NSSet<NSString*>*)supportedHeaders;
+
+// Rewrites |gURL| into a URL with a different URL scheme that will cause a
+// native iOS app to be launched to handle the mailto: URL. Returns nil if
+// |gURL| is not a mailto: URL. Base class implementation provides the typical
+// use which rewrites mailto:user@domain.com?subject=arg to
+// mailtoScheme:/co?to=user@domain.com&subject=arg
+- (NSString*)rewriteMailtoURL:(const GURL&)gURL;
+
+@end
+
+#endif  // _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_H_
diff --git a/ios/chrome/browser/web/mailto_handler.mm b/ios/chrome/browser/web/mailto_handler.mm
new file mode 100644
index 0000000..d2982f8
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler.mm
@@ -0,0 +1,68 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/web/mailto_handler.h"
+
+#import "base/strings/sys_string_conversions.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
+
+#import <UIKit/UIKit.h>
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation MailtoHandler
+@synthesize appName = _appName;
+@synthesize appStoreID = _appStoreID;
+
+- (instancetype)initWithName:(NSString*)appName
+                  appStoreID:(NSString*)appStoreID {
+  self = [super init];
+  if (self) {
+    _appName = [appName copy];
+    _appStoreID = [appStoreID copy];
+  }
+  return self;
+}
+
+- (BOOL)isAvailable {
+  NSURL* baseURL = [NSURL URLWithString:[self beginningScheme]];
+  NSURL* testURL = [NSURL
+      URLWithString:[NSString stringWithFormat:@"%@://", [baseURL scheme]]];
+  return [[UIApplication sharedApplication] canOpenURL:testURL];
+}
+
+- (NSString*)beginningScheme {
+  // Subclasses should override this method.
+  return @"mailtohandler:/co?";
+}
+
+- (NSSet<NSString*>*)supportedHeaders {
+  return [NSSet<NSString*>
+      setWithObjects:@"to", @"cc", @"bcc", @"subject", @"body", nil];
+}
+
+- (NSString*)rewriteMailtoURL:(const GURL&)gURL {
+  if (!gURL.SchemeIs(url::kMailToScheme))
+    return nil;
+  NSMutableArray* outParams = [NSMutableArray array];
+  NSString* recipient = base::SysUTF8ToNSString(gURL.path());
+  if ([recipient length]) {
+    [outParams addObject:[NSString stringWithFormat:@"to=%@", recipient]];
+  }
+  NSString* query = base::SysUTF8ToNSString(gURL.query());
+  for (NSString* keyvalue : [query componentsSeparatedByString:@"&"]) {
+    NSArray* pair = [keyvalue componentsSeparatedByString:@"="];
+    if ([pair count] != 2U || ![[self supportedHeaders] containsObject:pair[0]])
+      continue;
+    [outParams
+        addObject:[NSString stringWithFormat:@"%@=%@", pair[0], pair[1]]];
+  }
+  return [NSString stringWithFormat:@"%@%@", [self beginningScheme],
+                                    [outParams componentsJoinedByString:@"&"]];
+}
+
+@end
diff --git a/ios/chrome/browser/web/mailto_handler_gmail.h b/ios/chrome/browser/web/mailto_handler_gmail.h
new file mode 100644
index 0000000..69f62cf
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler_gmail.h
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_GMAIL_H_
+#define _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_GMAIL_H_
+
+#import "ios/chrome/browser/web/mailto_handler.h"
+
+// Handler for Gmail client.
+@interface MailtoHandlerGmail : MailtoHandler
+@end
+
+#endif  // _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_GMAIL_H_
diff --git a/ios/chrome/browser/web/mailto_handler_gmail.mm b/ios/chrome/browser/web/mailto_handler_gmail.mm
new file mode 100644
index 0000000..23a37a9
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler_gmail.mm
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/web/mailto_handler_gmail.h"
+#include "base/strings/sys_string_conversions.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
+
+#import <UIKit/UIKit.h>
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation MailtoHandlerGmail
+
+- (instancetype)init {
+  // Gmail product name is not supposed to be localized.
+  self = [super initWithName:@"Gmail" appStoreID:@"422689480"];
+  return self;
+}
+
+- (NSString*)beginningScheme {
+  return @"googlegmail:/co?";
+}
+
+@end
diff --git a/ios/chrome/browser/web/mailto_handler_gmail_unittest.mm b/ios/chrome/browser/web/mailto_handler_gmail_unittest.mm
new file mode 100644
index 0000000..165e66b
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler_gmail_unittest.mm
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/web/mailto_handler_gmail.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+TEST(MailtoHandlerGmailTest, TestConstructor) {
+  MailtoHandlerGmail* handler = [[MailtoHandlerGmail alloc] init];
+  EXPECT_NSEQ(@"Gmail", [handler appName]);
+  EXPECT_NSEQ(@"422689480", [handler appStoreID]);
+  EXPECT_NSEQ(@"googlegmail:/co?", [handler beginningScheme]);
+}
diff --git a/ios/chrome/browser/web/mailto_handler_system_mail.h b/ios/chrome/browser/web/mailto_handler_system_mail.h
new file mode 100644
index 0000000..534fd6d
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler_system_mail.h
@@ -0,0 +1,14 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_SYSTEM_MAIL_H_
+#define _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_SYSTEM_MAIL_H_
+
+#import "ios/chrome/browser/web/mailto_handler.h"
+
+// Handler for Apple Mail client.
+@interface MailtoHandlerSystemMail : MailtoHandler
+@end
+
+#endif  // _IOS_CHROME_BROWSER_WEB_MAILTO_HANDLER_SYSTEM_MAIL_H_
diff --git a/ios/chrome/browser/web/mailto_handler_system_mail.mm b/ios/chrome/browser/web/mailto_handler_system_mail.mm
new file mode 100644
index 0000000..43b9f91
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler_system_mail.mm
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/web/mailto_handler_system_mail.h"
+
+#include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/web/mailto_url_rewriter.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation MailtoHandlerSystemMail
+
+- (instancetype)init {
+  NSString* name = l10n_util::GetNSString(IDS_IOS_SYSTEM_MAIL_APP);
+  self = [super initWithName:name appStoreID:[MailtoURLRewriter systemMailApp]];
+  return self;
+}
+
+- (BOOL)isAvailable {
+  // System Mail client app is always available.
+  return YES;
+}
+
+- (NSString*)rewriteMailtoURL:(const GURL&)gURL {
+  if (!gURL.SchemeIs(url::kMailToScheme))
+    return nil;
+  return base::SysUTF8ToNSString(gURL.spec());
+}
+
+@end
diff --git a/ios/chrome/browser/web/mailto_handler_system_mail_unittest.mm b/ios/chrome/browser/web/mailto_handler_system_mail_unittest.mm
new file mode 100644
index 0000000..86bb5c9f
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler_system_mail_unittest.mm
@@ -0,0 +1,29 @@
+// 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/chrome/browser/web/mailto_handler_system_mail.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+TEST(MailtoHandlerSystemMailTest, TestConstructor) {
+  MailtoHandler* handler = [[MailtoHandlerSystemMail alloc] init];
+  EXPECT_TRUE(handler);
+  EXPECT_NSEQ(@"Mail", [handler appName]);
+  EXPECT_GT([[handler appStoreID] length], 0U);
+  EXPECT_TRUE([handler isAvailable]);
+}
+
+TEST(MailtoHandlerSystemMailTest, TestRewrite) {
+  MailtoHandler* handler = [[MailtoHandlerSystemMail alloc] init];
+  NSString* result = [handler rewriteMailtoURL:GURL("mailto:user@domain.com")];
+  EXPECT_NSEQ(@"mailto:user@domain.com", result);
+  result = [handler rewriteMailtoURL:GURL("http://www.google.com")];
+  EXPECT_FALSE(result);
+}
diff --git a/ios/chrome/browser/web/mailto_handler_unittest.mm b/ios/chrome/browser/web/mailto_handler_unittest.mm
new file mode 100644
index 0000000..fa9029f
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_handler_unittest.mm
@@ -0,0 +1,45 @@
+// 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/chrome/browser/web/mailto_handler.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "url/gurl.h"
+
+TEST(MailtoHandlerTest, TestConstructor) {
+  MailtoHandler* handler =
+      [[MailtoHandler alloc] initWithName:@"Some App" appStoreID:@"12345"];
+  EXPECT_NSEQ(@"Some App", [handler appName]);
+  EXPECT_NSEQ(@"12345", [handler appStoreID]);
+  EXPECT_NSEQ(@"mailtohandler:/co?", [handler beginningScheme]);
+  EXPECT_GT([[handler supportedHeaders] count], 0U);
+}
+
+TEST(MailtoHandlerTest, TestRewriteGood) {
+  MailtoHandler* handler = [[MailtoHandler alloc] init];
+  // Tests mailto URL without a subject.
+  NSString* result = [handler rewriteMailtoURL:GURL("mailto:user@domain.com")];
+  EXPECT_NSEQ(@"mailtohandler:/co?to=user@domain.com", result);
+  // Tests mailto URL with a subject.
+  result =
+      [handler rewriteMailtoURL:GURL("mailto:user@domain.com?subject=hello")];
+  EXPECT_NSEQ(@"mailtohandler:/co?to=user@domain.com&subject=hello", result);
+  // Tests mailto URL with unrecognized query parameters.
+  result = [handler
+      rewriteMailtoURL:
+          GURL("mailto:someone@there.com?garbage=in&garbageOut&subject=trash")];
+  EXPECT_NSEQ(@"mailtohandler:/co?to=someone@there.com&subject=trash", result);
+}
+
+TEST(MailtoHandlerTest, TestRewriteBad) {
+  MailtoHandler* handler = [[MailtoHandler alloc] init];
+  NSString* result = [handler rewriteMailtoURL:GURL("http://www.google.com")];
+  EXPECT_FALSE(result);
+  result = [handler
+      rewriteMailtoURL:
+          GURL("mailto:user@domain.com?foo=bar&cc=someone@somewhere.com")];
+  EXPECT_NSEQ(@"mailtohandler:/co?to=user@domain.com&cc=someone@somewhere.com",
+              result);
+}
diff --git a/ios/chrome/browser/web/mailto_url_rewriter.h b/ios/chrome/browser/web/mailto_url_rewriter.h
new file mode 100644
index 0000000..d5deb1cd
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_url_rewriter.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_WEB_MAILTO_URL_REWRITER_H_
+#define IOS_CHROME_BROWSER_WEB_MAILTO_URL_REWRITER_H_
+
+#import <Foundation/Foundation.h>
+
+@class MailtoHandler;
+class GURL;
+
+// An object that manages the available Mail client apps. The currently selected
+// Mail client to handle mailto: URL is stored in a key in NSUserDefaults.
+// If the key in NSUserDefaults is not set, the system-provided Mail client app
+// will be used.
+@interface MailtoURLRewriter : NSObject
+
+// The unique ID of the Mail client app was set to handle mailto: URL scheme.
+@property(nonatomic, copy) NSString* defaultHandlerID;
+
+// Returns the ID as a string for the system-provided Mail client app.
++ (NSString*)systemMailApp;
+
+// An initializer returning an instance that has the standard set of
+// MailtoHandlers initialized. Unit tests can use -init and then set up the
+// different handlers.
+- (instancetype)initWithStandardHandlers;
+
+// Returns an array of all the currently supported Mail client apps that claims
+// to handle mailto: URL scheme through their own custom defined URL schemes.
+- (NSArray<MailtoHandler*>*)defaultHandlers;
+
+// Rewrites |gURL| into a new URL that can be "opened" to launch the Mail
+// client app. May return nil if |gURL| is not a mailto: URL or there are no
+// Mail client app available.
+- (NSString*)rewriteMailtoURL:(const GURL&)gURL;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_WEB_MAILTO_URL_REWRITER_H_
diff --git a/ios/chrome/browser/web/mailto_url_rewriter.mm b/ios/chrome/browser/web/mailto_url_rewriter.mm
new file mode 100644
index 0000000..f6c00c5
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_url_rewriter.mm
@@ -0,0 +1,206 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/web/mailto_url_rewriter.h"
+
+#import "base/logging.h"
+#import "ios/chrome/browser/web/mailto_handler.h"
+#import "ios/chrome/browser/web/mailto_handler_gmail.h"
+#import "ios/chrome/browser/web/mailto_handler_system_mail.h"
+
+#import <UIKit/UIKit.h>
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// The key for NSUserDefaults to store the Mail client selected to handle
+// mailto: URL scheme. If this key is not set, user has not made an explicit
+// choice for default mailto: handler and system-provided Mail client app will
+// be used.
+NSString* const kMailtoDefaultHandlerKey = @"MailtoHandlerDefault";
+}  // namespace
+
+@interface MailtoURLRewriter ()
+
+// Dictionary keyed by the App Store ID of the Mail client and the value is
+// the MailtoHandler object that can rewrite a mailto: URL.
+@property(nonatomic, strong)
+    NSMutableDictionary<NSString*, MailtoHandler*>* handlers;
+
+// Private method for testing to clear the default state.
++ (void)resetDefaultHandlerIDForTesting;
+
+// Private method to add one or more |handlerApp| objects to the list of known
+// Mail client apps.
+- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps;
+
+// Custom logic to handle the migration from Google Native App Launcher options
+// to this simplified mailto: URL only system. This must be called after
+// -addMailtoApp: has been called to add all the known Mail client apps.
+// TODO(crbug.com/718601): At some point in the future when almost all users
+// have upgraded, this method can be removed.
+- (void)migrateLegacyOptions;
+
+// For users who have not made an explicit choice (kMailtoDefaultHandlerKey
+// not set), if Gmail is detected, make an explicit choice for the user to
+// use Gmail app as the default mailto: handler.
+- (void)autoDefaultToGmailIfInstalled;
+
+@end
+
+@implementation MailtoURLRewriter
+@synthesize handlers = _handlers;
+
++ (NSString*)systemMailApp {
+  // This is the App Store ID for Apple Mail app.
+  // See https://itunes.apple.com/us/app/mail/id1108187098?mt=8
+  return @"1108187098";
+}
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _handlers = [NSMutableDictionary dictionary];
+  }
+  return self;
+}
+
+- (instancetype)initWithStandardHandlers {
+  self = [self init];
+  if (self) {
+    [self addMailtoApps:@[
+      [[MailtoHandlerSystemMail alloc] init], [[MailtoHandlerGmail alloc] init]
+    ]];
+  }
+  return self;
+}
+
+- (NSArray<MailtoHandler*>*)defaultHandlers {
+  return [_handlers allValues];
+}
+
+- (NSString*)defaultHandlerID {
+  NSString* value = [[NSUserDefaults standardUserDefaults]
+      stringForKey:kMailtoDefaultHandlerKey];
+  return value ? value : [[self class] systemMailApp];
+}
+
+- (void)setDefaultHandlerID:(NSString*)appStoreID {
+  DCHECK([appStoreID length]);
+  [[NSUserDefaults standardUserDefaults] setObject:appStoreID
+                                            forKey:kMailtoDefaultHandlerKey];
+}
+
+- (NSString*)rewriteMailtoURL:(const GURL&)gURL {
+  NSString* value = [self defaultHandlerID];
+  if ([value length]) {
+    MailtoHandler* handler = _handlers[value];
+    if (handler) {
+      return [handler rewriteMailtoURL:gURL];
+    }
+  }
+  return nil;
+}
+
+#pragma mark - Private
+
++ (void)resetDefaultHandlerIDForTesting {
+  [[NSUserDefaults standardUserDefaults]
+      removeObjectForKey:kMailtoDefaultHandlerKey];
+}
+
+- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps {
+  for (MailtoHandler* app in handlerApps) {
+    if ([app isAvailable])
+      [_handlers setObject:app forKey:[app appStoreID]];
+  }
+  [self migrateLegacyOptions];
+  [self autoDefaultToGmailIfInstalled];
+}
+
+//
+// Implements the migration logic for users of previous versions of Google
+// Chrome which supports Google Native App Launcher. The goal is to preserve
+// the previous behavior and support user choice of non-system provided Mail
+// client apps. System-provided Mail client app will be referred to as
+// "System Mail". The migration goals are:
+// - If a user has not made a choice for preferred mailto: handler in the past,
+//   the behavior after upgrading to this version should not change.
+// - If a user had disabled Gmail app as the preferred mailto: handler in the
+//   past, System Mail should be selected as the default mailto: handler.
+// - If a user installs Gmail app after the installation of Chrome, Gmail app
+//   will be chosen as the default mailto: handler, preserving the previous
+//   behavior.
+// - If a user installs another 3rd party mail client app, assuming that the
+//   3rd party mail client app is explicitly supported in Chrome, the new 3rd
+//   party mail client app can be set as the default mailto: handler through
+//   Tools > Settings > Content Settings.
+//
+// Two NSUserDefaults keys are interpreted with the following meanings:
+// - kLegacyShouldAutoOpenKey: The existence of this key implies that the user
+//   has used Chrome in the past and had Gmail app installed. Gmail app may or
+//   may not be installed currently.
+// - kMailtoDefaultHandlerKey: If this key is not set, System Mail app is used
+//   to handle mailto: URLs. If this key is set, the value is a string that is
+//   the key to |_handlers| which maps to a MailtoHandler object.
+//
+- (void)migrateLegacyOptions {
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+
+  // User previously had a selection made for opening mailto: links with Gmail,
+  // upgrade will set Gmail app to be the default mailto: handler. If user had
+  // opted out to using Gmail app (even when it was installed), migrate user
+  // to use system-provided Mail client.
+  // The key used in NSUserDefaults is from legacy code when Native App
+  // Launcher was still in use. The general format is a string prefix,
+  // underscore, then followed by the App Store ID of the application.
+  NSString* const kGmailAppStoreID = @"422689480";
+  NSString* const kLegacyShouldAutoOpenKey =
+      [NSString stringWithFormat:@"ShouldAutoOpenLinks_%@", kGmailAppStoreID];
+  NSNumber* legacyValue = [defaults objectForKey:kLegacyShouldAutoOpenKey];
+  if (legacyValue) {
+    switch ([legacyValue intValue]) {
+      case 0:
+      case 2: {
+        // If legacy user was using default (kAutoOpenLinksNotSet) or had
+        // explicitly chosen to use Gmail (kAutoOpenLinksYes), migrate to use
+        // Gmail app.
+        MailtoHandler* gmailHandler = _handlers[kGmailAppStoreID];
+        if ([gmailHandler isAvailable])
+          [defaults setObject:kGmailAppStoreID forKey:kMailtoDefaultHandlerKey];
+        else
+          [defaults removeObjectForKey:kMailtoDefaultHandlerKey];
+        break;
+      }
+      case 1:
+        // If legacy user was not using Gmail to handle mailto: links
+        // (kAutoOpenLinksNo), consider this an explicit user choice and
+        // migrate to use system-provided Mail app.
+        [defaults setObject:[[self class] systemMailApp]
+                     forKey:kMailtoDefaultHandlerKey];
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+    [defaults removeObjectForKey:kLegacyShouldAutoOpenKey];
+  }
+}
+
+- (void)autoDefaultToGmailIfInstalled {
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  // If a default handler for mailto: has already been set, user had made an
+  // explicit choice and no further changes should be done.
+  if ([defaults objectForKey:kMailtoDefaultHandlerKey])
+    return;
+
+  NSString* const kGmailAppStoreID = @"422689480";
+  MailtoHandler* gmailHandler = _handlers[kGmailAppStoreID];
+  if ([gmailHandler isAvailable])
+    [defaults setObject:kGmailAppStoreID forKey:kMailtoDefaultHandlerKey];
+}
+
+@end
diff --git a/ios/chrome/browser/web/mailto_url_rewriter_unittest.mm b/ios/chrome/browser/web/mailto_url_rewriter_unittest.mm
new file mode 100644
index 0000000..8edead2
--- /dev/null
+++ b/ios/chrome/browser/web/mailto_url_rewriter_unittest.mm
@@ -0,0 +1,214 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/web/mailto_url_rewriter.h"
+
+#import "ios/chrome/browser/web/mailto_handler.h"
+#import "ios/chrome/browser/web/mailto_handler_gmail.h"
+#import "ios/chrome/browser/web/mailto_handler_system_mail.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Defines the 3 valid states for ShouldAutoOpenLinks_422689480.
+enum {
+  kAutoOpenLinksNotSet = 0,
+  kAutoOpenLinksNo = 1,
+  kAutoOpenLinksYes = 2,
+};
+NSString* const kLegacyShouldAutoOpenKey = @"ShouldAutoOpenLinks_422689480";
+NSString* const kGmailAppStoreID = @"422689480";
+
+}  // namespace
+
+#pragma mark - Gmail not installed
+
+@interface FakeMailtoHandlerGmailNotInstalled : MailtoHandlerGmail
+@end
+
+@implementation FakeMailtoHandlerGmailNotInstalled
+- (BOOL)isAvailable {
+  return NO;
+}
+@end
+
+#pragma mark - Gmail is installed
+
+@interface FakeMailtoHandlerGmailInstalled : MailtoHandlerGmail
+@end
+
+@implementation FakeMailtoHandlerGmailInstalled
+- (BOOL)isAvailable {
+  return YES;
+}
+@end
+
+#pragma mark - MailtoURLRewriter private interfaces for testing.
+
+@interface MailtoURLRewriter ()
++ (void)resetDefaultHandlerIDForTesting;
+- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps;
+@end
+
+#pragma mark - Unit Test Cases
+
+class MailtoURLRewriterTest : public PlatformTest {
+ protected:
+  void SetUp() override { [MailtoURLRewriter resetDefaultHandlerIDForTesting]; }
+};
+
+// Tests that a standard instance has the expected values.
+TEST_F(MailtoURLRewriterTest, TestStandardInstance) {
+  MailtoURLRewriter* rewriter =
+      [[MailtoURLRewriter alloc] initWithStandardHandlers];
+  EXPECT_TRUE(rewriter);
+  // ID for system Mail client app must not be an empty string.
+  EXPECT_GT([[MailtoURLRewriter systemMailApp] length], 0U);
+
+  NSArray<MailtoHandler*>* handlers = [rewriter defaultHandlers];
+  EXPECT_GE([handlers count], 1U);
+  for (MailtoHandler* handler in handlers) {
+    ASSERT_TRUE(handler);
+    NSString* appStoreID = [handler appStoreID];
+    [rewriter setDefaultHandlerID:appStoreID];
+    EXPECT_NSEQ(appStoreID, [rewriter defaultHandlerID]);
+  }
+}
+
+TEST_F(MailtoURLRewriterTest, TestUserPreferencePersistence) {
+  // Sets up a first MailtoURLRewriter with at least 2 MailtoHandler objects.
+  // A faked Gmail handler that is installed must be used or -addMailtoApp:
+  // will just skip it.
+  MailtoURLRewriter* rewriter = [[MailtoURLRewriter alloc] init];
+  MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
+  MailtoHandler* fakeGmailHandler =
+      [[FakeMailtoHandlerGmailInstalled alloc] init];
+  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+
+  // Verifies that there must be 2 registered handlers. Then find a
+  // MailtoHandler that is not the current default and set that as the new
+  // default.
+  NSArray<MailtoHandler*>* handlers = [rewriter defaultHandlers];
+  ASSERT_GE([handlers count], 2U);
+  NSString* initialHandlerID = [rewriter defaultHandlerID];
+  NSString* otherHandlerID = nil;
+  for (MailtoHandler* handler in handlers) {
+    if (![initialHandlerID isEqualToString:[handler appStoreID]]) {
+      otherHandlerID = [handler appStoreID];
+      break;
+    }
+  }
+  ASSERT_TRUE([otherHandlerID length]);
+  [rewriter setDefaultHandlerID:otherHandlerID];
+
+  // Create a new MailtoURLRewriter object and verify that the current
+  // default is the |otherHandlerID| set in the previous step.
+  MailtoURLRewriter* rewriter2 = [[MailtoURLRewriter alloc] init];
+  [rewriter2 addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+  EXPECT_NSEQ(otherHandlerID, [rewriter2 defaultHandlerID]);
+}
+
+// Tests that a new user without Gmail app installed launches system Mail app.
+TEST_F(MailtoURLRewriterTest, TestNewUserNoGmail) {
+  // Sets pre-condition for a user who did not have Chrome installed.
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  [defaults removeObjectForKey:kLegacyShouldAutoOpenKey];
+  // A faked MailtoHandler for Gmail.
+  MailtoHandler* fakeGmailHandler =
+      [[FakeMailtoHandlerGmailNotInstalled alloc] init];
+
+  // Sets up a MailtoURLRewriter for testing.
+  MailtoURLRewriter* rewriter = [[MailtoURLRewriter alloc] init];
+  MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
+  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+
+  // Verify that MailtoURLRewriter will use the system Mail app.
+  EXPECT_NSEQ([MailtoURLRewriter systemMailApp], [rewriter defaultHandlerID]);
+}
+
+// Tests that a new user with Gmail app installed launches Gmail app.
+TEST_F(MailtoURLRewriterTest, TestNewUserWithGmail) {
+  // Sets pre-condition for a user who did not have Chrome installed.
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  [defaults removeObjectForKey:kLegacyShouldAutoOpenKey];
+  // A faked MailtoHandler for Gmail.
+  MailtoHandler* fakeGmailHandler =
+      [[FakeMailtoHandlerGmailInstalled alloc] init];
+
+  // Sets up a MailtoURLRewriter for testing.
+  MailtoURLRewriter* rewriter = [[MailtoURLRewriter alloc] init];
+  MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
+  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+
+  // Verify that MailtoURLRewriter will use Gmail app.
+  EXPECT_NSEQ(kGmailAppStoreID, [rewriter defaultHandlerID]);
+}
+
+// Tests that a user who has Gmail installed but has chosen not to use Gmail
+// as the app to handle mailto: links retains the same behavior when upgrading
+// Chrome.
+TEST_F(MailtoURLRewriterTest, TestUpgradeUserWithGmailDisabled) {
+  // Sets pre-condition for a user who had Chrome installed.
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  [defaults setObject:@(kAutoOpenLinksNo) forKey:kLegacyShouldAutoOpenKey];
+  // A faked MailtoHandler for Gmail.
+  MailtoHandler* fakeGmailHandler =
+      [[FakeMailtoHandlerGmailInstalled alloc] init];
+
+  // Sets up a MailtoURLRewriter for testing.
+  MailtoURLRewriter* rewriter = [[MailtoURLRewriter alloc] init];
+  MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
+  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+
+  // Verify that MailtoURLRewriter will use the system Mail app. As part of the
+  // "upgrade", the legacy key should be removed as well.
+  EXPECT_NSEQ([MailtoURLRewriter systemMailApp], [rewriter defaultHandlerID]);
+  EXPECT_FALSE([defaults objectForKey:kLegacyShouldAutoOpenKey]);
+}
+
+// Tests that a user who has Gmail installed and has chosen to use Gmail as the
+// app to handle mailto: links retains the same behavior.
+TEST_F(MailtoURLRewriterTest, TestUpgradeUserWithGmailEnabled) {
+  // Sets pre-condition for a user who did not have Chrome installed.
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  [defaults setObject:@(kAutoOpenLinksYes) forKey:kLegacyShouldAutoOpenKey];
+  // A faked MailtoHandler for Gmail.
+  MailtoHandler* fakeGmailHandler =
+      [[FakeMailtoHandlerGmailInstalled alloc] init];
+
+  // Sets up a MailtoURLRewriter for testing.
+  MailtoURLRewriter* rewriter = [[MailtoURLRewriter alloc] init];
+  MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
+  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+
+  // Verify that MailtoURLRewriter will use Gmail app. As part of the upgrade,
+  // the legacy key should be removed as well.
+  EXPECT_NSEQ(kGmailAppStoreID, [rewriter defaultHandlerID]);
+  EXPECT_FALSE([defaults objectForKey:kLegacyShouldAutoOpenKey]);
+}
+
+// Tests that a user who installed Gmail after started using Chrome gets Gmail
+// as the handler of mailto: links.
+TEST_F(MailtoURLRewriterTest, TestInstalledGmailAfterChrome) {
+  // Pre-condition for a user who did not have Chrome installed.
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  [defaults removeObjectForKey:kLegacyShouldAutoOpenKey];
+  // A faked MailtoHandler for Gmail.
+  MailtoHandler* fakeGmailHandler =
+      [[FakeMailtoHandlerGmailInstalled alloc] init];
+
+  // Sets up a MailtoURLRewriter for testing.
+  MailtoURLRewriter* rewriter = [[MailtoURLRewriter alloc] init];
+  MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init];
+  [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]];
+
+  // Verify that MailtoURLRewriter will use Gmail app.
+  EXPECT_NSEQ(kGmailAppStoreID, [rewriter defaultHandlerID]);
+}
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 49358a0..4c64b5a 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -192,6 +192,7 @@
     "//ios/chrome/common:unit_tests",
     "//ios/chrome/search_widget_extension:unit_tests",
     "//ios/chrome/test/base:unit_tests",
+    "//ios/shared/chrome/browser/ui/broadcaster:unit_tests",
     "//ios/shared/chrome/browser/ui/browser_list:unit_tests",
     "//ios/shared/chrome/browser/ui/commands:unit_tests",
     "//ios/shared/chrome/browser/ui/coordinators:unit_tests",
diff --git a/ios/shared/chrome/browser/ui/broadcaster/BUILD.gn b/ios/shared/chrome/browser/ui/broadcaster/BUILD.gn
new file mode 100644
index 0000000..6c327a2
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/broadcaster/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("broadcaster") {
+  sources = [
+    "chrome_broadcast_observer.h",
+    "chrome_broadcaster.h",
+    "chrome_broadcaster.mm",
+  ]
+  deps = [
+    "//base",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "chrome_broadcaster_unittest.mm",
+  ]
+  deps = [
+    ":broadcaster",
+    "//base",
+    "//testing/gtest",
+    "//testing/perf",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h b/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h
new file mode 100644
index 0000000..2852f317
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROADCASTER_CHROME_BROADCAST_OBSERVER_H_
+#define IOS_SHARED_CHROME_BROWSER_UI_BROADCASTER_CHROME_BROADCAST_OBSERVER_H_
+
+#import <UIKit/UIKit.h>
+
+// Protocol collecting all of the methods that broadcast keys will trigger
+// in an observer. Each key maps to a specific observer method as indicated.
+// (this mapping is generated in the implementation of the Broadcaster class).
+//
+// All of the methods in this protocol *must* return void and take exactly one
+// argument.
+@protocol ChromeBroadcastObserver<NSObject>
+@optional
+
+// Observer method for object that care about the current visibility of the tab
+// strip.
+- (void)broadcastTabStripVisible:(BOOL)visible;
+
+// Observer method for objects that care about the current vertical (y-axis)
+// scroll offset of the tab content area.
+- (void)broadcastContentScrollOffset:(CGFloat)offset;
+
+@end
+
+#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROADCASTER_CHROME_BROADCAST_OBSERVER_H_
diff --git a/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.h b/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.h
new file mode 100644
index 0000000..c5dab0360
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.h
@@ -0,0 +1,60 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_SHARED_CHROME_BROWSER_UI_BROADCASTER_CHROME_BROADCASTER_H_
+#define IOS_SHARED_CHROME_BROWSER_UI_BROADCASTER_CHROME_BROADCASTER_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/shared/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h"
+
+// An interface for lightweight synchronization of object properties; it is
+// generally intended to allow properties of UI-layer objects (typically view
+// controllers) to be observed without the observer needing to know the identity
+// of the observed object.
+//
+// ChromeBroadcaster is not intended to be able to be used for arbitrary
+// property observation; rather there is a defined protocol (BroadcastObserver)
+// of observer methods which are associated with broadcasting objects.
+//
+// (The class is named 'ChromeBroadcaster' to avoid various symbol conflicts
+// that the terser name 'Broadcaster' creates, but associated classes and
+// properties will refer to instances of this class as just a 'Broadcaster' for
+// simplicity.)
+@interface ChromeBroadcaster : NSObject
+
+// Makes the value (property) of |object| identified by |valueKey| observable
+// via |selector|. It is an error if |selector| is not defined in the
+// BroadcastObserver protocol, or if a value is already being broadcast
+// for |selector|.
+// If there are already observers for |selector|, they will have their observer
+// methods called immediately with the current broadcast value, before this
+// method returns.
+- (void)broadcastValue:(NSString*)valueKey
+              ofObject:(NSObject*)object
+              selector:(SEL)selector;
+
+// Stop broadcasting for |selector|. This doesn't remove or change any
+// observers for that selector. If |selector| is not being broadcast, this
+// method does nothing.
+- (void)stopBroadcastingForSelector:(SEL)selector;
+
+// Adds |observer| as an observer for |selector|. If |selector| is already being
+// broadcast, |selector| will be called on |observer| with the current value of
+// the broadcast property before this method returns.
+// It is an error if |selector| is not one of the methods in the
+// BroadcastObserver protocol, or if |observer| does not respond to |selector|.
+- (void)addObserver:(id<ChromeBroadcastObserver>)observer
+        forSelector:(SEL)selector;
+
+// Removes |observer| from the observers for |selector|. If |observer| is also
+// an observer for another selector, this method will not change that.
+// It is an error if |selector| is not one of the methods in the
+// BroadcastObserver protocol.
+- (void)removeObserver:(id<ChromeBroadcastObserver>)observer
+           forSelector:(SEL)selector;
+
+@end
+
+#endif  // IOS_SHARED_CHROME_BROWSER_UI_BROADCASTER_CHROME_BROADCASTER_H_
diff --git a/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.mm b/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.mm
new file mode 100644
index 0000000..fea4569
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.mm
@@ -0,0 +1,326 @@
+// 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/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.h"
+
+#import <objc/runtime.h>
+#include <memory>
+
+#import "base/ios/crb_protocol_observers.h"
+#import "base/logging.h"
+#import "base/mac/foundation_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Constructs an NSInvocation that will be used for repeated execution of
+// |selector|. |selector| must return void and take exactly one argument; it is
+// an error otherwise.
+NSInvocation* InvocationForBroadcasterSelector(SEL selector) {
+  struct objc_method_description methodDesc = protocol_getMethodDescription(
+      @protocol(ChromeBroadcastObserver), selector,
+      NO /* not a required method */, YES /* an instance method */);
+  DCHECK(methodDesc.types);
+  NSMethodSignature* method =
+      [NSMethodSignature signatureWithObjCTypes:methodDesc.types];
+  DCHECK(method);
+  // There should always be exactly three arguments: the two implicit arguments
+  // that every Objective-C method has (self and _cmd), and the single value
+  // argument for the broadcast value.
+  DCHECK(method.numberOfArguments == 3);
+
+  // Methods should always return void.
+  DCHECK(strcmp(method.methodReturnType, @encode(void)) == 0);
+
+  NSInvocation* invocation =
+      [NSInvocation invocationWithMethodSignature:method];
+  invocation.selector = selector;
+  return invocation;
+}
+}
+
+// Protocol observer subclass that explicitly implements <BroadcastObserver>.
+// Mostly this is used for the non-retaining observer set; this requires
+// observers to be removed before they dealloc. It would be better to track
+// observer lifetime via associated objects and remove them automatically.
+@interface BroadcastObservers : CRBProtocolObservers<ChromeBroadcastObserver>
++ (instancetype)observers;
+@end
+
+@implementation BroadcastObservers
++ (instancetype)observers {
+  return [self observersWithProtocol:@protocol(ChromeBroadcastObserver)];
+}
+@end
+
+// An object that collects the information about a single observed property.
+@interface BroadcastItem : NSObject
+// The observed object.
+@property(nonatomic, readonly) NSObject* object;
+// The observed key path.
+@property(nonatomic, readonly, copy) NSString* key;
+// The name associated with this observation.
+@property(nonatomic, readonly, copy) NSString* name;
+// The current value of |key| on |object|.
+@property(nonatomic, readonly) NSValue* currentValue;
+
+// Designated initializer.
+- (instancetype)initWithObject:(NSObject*)object
+                           key:(NSString*)key
+                          name:(NSString*)name NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// Add |observer| as a KVO of the object and key represented by the receiver.
+- (void)addObserver:(NSObject*)observer;
+// Remove |observer| from the KVO represented by the receiver.
+- (void)removeObserver:(NSObject*)observer;
+@end
+
+@implementation BroadcastItem
+@synthesize object = _object;
+@synthesize key = _key;
+@synthesize name = _name;
+
+- (instancetype)initWithObject:(NSObject*)object
+                           key:(NSString*)key
+                          name:(NSString*)name {
+  if ((self = [super init])) {
+    _object = object;
+    _key = [key copy];
+    _name = [name copy];
+  }
+  return self;
+}
+
+- (NSValue*)currentValue {
+  return [self.object valueForKey:self.key];
+}
+
+- (void)addObserver:(NSObject*)observer {
+  // Important: because the NSKeyValueObservingOptionInitial is passed in,
+  // addObserver:forKeyPath:options:context: will trigger a notification before
+  // it returns, so all of the infrastructure for handling the notification must
+  // be in place before the -addObserver... call.
+  NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew |
+                                       NSKeyValueObservingOptionOld |
+                                       NSKeyValueObservingOptionInitial;
+
+  // So that the selector name to be used for this object/key pair can be
+  // retrieved, it's added as an opaque 'context' object. These names are
+  // constant strings used as keys in the immutable _observerInvocations
+  // dictionary, which will thus live as long as this object does.
+  [self.object addObserver:observer
+                forKeyPath:self.key
+                   options:options
+                   context:(__bridge void*)self.name];
+}
+
+- (void)removeObserver:(NSObject*)observer {
+  [self.object removeObserver:observer
+                   forKeyPath:self.key
+                      context:(__bridge void*)self.name];
+}
+
+@end
+
+@interface ChromeBroadcaster ()
+// Map of selectors (as strings) to observers.
+@property(nonatomic, readonly)
+    NSMutableDictionary<NSString*, BroadcastObservers*>* observers;
+// Map of selectors (as strings) to broadcast items.
+@property(nonatomic, readonly)
+    NSMutableDictionary<NSString*, BroadcastItem*>* items;
+// Map of selectors (as strings) to invocations to be called on observers.
+// Invocations should be fetched from this dictionary via the
+// -invocationForName:value: method.
+@property(nonatomic, readonly)
+    NSDictionary<NSString*, NSInvocation*>* observerInvocations;
+@end
+
+@implementation ChromeBroadcaster
+@synthesize observers = _observers;
+@synthesize items = _items;
+@synthesize observerInvocations = _observerInvocations;
+
+- (instancetype)init {
+  if (self = [super init]) {
+    _observers =
+        [[NSMutableDictionary<NSString*, BroadcastObservers*> alloc] init];
+    _items = [[NSMutableDictionary<NSString*, BroadcastItem*> alloc] init];
+
+    // Pre-build the map of selector names to invocations.  The source of
+    // selectors is the optional methods defined (directly) in the
+    // BroadcastObserver protocol.
+    NSMutableDictionary<NSString*, NSInvocation*>* observerInvocations =
+        [[NSMutableDictionary<NSString*, NSInvocation*> alloc] init];
+
+    unsigned int methodCount;
+    objc_method_description* instanceMethods =
+        protocol_copyMethodDescriptionList(
+            @protocol(ChromeBroadcastObserver), NO /* not required methods */,
+            YES /* instance methods */, &methodCount);
+
+    for (unsigned int i = 0; i < methodCount; i++) {
+      struct objc_method_description method = instanceMethods[i];
+      NSString* name = NSStringFromSelector(method.name);
+      observerInvocations[name] = InvocationForBroadcasterSelector(method.name);
+    }
+    free(instanceMethods);
+
+    _observerInvocations = [observerInvocations copy];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  for (NSString* name in self.items.allKeys) {
+    [self stopBroadcastingForSelector:NSSelectorFromString(name)];
+  }
+}
+
+- (void)broadcastValue:(NSString*)valueKey
+              ofObject:(NSObject*)object
+              selector:(SEL)selector {
+  NSString* name = NSStringFromSelector(selector);
+  // Sanity check: |selector| must be one of the selectors that are mapped.
+  DCHECK(self.observerInvocations[name]);
+  // Sanity check: |selector| must not already be broadcast.
+  DCHECK(!self.items[name]);
+
+  // TODO(crbug.com/719911) -- Another sanity check is needed here -- verify
+  // that the value to be observed is of the type that |selector| expects.
+
+  self.items[name] =
+      [[BroadcastItem alloc] initWithObject:object key:valueKey name:name];
+
+  [self.items[name] addObserver:self];
+}
+
+// This is usually only needed when the broadcasting object goes away, since
+// it's an exception for an object with key-value observers to dealloc. This
+// should just be handled by associating monitor objects with the broadcasting
+// object instead.
+- (void)stopBroadcastingForSelector:(SEL)selector {
+  NSString* name = NSStringFromSelector(selector);
+  [self.items[name] removeObserver:self];
+  [self.items removeObjectForKey:name];
+}
+
+- (void)addObserver:(id<ChromeBroadcastObserver>)observer
+        forSelector:(SEL)selector {
+  NSString* name = NSStringFromSelector(selector);
+  // Sanity check: |selector| must be one of the keys that are mapped.
+  DCHECK(self.observerInvocations[name]);
+  // Sanity check: |observer| must implement the selector for |selector|.
+  DCHECK([observer respondsToSelector:selector]);
+
+  if (!self.observers[name])
+    self.observers[name] = [BroadcastObservers observers];
+
+  // If the key is already being broadcast, update the observer immediately.
+  if (self.items[name]) {
+    NSInvocation* call =
+        [self invocationForName:name value:self.items[name].currentValue];
+    [call invokeWithTarget:observer];
+  }
+
+  [self.observers[name] addObserver:observer];
+}
+
+- (void)removeObserver:(id<ChromeBroadcastObserver>)observer
+           forSelector:(SEL)selector {
+  NSString* name = NSStringFromSelector(selector);
+  // Sanity check: |selector| must be one of the selectors that are mapped.
+  DCHECK(self.observerInvocations[name]);
+
+  [self.observers[name] removeObserver:observer];
+  if (self.observers[name].empty)
+    [self.observers removeObjectForKey:name];
+}
+
+#pragma mark - KVO
+
+- (void)observeValueForKeyPath:(NSString*)keyPath
+                      ofObject:(id)object
+                        change:(NSDictionary<NSKeyValueChangeKey, id>*)change
+                       context:(void*)context {
+  // Bridge cast the context back to a selector name.
+  NSString* name = (__bridge NSString*)context;
+  // Sanity check: |name| must be one of the selectors that are mapped.
+  DCHECK(self.observerInvocations[name]);
+  // Sanity check: |object| should be the object currently being observed for
+  // |name|.
+  DCHECK(self.items[name].object == object);
+
+  BroadcastObservers* observers = self.observers[name];
+  if (!observers)
+    return;
+
+  // Sanity check: this isn't a change to a collection -- where the observed
+  // property is a collection object and this change is (for example) the
+  // addition of a new object to the collection. That kind of KVO isn't
+  // supported by ChromeBroadcaster.
+  DCHECK([change[NSKeyValueChangeKindKey]
+      isEqualToValue:@(NSKeyValueChangeSetting)]);
+
+  // If strings or other non-value types are being broadcast, then this will
+  // need to change. Either value will be nil if they aren't actually NSValues.
+  NSValue* newValue =
+      base::mac::ObjCCast<NSValue>(change[NSKeyValueChangeNewKey]);
+  NSValue* oldValue =
+      base::mac::ObjCCast<NSValue>(change[NSKeyValueChangeOldKey]);
+
+  // If the value is unchanged -- if the old and new values are equal -- then
+  // return without notifying observers.
+  // -isEqualToValue doesn't deal with nil arguments well, so nil check oldValue
+  // here.
+  if (oldValue && [newValue isEqualToValue:oldValue])
+    return;
+
+  NSInvocation* call = [self invocationForName:name value:newValue];
+
+  [call invokeWithTarget:observers];
+}
+
+#pragma mark - internal
+
+// Returns the invocation for the selector named |name|, populated with
+// |value| as the argument.
+// This method mutates the invocations stored in |self.observerInvocations|, so
+// any code that gets an invocation from that dictionary to be invoked should
+// do so through this method.
+- (NSInvocation*)invocationForName:(NSString*)name value:(NSValue*)value {
+  NSInvocation* invocation = self.observerInvocations[name];
+  // Attempt to cast |value| into an NSNumber; ObjCCast will instead return
+  // nil if this isn't possible.
+  NSNumber* valueAsNumber = base::mac::ObjCCast<NSNumber>(value);
+  std::string type([invocation.methodSignature getArgumentTypeAtIndex:2]);
+
+  if (type == @encode(BOOL)) {
+    DCHECK(valueAsNumber);
+    BOOL boolValue = valueAsNumber.boolValue;
+    [invocation setArgument:&boolValue atIndex:2];
+  } else if (type == @encode(CGFloat)) {
+    DCHECK(valueAsNumber);
+// CGFloat is a float on 32-bit devices, but a double on 64-bit devices.
+#if CGFLOAT_IS_DOUBLE
+    CGFloat cgfloatValue = valueAsNumber.doubleValue;
+#else
+    CGFloat cgfloatValue = valueAsNumber.floatValue;
+#endif
+    [invocation setArgument:&cgfloatValue atIndex:2];
+  } else {
+    // Add more clauses as needed.
+    NOTREACHED() << "Unknown argument type: " << type;
+    return nil;
+  }
+
+  return invocation;
+}
+
+@end
diff --git a/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster_unittest.mm b/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster_unittest.mm
new file mode 100644
index 0000000..4e7b1c2
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/broadcaster/chrome_broadcaster_unittest.mm
@@ -0,0 +1,319 @@
+// 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/shared/chrome/browser/ui/broadcaster/chrome_broadcaster.h"
+
+#import "ios/shared/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/perf/perf_test.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface TestObserver : NSObject<ChromeBroadcastObserver>
+@property(nonatomic) BOOL lastObservedBool;
+@property(nonatomic) CGFloat lastObservedCGFloat;
+@property(nonatomic) NSInteger tabStripVisibleCallCount;
+@property(nonatomic) NSInteger contentScrollOffsetCallCount;
+@end
+
+@implementation TestObserver
+@synthesize lastObservedBool = _lastObservedBool;
+@synthesize lastObservedCGFloat = _lastObservedCGFloat;
+@synthesize tabStripVisibleCallCount = _tabStripVisibleCallCount;
+@synthesize contentScrollOffsetCallCount = _contentScrollOffsetCallCount;
+
+- (void)broadcastTabStripVisible:(BOOL)visible {
+  self.tabStripVisibleCallCount++;
+  self.lastObservedBool = visible;
+}
+
+- (void)broadcastContentScrollOffset:(CGFloat)offset {
+  self.contentScrollOffsetCallCount++;
+  self.lastObservedCGFloat = offset;
+}
+
+@end
+
+@interface TestObservable : NSObject
+@property(nonatomic) BOOL observableBool;
+@property(nonatomic) CGFloat observableCGFloat;
+@end
+@implementation TestObservable
+@synthesize observableBool = _observableBool;
+@synthesize observableCGFloat = _observableCGFloat;
+@end
+
+typedef PlatformTest ChromeBroadcasterTest;
+
+TEST_F(ChromeBroadcasterTest, TestBroadcastBoolFirst) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  TestObservable* observable = [[TestObservable alloc] init];
+  observable.observableBool = NO;
+
+  [broadcaster broadcastValue:@"observableBool"
+                     ofObject:observable
+                     selector:@selector(broadcastTabStripVisible:)];
+
+  observable.observableBool = YES;
+
+  TestObserver* observer = [[TestObserver alloc] init];
+  EXPECT_FALSE(observer.lastObservedBool);
+  EXPECT_EQ(0, observer.tabStripVisibleCallCount);
+  [broadcaster addObserver:observer
+               forSelector:@selector(broadcastTabStripVisible:)];
+  EXPECT_EQ(1, observer.tabStripVisibleCallCount);
+  EXPECT_TRUE(observer.lastObservedBool);
+  observable.observableBool = NO;
+  EXPECT_FALSE(observer.lastObservedBool);
+  EXPECT_EQ(2, observer.tabStripVisibleCallCount);
+}
+
+TEST_F(ChromeBroadcasterTest, TestBroadcastFloatFirst) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  TestObservable* observable = [[TestObservable alloc] init];
+  observable.observableCGFloat = 1.0;
+
+  [broadcaster broadcastValue:@"observableCGFloat"
+                     ofObject:observable
+                     selector:@selector(broadcastContentScrollOffset:)];
+
+  observable.observableCGFloat = 2.0;
+
+  TestObserver* observer = [[TestObserver alloc] init];
+  EXPECT_EQ(0.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(0, observer.contentScrollOffsetCallCount);
+  [broadcaster addObserver:observer
+               forSelector:@selector(broadcastContentScrollOffset:)];
+  EXPECT_EQ(2.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(1, observer.contentScrollOffsetCallCount);
+  observable.observableCGFloat = 3.0;
+  EXPECT_EQ(3.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(2, observer.contentScrollOffsetCallCount);
+}
+
+TEST_F(ChromeBroadcasterTest, TestObserveBoolFirst) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  TestObserver* observer = [[TestObserver alloc] init];
+  EXPECT_FALSE(observer.lastObservedBool);
+  EXPECT_EQ(0, observer.tabStripVisibleCallCount);
+  [broadcaster addObserver:observer
+               forSelector:@selector(broadcastTabStripVisible:)];
+  EXPECT_FALSE(observer.lastObservedBool);
+  EXPECT_EQ(0, observer.tabStripVisibleCallCount);
+
+  TestObservable* observable = [[TestObservable alloc] init];
+  observable.observableBool = YES;
+  EXPECT_FALSE(observer.lastObservedBool);
+  EXPECT_EQ(0, observer.tabStripVisibleCallCount);
+
+  [broadcaster broadcastValue:@"observableBool"
+                     ofObject:observable
+                     selector:@selector(broadcastTabStripVisible:)];
+  EXPECT_TRUE(observer.lastObservedBool);
+  EXPECT_EQ(1, observer.tabStripVisibleCallCount);
+  observable.observableBool = NO;
+  EXPECT_FALSE(observer.lastObservedBool);
+  EXPECT_EQ(2, observer.tabStripVisibleCallCount);
+}
+
+TEST_F(ChromeBroadcasterTest, TestObserveFloatFirst) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  TestObserver* observer = [[TestObserver alloc] init];
+  EXPECT_EQ(0.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(0, observer.contentScrollOffsetCallCount);
+  [broadcaster addObserver:observer
+               forSelector:@selector(broadcastContentScrollOffset:)];
+  EXPECT_EQ(0.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(0, observer.contentScrollOffsetCallCount);
+
+  TestObservable* observable = [[TestObservable alloc] init];
+  observable.observableCGFloat = 1.0;
+  EXPECT_EQ(0.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(0, observer.contentScrollOffsetCallCount);
+
+  [broadcaster broadcastValue:@"observableCGFloat"
+                     ofObject:observable
+                     selector:@selector(broadcastContentScrollOffset:)];
+  EXPECT_EQ(1.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(1, observer.contentScrollOffsetCallCount);
+
+  observable.observableCGFloat = 2.0;
+  EXPECT_EQ(2.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(2, observer.contentScrollOffsetCallCount);
+}
+
+TEST_F(ChromeBroadcasterTest, TestBroadcastManyFloats) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  NSMutableArray<TestObserver*>* observers = [[NSMutableArray alloc] init];
+  for (size_t i = 0; i < 100; i++) {
+    [observers addObject:[[TestObserver alloc] init]];
+    [broadcaster addObserver:observers.lastObject
+                 forSelector:@selector(broadcastContentScrollOffset:)];
+  }
+
+  TestObservable* observable = [[TestObservable alloc] init];
+  observable.observableCGFloat = 1.0;
+  [broadcaster broadcastValue:@"observableCGFloat"
+                     ofObject:observable
+                     selector:@selector(broadcastContentScrollOffset:)];
+  // All observers should have the initial value set.
+  for (TestObserver* observer in observers) {
+    EXPECT_EQ(1.0, observer.lastObservedCGFloat);
+    EXPECT_EQ(1, observer.contentScrollOffsetCallCount);
+  }
+
+  // Change the value a thousand times.
+  NSDate* start = [NSDate date];
+  for (size_t i = 0; i < 1000; i++) {
+    observable.observableCGFloat += 1.0;
+  }
+  NSTimeInterval elapsed = -[start timeIntervalSinceNow] * 1000.0 /* to ms */;
+
+  // Log the elapsed time for performance tracking.
+  perf_test::PrintResult("Broadcast", "", "100 observers, 1000 updates",
+                         elapsed, "ms", true /* "important" */);
+
+  EXPECT_EQ(1001.0, observable.observableCGFloat);
+  for (TestObserver* observer in observers) {
+    EXPECT_EQ(1001.0, observer.lastObservedCGFloat);
+    EXPECT_EQ(1001, observer.contentScrollOffsetCallCount);
+  }
+}
+
+TEST_F(ChromeBroadcasterTest, TestBroadcastDuplicateFloats) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  TestObservable* observable = [[TestObservable alloc] init];
+  observable.observableCGFloat = 1.0;
+
+  [broadcaster broadcastValue:@"observableCGFloat"
+                     ofObject:observable
+                     selector:@selector(broadcastContentScrollOffset:)];
+
+  observable.observableCGFloat = 2.0;
+
+  TestObserver* observer = [[TestObserver alloc] init];
+  [broadcaster addObserver:observer
+               forSelector:@selector(broadcastContentScrollOffset:)];
+  EXPECT_EQ(2.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(1, observer.contentScrollOffsetCallCount);
+  observable.observableCGFloat = 2.0;
+  EXPECT_EQ(2.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(1, observer.contentScrollOffsetCallCount);
+  observable.observableCGFloat = 3.0;
+  EXPECT_EQ(3.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(2, observer.contentScrollOffsetCallCount);
+  observable.observableCGFloat = 3.0;
+  EXPECT_EQ(3.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(2, observer.contentScrollOffsetCallCount);
+}
+
+TEST_F(ChromeBroadcasterTest, TestSeparateObservers) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  TestObserver* boolObserver = [[TestObserver alloc] init];
+  TestObserver* floatObserver = [[TestObserver alloc] init];
+
+  TestObservable* observable = [[TestObservable alloc] init];
+
+  [broadcaster broadcastValue:@"observableBool"
+                     ofObject:observable
+                     selector:@selector(broadcastTabStripVisible:)];
+  [broadcaster broadcastValue:@"observableCGFloat"
+                     ofObject:observable
+                     selector:@selector(broadcastContentScrollOffset:)];
+
+  [broadcaster addObserver:boolObserver
+               forSelector:@selector(broadcastTabStripVisible:)];
+  [broadcaster addObserver:floatObserver
+               forSelector:@selector(broadcastContentScrollOffset:)];
+  EXPECT_FALSE(boolObserver.lastObservedBool);
+  EXPECT_EQ(1, boolObserver.tabStripVisibleCallCount);
+  EXPECT_EQ(0, floatObserver.tabStripVisibleCallCount);
+  EXPECT_EQ(0.0, floatObserver.lastObservedCGFloat);
+  EXPECT_EQ(1, floatObserver.contentScrollOffsetCallCount);
+  EXPECT_EQ(0, boolObserver.contentScrollOffsetCallCount);
+
+  observable.observableCGFloat = 5.0;
+  EXPECT_EQ(5.0, floatObserver.lastObservedCGFloat);
+  EXPECT_EQ(2, floatObserver.contentScrollOffsetCallCount);
+  EXPECT_EQ(0.0, boolObserver.lastObservedCGFloat);
+  EXPECT_EQ(0, boolObserver.contentScrollOffsetCallCount);
+
+  observable.observableBool = YES;
+  EXPECT_TRUE(boolObserver.lastObservedBool);
+  EXPECT_EQ(2, boolObserver.tabStripVisibleCallCount);
+  EXPECT_FALSE(floatObserver.lastObservedBool);
+  EXPECT_EQ(0, floatObserver.tabStripVisibleCallCount);
+}
+
+TEST_F(ChromeBroadcasterTest, TestStopBroadcasting) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  TestObservable* observable = [[TestObservable alloc] init];
+  observable.observableCGFloat = 1.0;
+
+  [broadcaster broadcastValue:@"observableCGFloat"
+                     ofObject:observable
+                     selector:@selector(broadcastContentScrollOffset:)];
+
+  observable.observableCGFloat = 2.0;
+
+  TestObserver* observer = [[TestObserver alloc] init];
+  [broadcaster addObserver:observer
+               forSelector:@selector(broadcastContentScrollOffset:)];
+  EXPECT_EQ(2.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(1, observer.contentScrollOffsetCallCount);
+  observable.observableCGFloat = 3.0;
+  EXPECT_EQ(3.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(2, observer.contentScrollOffsetCallCount);
+  [broadcaster
+      stopBroadcastingForSelector:@selector(broadcastContentScrollOffset:)];
+  observable.observableCGFloat = 4.0;
+  EXPECT_EQ(3.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(2, observer.contentScrollOffsetCallCount);
+}
+
+TEST_F(ChromeBroadcasterTest, TestStopObserving) {
+  ChromeBroadcaster* broadcaster = [[ChromeBroadcaster alloc] init];
+  TestObservable* observable = [[TestObservable alloc] init];
+  observable.observableCGFloat = 1.0;
+
+  [broadcaster broadcastValue:@"observableBool"
+                     ofObject:observable
+                     selector:@selector(broadcastTabStripVisible:)];
+  [broadcaster broadcastValue:@"observableCGFloat"
+                     ofObject:observable
+                     selector:@selector(broadcastContentScrollOffset:)];
+
+  observable.observableCGFloat = 2.0;
+  observable.observableBool = YES;
+  TestObserver* observer = [[TestObserver alloc] init];
+
+  [broadcaster addObserver:observer
+               forSelector:@selector(broadcastTabStripVisible:)];
+  [broadcaster addObserver:observer
+               forSelector:@selector(broadcastContentScrollOffset:)];
+  EXPECT_EQ(2.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(1, observer.contentScrollOffsetCallCount);
+  EXPECT_TRUE(observer.lastObservedBool);
+  EXPECT_EQ(1, observer.tabStripVisibleCallCount);
+  observable.observableCGFloat = 3.0;
+  EXPECT_EQ(3.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(2, observer.contentScrollOffsetCallCount);
+  [broadcaster removeObserver:observer
+                  forSelector:@selector(broadcastContentScrollOffset:)];
+  observable.observableCGFloat = 4.0;
+  EXPECT_EQ(3.0, observer.lastObservedCGFloat);
+  EXPECT_EQ(2, observer.contentScrollOffsetCallCount);
+  observable.observableBool = NO;
+  EXPECT_FALSE(observer.lastObservedBool);
+  EXPECT_EQ(2, observer.tabStripVisibleCallCount);
+  [broadcaster removeObserver:observer
+                  forSelector:@selector(broadcastTabStripVisible:)];
+  observable.observableBool = YES;
+  EXPECT_FALSE(observer.lastObservedBool);
+  EXPECT_EQ(2, observer.tabStripVisibleCallCount);
+}
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index 2b2613f..7c60bb1 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -116,12 +116,13 @@
   }
 }
 
-mojom_component("mojom") {
-  output_prefix = "ipc_mojom"
-  macro_prefix = "IPC_MOJOM"
+mojom("mojom") {
   sources = [
     "ipc.mojom",
   ]
+  export_class_attribute = "IPC_EXPORT"
+  export_define = "IPC_IMPLEMENTATION"
+  export_header = "ipc/ipc_export.h"
 }
 
 mojom("test_interfaces") {
diff --git a/mojo/public/tools/bindings/blink_bindings_configuration.gni b/mojo/public/tools/bindings/blink_bindings_configuration.gni
index 36e9fda..bb0fc43 100644
--- a/mojo/public/tools/bindings/blink_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/blink_bindings_configuration.gni
@@ -31,4 +31,3 @@
 }
 
 blacklist = []
-component_macro_suffix = "_BLINK"
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 6fe0750..0d020488 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -80,5 +80,3 @@
         config = read_file(typemap, "scope")
       } ]
 }
-
-component_macro_suffix = ""
diff --git a/mojo/public/tools/bindings/generate_export_header.py b/mojo/public/tools/bindings/generate_export_header.py
deleted file mode 100755
index 614ad593..0000000
--- a/mojo/public/tools/bindings/generate_export_header.py
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-"""Generates a C++ header to define a component export macro."""
-
-import argparse
-import os
-import sys
-
-
-_TEMPLATE = """// 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 {{guard}}
-#define {{guard}}
-
-#if defined(COMPONENT_BUILD)
-#if defined(WIN32)
-
-#if defined({{impl_macro}})
-#define {{export_macro}} __declspec(dllexport)
-#else
-#define {{export_macro}} __declspec(dllimport)
-#endif  // defined({{impl_macro}})
-
-#else  // defined(WIN32)
-
-#if defined({{impl_macro}})
-#define {{export_macro}} __attribute__((visibility("default")))
-#else
-#define {{export_macro}}
-#endif
-
-#endif
-
-#else  // defined(COMPONENT_BUILD)
-#define {{export_macro}}
-#endif
-
-#endif  // {{guard}}
-"""
-
-
-def WriteHeader(output_file, relative_path, prefix):
-  substitutions = {
-    "guard": relative_path.replace("/", "_").replace(".", "_").upper() + "_",
-    "impl_macro": prefix + "_IMPL",
-    "export_macro": prefix + "_EXPORT",
-  }
-  contents = _TEMPLATE
-  for k, v in substitutions.iteritems():
-    contents = contents.replace("{{%s}}" % k, v)
-  with open(output_file, "w") as f:
-    f.write(contents)
-
-
-def main():
-  parser = argparse.ArgumentParser(
-      description=__doc__,
-      formatter_class=argparse.RawDescriptionHelpFormatter)
-  parser.add_argument(
-      "--macro_prefix", type=str, required=True,
-      help=("A prefix used to generate _IMPL and _EXPORT macro names."))
-  parser.add_argument(
-      "--output_file", type=str, required=True,
-      help=("The file path to which the generated header should be written."))
-  parser.add_argument(
-      "--relative_path", type=str, required=True,
-      help=("The generated header's path relative to the generated output"
-            " root. Used to generate header guards."))
-
-  params, _ = parser.parse_known_args()
-  WriteHeader(params.output_file, params.relative_path, params.macro_prefix)
-
-
-if __name__ == "__main__":
-  sys.exit(main())
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
index 3033a99..c400868 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
@@ -22,10 +22,6 @@
 #include "{{import.path}}-shared-internal.h"
 {%- endfor %}
 
-{%- if export_header %}
-#include "{{export_header}}"
-{%- endif %}
-
 namespace mojo {
 namespace internal {
 class ValidationContext;
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
index 148dc7ae..b7c3070 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
@@ -57,10 +57,6 @@
 #include "{{import.path}}-shared.h"
 {%- endfor %}
 
-{%- if export_header %}
-#include "{{export_header}}"
-{%- endif %}
-
 {{namespace_begin()}}
 
 {#--- Struct Forward Declarations -#}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
index a249eb6..005ba76b 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
@@ -2,7 +2,7 @@
 {%- set enum_name = union.name ~ "_Tag" -%}
 {%- import "struct_macros.tmpl" as struct_macros %}
 
-class {{export_attribute}} {{class_name}} {
+class {{class_name}} {
  public:
   // Used to identify Mojom Union Data Classes.
   typedef void MojomUnionDataType;
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index 7cd59b5..0c42bdd8 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -36,9 +36,6 @@
   "$mojom_generator_script",
 ]
 
-generate_export_header_script =
-    "$mojom_generator_root/generate_export_header.py"
-
 if (enable_mojom_typemapping) {
   if (!is_ios) {
     _bindings_configuration_files = [
@@ -75,11 +72,9 @@
   _bindings_configurations = [
     {
       typemaps = []
-      component_macro_suffix = ""
     },
     {
       variant = "blink"
-      component_macro_suffix = "_BLINK"
       for_blink = true
       typemaps = []
     },
@@ -146,30 +141,10 @@
 #       TODO(yzshen): Switch all existing users to use_new_js_bindings=true and
 #       remove the old mode.
 #
-#   component_output_prefix (optional)
-#       The prefix to use for the output_name of any component library emitted
-#       for generated C++ bindings. If this is omitted, C++ bindings targets are
-#       emitted as source_sets instead. Because this controls the name of the
-#       output shared library binary in the root output directory, it must be
-#       unique across the entire build configuration.
-#
-#   component_macro_prefix (optional)
-#       Required iff |component_output_prefix| is specified. This specifies a
-#       macro prefix to use for component export macros and should therefore
-#       be globally unique in the project. For example if this is "FOO_BAR",
-#       then the generated C++ sources will be built with FOO_BAR_IMPL defined,
-#       and the generated public headers will affix FOO_BAR_EXPORT to all public
-#       symbol definitions; the meaning of the EXPORT macro depends on whether
-#       the corresponding IMPL macro is defined, per standard practice with
-#       Chromium component exports.
-#
-#       If |component_output_prefix| is not defined, this parameter is unused.
-#
 # The following parameters are used to support the component build. They are
 # needed so that bindings which are linked with a component can use the same
 # export settings for classes. The first three are for the chromium variant, and
-# the last three are for the blink variant. These parameters are mutually
-# exclusive to |component_output_prefix| and |component_macro_prefix|.
+# the last three are for the blink variant.
 #   export_class_attribute (optional)
 #       The attribute to add to the class declaration. e.g. "CONTENT_EXPORT"
 #   export_define (optional)
@@ -205,7 +180,6 @@
     assert(defined(invoker.export_class_attribute))
     assert(defined(invoker.export_define))
     assert(defined(invoker.export_header))
-    assert(!defined(invoker.component_output_prefix))
   }
   if (defined(invoker.export_class_attribute_blink) ||
       defined(invoker.export_define_blink) ||
@@ -213,7 +187,6 @@
     assert(defined(invoker.export_class_attribute_blink))
     assert(defined(invoker.export_define_blink))
     assert(defined(invoker.export_header_blink))
-    assert(!defined(invoker.component_output_prefix))
   }
   if (defined(invoker.overridden_deps) || defined(invoker.component_deps)) {
     assert(defined(invoker.overridden_deps))
@@ -234,13 +207,6 @@
     all_deps += invoker.public_deps
   }
 
-  if (defined(invoker.component_output_prefix)) {
-    assert(defined(invoker.component_macro_prefix))
-  }
-  if (defined(invoker.component_macro_prefix)) {
-    assert(defined(invoker.component_output_prefix))
-  }
-
   group("${target_name}__is_mojom") {
   }
 
@@ -280,11 +246,6 @@
       }
     }
 
-    if (defined(invoker.component_output_prefix)) {
-      generated_shared_export_header =
-          rebase_path("${target_name}_shared_export.h", "", target_gen_dir)
-    }
-
     generator_shared_cpp_outputs = [
       "{{source_gen_dir}}/{{source_name_part}}.mojom-shared-internal.h",
       "{{source_gen_dir}}/{{source_name_part}}.mojom-shared.cc",
@@ -312,34 +273,6 @@
         "--depfile_target",
         "{{source_gen_dir}}/{{source_name_part}}.mojom-shared-internal.h",
       ]
-
-      if (defined(invoker.component_macro_prefix)) {
-        args += [
-          "--export_attribute",
-          "${invoker.component_macro_prefix}_SHARED_EXPORT",
-          "--export_header",
-          rebase_path(generated_shared_export_header, root_gen_dir),
-        ]
-      }
-    }
-  }
-
-  if (defined(invoker.component_macro_prefix)) {
-    shared_export_generator_target_name =
-        "${target_name}__generate_shared_export_header"
-    action(shared_export_generator_target_name) {
-      script = generate_export_header_script
-      outputs = [
-        get_path_info(generated_shared_export_header, "abspath"),
-      ]
-      args = [
-        "--macro_prefix",
-        "${invoker.component_macro_prefix}_SHARED",
-        "--output_file",
-        generated_shared_export_header,
-        "--relative_path",
-        rebase_path(generated_shared_export_header, root_gen_dir),
-      ]
     }
   }
 
@@ -355,9 +288,7 @@
           process_file_template(invoker.sources, generator_shared_cpp_outputs)
       deps += [ ":$generator_shared_target_name" ]
     }
-    public_deps = [
-      "//mojo/public/cpp/bindings",
-    ]
+    public_deps = []
     foreach(d, all_deps) {
       # Resolve the name, so that a target //mojo/something becomes
       # //mojo/something:something and we can append shared_cpp_sources_suffix
@@ -365,33 +296,6 @@
       full_name = get_label_info("$d", "label_no_toolchain")
       public_deps += [ "${full_name}_${shared_cpp_sources_suffix}" ]
     }
-    if (defined(invoker.component_macro_prefix)) {
-      defines = [ "${invoker.component_macro_prefix}_SHARED_IMPL" ]
-      sources += [ get_path_info(generated_shared_export_header, "abspath") ]
-      public_deps += [ ":$shared_export_generator_target_name" ]
-    }
-  }
-
-  shared_cpp_library_target_name = "${target_name}_shared"
-  if (!defined(invoker.component_output_prefix)) {
-    group(shared_cpp_library_target_name) {
-      if (defined(invoker.testonly)) {
-        testonly = invoker.testonly
-      }
-      public_deps = [
-        ":$shared_cpp_sources_target_name",
-      ]
-    }
-  } else {
-    component(shared_cpp_library_target_name) {
-      if (defined(invoker.testonly)) {
-        testonly = invoker.testonly
-      }
-      output_name = "${invoker.component_output_prefix}_shared"
-      public_deps = [
-        ":$shared_cpp_sources_target_name",
-      ]
-    }
   }
 
   # Generate code for variants.
@@ -461,29 +365,6 @@
         }
       }
 
-      if (defined(invoker.component_output_prefix)) {
-        export_header_generator_target_name =
-            "${target_name}${variant_suffix}__generate_export_header"
-        generated_export_header =
-            rebase_path("${target_name}${variant_suffix}_export.h",
-                        "",
-                        target_gen_dir)
-        action(export_header_generator_target_name) {
-          script = generate_export_header_script
-          outputs = [
-            get_path_info(generated_export_header, "abspath"),
-          ]
-          args = [
-            "--macro_prefix",
-            "${invoker.component_macro_prefix}${bindings_configuration.component_macro_suffix}",
-            "--output_file",
-            generated_export_header,
-            "--relative_path",
-            rebase_path(generated_export_header, root_gen_dir),
-          ]
-        }
-      }
-
       if (!cpp_only) {
         generator_js_outputs =
             [ "{{source_gen_dir}}/{{source_name_part}}.mojom.js" ]
@@ -535,15 +416,6 @@
           rebase_path(type_mappings_path, root_build_dir),
         ]
 
-        if (defined(invoker.component_macro_prefix)) {
-          args += [
-            "--export_attribute",
-            "${invoker.component_macro_prefix}${bindings_configuration.component_macro_suffix}_EXPORT",
-            "--export_header",
-            rebase_path(generated_export_header, root_gen_dir),
-          ]
-        }
-
         if (defined(bindings_configuration.for_blink) &&
             bindings_configuration.for_blink) {
           args += [ "--for_blink" ]
@@ -639,13 +511,7 @@
       }
     }
 
-    if (defined(invoker.component_output_prefix)) {
-      output_target_type = "component"
-    } else {
-      output_target_type = "source_set"
-    }
-
-    target(output_target_type, "${target_name}${variant_suffix}") {
+    source_set("${target_name}${variant_suffix}") {
       if (defined(bindings_configuration.for_blink) &&
           bindings_configuration.for_blink &&
           defined(invoker.visibility_blink)) {
@@ -685,12 +551,6 @@
       if (enabled_sources != []) {
         public_deps += [ ":$generator_target_name" ]
       }
-      if (defined(invoker.component_output_prefix)) {
-        output_name = "${invoker.component_output_prefix}${variant_suffix}"
-        defines += [ "${invoker.component_macro_prefix}${bindings_configuration.component_macro_suffix}_IMPL" ]
-        sources += [ get_path_info(generated_export_header, "abspath") ]
-        deps += [ ":$export_header_generator_target_name" ]
-      }
       foreach(d, all_deps) {
         # Resolve the name, so that a target //mojo/something becomes
         # //mojo/something:something and we can append variant_suffix to
@@ -802,23 +662,3 @@
     }
   }
 }
-
-# A helper for the mojom() template above when component libraries are desired
-# for generated C++ bindings units. Supports all the same arguments as mojom()
-# except for the optional |component_output_prefix| and |component_macro_prefix|
-# arguments. These are instead shortened to |output_prefix| and |macro_prefix|
-# and are *required*.
-template("mojom_component") {
-  assert(defined(invoker.output_prefix) && defined(invoker.macro_prefix))
-
-  mojom(target_name) {
-    forward_variables_from(invoker,
-                           "*",
-                           [
-                             "output_prefix",
-                             "macro_prefix",
-                           ])
-    component_output_prefix = invoker.output_prefix
-    component_macro_prefix = invoker.macro_prefix
-  }
-}
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 5f7c8531e..95ba606 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -351,6 +351,11 @@
 }
 
 // static
+bool HttpUtil::IsMethodIdempotent(const std::string& method) {
+  return IsMethodSafe(method) || method == "PUT" || method == "DELETE";
+}
+
+// static
 bool HttpUtil::IsSafeHeader(const std::string& name) {
   std::string lower_name(base::ToLowerASCII(name));
   if (base::StartsWith(lower_name, "proxy-", base::CompareCase::SENSITIVE) ||
diff --git a/net/http/http_util.h b/net/http/http_util.h
index 5903238..8f84ce1 100644
--- a/net/http/http_util.h
+++ b/net/http/http_util.h
@@ -75,9 +75,14 @@
                                     base::Time now,
                                     base::TimeDelta* retry_after);
 
-  // True if the request method is "safe" (per section 4.2.1 of RFC 7231).
+  // Returns true if the request method is "safe" (per section 4.2.1 of
+  // RFC 7231).
   static bool IsMethodSafe(const std::string& method);
 
+  // Returns true if the request method is idempotent (per section 4.2.2 of
+  // RFC 7231).
+  static bool IsMethodIdempotent(const std::string& method);
+
   // Returns true if it is safe to allow users and scripts to specify the header
   // named |name|.
   static bool IsSafeHeader(const std::string& name);
diff --git a/testing/scripts/run_gtest_perf_test.py b/testing/scripts/run_gtest_perf_test.py
index df28f62..638929f 100755
--- a/testing/scripts/run_gtest_perf_test.py
+++ b/testing/scripts/run_gtest_perf_test.py
@@ -95,6 +95,8 @@
       with common.temporary_file() as tempfile_path:
         rc = common.run_command_with_output([executable] + extra_flags,
             env=env, stdoutfile=tempfile_path)
+        with open(tempfile_path) as f:
+          print f.read()
 
         # Now get the correct json format from the stdout to write to the
         # perf results file
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 816d6b7..52961cb2 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -26171,6 +26171,13 @@
   <int value="4" label="Internal error"/>
 </enum>
 
+<enum name="OverlaySupportFlag" type="int">
+  <int value="0" label="None"/>
+  <int value="1" label="Direct overlay support"/>
+  <int value="2" label="Scaling overlay support"/>
+  <int value="3" label="Direct and scaling overlay support"/>
+</enum>
+
 <enum name="OverscrollMode" type="int">
   <summary>Direction of the overscroll gesture.</summary>
   <int value="1" label="North">Scrolled from bottom towards top</int>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c2a5e7f..8adc6f0 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -22450,6 +22450,16 @@
   <summary>True if Chrome will try to use DirectComposition overlays.</summary>
 </histogram>
 
+<histogram name="GPU.DirectComposition.OverlaySupportFlags"
+    enum="OverlaySupportFlag">
+  <owner>jbauman@chromium.org</owner>
+  <summary>
+    These are the flags explaining how well overlays are supported on the
+    current display. The is recorded for every connected display when creating a
+    DirectComposition view context.
+  </summary>
+</histogram>
+
 <histogram name="GPU.DirectComposition.OverlaysUsed" enum="BooleanOverlayUsage">
   <owner>jbauman@chromium.org</owner>
   <summary>Whether or not a frame displays an overlay.</summary>
@@ -25175,6 +25185,14 @@
   <summary>Network channel used for invalidations.</summary>
 </histogram>
 
+<histogram name="IOS.MailtoURLRewritten" enum="Boolean">
+  <owner>pkl@chromium.org</owner>
+  <summary>
+    Counts the times when a mailto: URL is tapped by user and whether the URL
+    has been rewritten for an available native Mail client app or not.
+  </summary>
+</histogram>
+
 <histogram name="IOS.ShareExtension.ReceivedEntriesCount" units="files">
   <owner>olivierrobin@chromium.org</owner>
   <summary>
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index 06f9418..f128fe4 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -139,8 +139,6 @@
 system_health.webview_startup,"perezju@chromium.org, torne@chromium.org",
 system_health.webview_startup_multiprocess,,
 tab_switching.typical_25,vovoy@chromium.org,OS>Performance
-text_selection.character,mfomitchev@chromium.org,
-text_selection.direction,mfomitchev@chromium.org,
 thread_times.key_hit_test_cases,,
 thread_times.key_idle_power_cases,skyostil@chromium.org,
 thread_times.key_mobile_sites_smooth,,
diff --git a/tools/perf/benchmarks/benchmark_smoke_unittest.py b/tools/perf/benchmarks/benchmark_smoke_unittest.py
index b910e706..5beb23b 100644
--- a/tools/perf/benchmarks/benchmark_smoke_unittest.py
+++ b/tools/perf/benchmarks/benchmark_smoke_unittest.py
@@ -29,7 +29,6 @@
 from benchmarks import rasterize_and_record_micro
 from benchmarks import repaint
 from benchmarks import speedometer
-from benchmarks import text_selection
 from benchmarks import v8_browsing
 
 
@@ -97,7 +96,6 @@
     repaint,  # Often fails & takes long time to timeout on cq bot.
     speedometer,  # Takes 101 seconds.
     jetstream,  # Take 206 seconds.
-    text_selection,  # Always fails on cq bot.
     kraken,  # Flaky on Android, crbug.com/626174.
     v8_browsing, # Flaky on Android, crbug.com/628368.
     battor #Flaky on android, crbug.com/618330.
diff --git a/tools/perf/benchmarks/text_selection.py b/tools/perf/benchmarks/text_selection.py
deleted file mode 100644
index da2fbf5..0000000
--- a/tools/perf/benchmarks/text_selection.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright 2014 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.
-
-from core import perf_benchmark
-
-from telemetry import benchmark
-from telemetry.timeline import chrome_trace_category_filter
-from telemetry.web_perf import timeline_based_measurement
-
-import page_sets
-
-TEXT_SELECTION_CATEGORY = 'blink'
-TIMELINE_REQUIRED_CATEGORY = 'blink.console'
-
-
-class _TextSelection(perf_benchmark.PerfBenchmark):
-  page_set = page_sets.TextSelectionSitesPageSet
-
-  def CreateTimelineBasedMeasurementOptions(self):
-    cat_filter = chrome_trace_category_filter.ChromeTraceCategoryFilter()
-    cat_filter.AddIncludedCategory(TEXT_SELECTION_CATEGORY)
-    cat_filter.AddIncludedCategory(TIMELINE_REQUIRED_CATEGORY)
-
-    return timeline_based_measurement.Options(
-        overhead_level=cat_filter)
-
-  @classmethod
-  def Name(cls):
-    return 'text_selection'
-
-  @classmethod
-  def ValueCanBeAddedPredicate(cls, value, is_first_result):
-    if 'text-selection' not in value.name:
-      return False
-    return value.values != None
-
-
-# See crbug.com/519044
-@benchmark.Disabled('all')
-@benchmark.Owner(emails=['mfomitchev@chromium.org'])
-class TextSelectionDirection(_TextSelection):
-  """Measure text selection metrics while dragging a touch selection handle on a
-  subset of top ten mobile sites and using the 'direction' touch selection
-  strategy."""
-
-  def SetExtraBrowserOptions(self, options):
-    options.AppendExtraBrowserArgs(['--touch-selection-strategy=direction'])
-
-  @classmethod
-  def Name(cls):
-    return 'text_selection.direction'
-
-
-# See crbug.com/519044
-@benchmark.Disabled('all')
-@benchmark.Owner(emails=['mfomitchev@chromium.org'])
-class TextSelectionCharacter(_TextSelection):
-  """Measure text selection metrics while dragging a touch selection handle on a
-  subset of top ten mobile sites and using the 'character' touch selection
-  strategy."""
-
-  def SetExtraBrowserOptions(self, options):
-    options.AppendExtraBrowserArgs(['--touch-selection-strategy=character'])
-
-  @classmethod
-  def Name(cls):
-    return 'text_selection.character'
diff --git a/tools/perf/page_sets/jitter_pages.py b/tools/perf/page_sets/jitter_pages.py
deleted file mode 100644
index f56a9d9c..0000000
--- a/tools/perf/page_sets/jitter_pages.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright 2015 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.
-from telemetry import page as page_module
-from telemetry import story
-from telemetry.page import shared_page_state
-
-
-def _IssueMarkerAndScroll(action_runner):
-  with action_runner.CreateGestureInteraction('ScrollAction'):
-    action_runner.ScrollPage()
-
-class JitterPage(page_module.Page):
-
-  def __init__(self, url, page_set, name=''):
-    super(JitterPage, self).__init__(
-        url=url, page_set=page_set, name=name,
-        shared_page_state_class=shared_page_state.SharedDesktopPageState)
-
-  def RunPageInteractions(self, action_runner):
-    _IssueMarkerAndScroll(action_runner)
-
-class JitterPageSet(story.StorySet):
-
-  def __init__(self):
-    super(JitterPageSet, self).__init__()
-
-    urls = [
-        # one fixed layer with no jitter
-        'file://jitter_test_cases/fixed.html',
-        # one layer that jitters
-        'file://jitter_test_cases/one_layer_jitter.html',
-        # one layer inside another, both jitter together
-        'file://jitter_test_cases/child_jitter_with_parent.html',
-        # two non overlapping layers jitter
-        'file://jitter_test_cases/two_layers_jitter.html',
-        # jittering layer size bigger
-        'file://jitter_test_cases/big_layer_jitter.html',
-    ]
-
-    for url in urls:
-      self.AddStory(JitterPage(url, self))
diff --git a/tools/perf/page_sets/jitter_test_cases/big_layer_jitter.html b/tools/perf/page_sets/jitter_test_cases/big_layer_jitter.html
deleted file mode 100644
index 3ffffd64..0000000
--- a/tools/perf/page_sets/jitter_test_cases/big_layer_jitter.html
+++ /dev/null
@@ -1,77 +0,0 @@
-<!DOCTYPE html>
-<!-- saved from url=(0048)http://fiddle.jshell.net/bt8dhkrn/24/show/light/ -->
-<!-- here we have three divs that jitter together and the size of fixed-parent
-    div is bigger than in other test cases -->
-
-<style>
-  #jitter-big-parent {
-    width: 500px;
-    height: 500px;
-    background: papayawhip;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  #jitter-child {
-    width: 250px;
-    height: 250px;
-    background: green;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  #jitter-grand-child {
-    width: 100px;
-    height: 100px;
-    background: blue;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  #scrolled1 {
-    width: 500px;
-    height: 500px;
-    background: red;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  #scrolled2 {
-    width: 500px;
-    height: 500px;
-    background: green;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  body {
-    height: 2500px;
-  }
-</style>
-
-<script>
-window.onload=function(){
-tick = function(timestamp) {
-    document.getElementById("jitter-big-parent").style.transform = "translate(0px, " + document.body.scrollTop + "px)";
-    window.requestAnimationFrame(tick);
-}
-window.requestAnimationFrame(tick);
-
-jank = function(timestamp) {
-    for (var i = 0; i < 10; ++i) {
-        Date.now();
-    }
-    window.setTimeout(jank, 50);
-}
-window.setTimeout(jank, 50);
-}
-</script>
-
-<div id="jitter-big-parent" style="transform: translate(0px, 0px);">
-  <div id="jitter-child">
-      <div id="jitter-grand-child"></div>
-  </div>
-</div>
-<div id="scrolled1"></div>
-<div id="scrolled2"></div>
-</html>
diff --git a/tools/perf/page_sets/jitter_test_cases/child_jitter_with_parent.html b/tools/perf/page_sets/jitter_test_cases/child_jitter_with_parent.html
deleted file mode 100644
index b2409f77..0000000
--- a/tools/perf/page_sets/jitter_test_cases/child_jitter_with_parent.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!DOCTYPE html>
-<!-- saved from url=(0048)http://fiddle.jshell.net/bt8dhkrn/10/show/light/ -->
-<!-- here we have two divs that jitter together -->
-
-<style>
-  #jitter-parent {
-    width: 100px;
-    height: 100px;
-    background: papayawhip;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  #jitter-child {
-    width: 50px;
-    height: 50px;
-    background: green;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  #scrolled {
-    width: 100px;
-    height: 100px;
-    background: red;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
- body {
-    height: 2500px;
-  }
-</style>
-
-<script>
-window.onload=function(){
-tick = function(timestamp) {
-    document.getElementById("jitter-parent").style.transform = "translate(0px, " + document.body.scrollTop + "px)";
-    window.requestAnimationFrame(tick);
-}
-window.requestAnimationFrame(tick);
-
-jank = function(timestamp) {
-    for (var i = 0; i < 10; ++i) {
-        Date.now();
-    }
-    window.setTimeout(jank, 50);
-}
-window.setTimeout(jank, 50);
-}
-</script>
-
-<div id="jitter-parent" style="transform: translate(0px, 0px);">
-   <div id="jitter-child"></div>
-</div>
-<div id="scrolled"></div>
-</html>
diff --git a/tools/perf/page_sets/jitter_test_cases/fixed.html b/tools/perf/page_sets/jitter_test_cases/fixed.html
deleted file mode 100644
index df97e8e6..0000000
--- a/tools/perf/page_sets/jitter_test_cases/fixed.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<!-- saved from url=(0048)http://fiddle.jshell.net/bt8dhkrn/28/show/light/ -->
-<!-- here we have a fixed div that doesn't jitter -->
-
-<style>
-  #fixed {
-    width: 100px;
-    height: 100px;
-    background: papayawhip;
-    border: 1px solid black;
-    will-change: transform;
-    position : fixed;
-  }
-
-  body {
-    height: 2500px;
-  }
-</style>
-
-<script>
-window.onload=function(){
-}
-</script>
-
-<div id="fixed"></div>
-</html>
diff --git a/tools/perf/page_sets/jitter_test_cases/one_layer_jitter.html b/tools/perf/page_sets/jitter_test_cases/one_layer_jitter.html
deleted file mode 100644
index d61116d..0000000
--- a/tools/perf/page_sets/jitter_test_cases/one_layer_jitter.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<!-- saved from url=(0048)http://fiddle.jshell.net/bt8dhkrn/29/show/light/ -->
-<!-- here we have a single div that jitters -->
-
-<style>
-  #jitter {
-    width: 100px;
-    height: 100px;
-    background: papayawhip;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  body {
-    height: 2500px;
-  }
-</style>
-
-<script>
-window.onload=function(){
-tick = function(timestamp) {
-    document.getElementById("jitter").style.transform = "translate(0px, " + document.body.scrollTop + "px)";
-    window.requestAnimationFrame(tick);
-}
-window.requestAnimationFrame(tick);
-
-jank = function(timestamp) {
-    for (var i = 0; i < 10; ++i) {
-        Date.now();
-    }
-    window.setTimeout(jank, 50);
-}
-window.setTimeout(jank, 50);
-}
-</script>
-
-<div id="jitter" style="transform: translate(0px, 0px);"></div>
-</html>
diff --git a/tools/perf/page_sets/jitter_test_cases/two_layers_jitter.html b/tools/perf/page_sets/jitter_test_cases/two_layers_jitter.html
deleted file mode 100644
index 57d0d4a..0000000
--- a/tools/perf/page_sets/jitter_test_cases/two_layers_jitter.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<!-- saved from url=(0048)http://fiddle.jshell.net/bt8dhkrn/26/show/light/ -->
-<!-- here we have two divs that jitter -->
-
-<style type="text/css">
-  #jitter1 {
-    width: 100px;
-    height: 100px;
-    background: papayawhip;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  #jitter2 {
-    width: 100px;
-    height: 100px;
-    background: red;
-    border: 1px solid black;
-    will-change: transform;
-  }
-
-  body {
-    height: 2500px;
-  }
-  </style>
-
-<script>
-window.onload=function(){
-tick = function(timestamp) {
-    document.getElementById("jitter1").style.transform = "translate(0px, " + document.body.scrollTop + "px)";
-    document.getElementById("jitter2").style.transform = "translate(0px, " + document.body.scrollTop + "px)";
-    window.requestAnimationFrame(tick);
-}
-window.requestAnimationFrame(tick);
-
-jank = function(timestamp) {
-    for (var i = 0; i < 10; ++i) {
-        Date.now();
-    }
-    window.setTimeout(jank, 50);
-}
-window.setTimeout(jank, 50);
-}
-</script>
-
-<div id="jitter1" style="transform: translate(0px, 318px);"></div>
-<div id="jitter2" style="transform: translate(0px, 318px);"></div>
-</html>
diff --git a/tools/perf/page_sets/text_selection_sites.py b/tools/perf/page_sets/text_selection_sites.py
deleted file mode 100644
index 435379c..0000000
--- a/tools/perf/page_sets/text_selection_sites.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# Copyright 2014 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.
-from telemetry import story
-from telemetry.page import page as page_module
-from telemetry.page import shared_page_state
-
-
-class SimplePage(page_module.Page):
-
-  def __init__(self, url, page_set):
-    super(SimplePage, self).__init__(
-        url=url,
-        page_set=page_set,
-        shared_page_state_class=shared_page_state.SharedPageState,
-        credentials_path='data/credentials.json')
-    self.archive_data_file = 'data/text_selection_sites.json'
-
-  def RunNavigateSteps(self, action_runner):
-    super(SimplePage, self).RunNavigateSteps(action_runner)
-    action_runner.WaitForJavaScriptCondition(
-        'document.readyState == "complete"')
-
-
-class SimpleTextSelectionPage(SimplePage):
-
-  def __init__(self, url, page_set):
-    super(SimpleTextSelectionPage, self).__init__(url=url, page_set=page_set)
-
-  def RunPageInteractions(self, action_runner):
-    # Create a fixed position div in the top left corner of the page, and
-    # another one in the bottom right corner of the page.
-    # Select the text within the first div.
-    action_runner.ExecuteJavaScript('''
-        (function() {
-          var text_div = document.createElement('div');
-          var text_div_2 = document.createElement('div');
-
-          text_div.style.fontSize = text_div_2.style.fontSize = "10vh";
-          text_div.style.lineHeight = text_div_2.style.lineHeight = "normal";
-          text_div.style.color = text_div_2.style.color = "red";
-          text_div.style.zIndex = text_div_2.style.zIndex = "1000";
-          text_div.style.position = text_div_2.style.position = "fixed";
-          text_div.style.left = "10%";
-          text_div.style.top = "10%";
-          text_div_2.style.right="0";
-          text_div_2.style.bottom="2%";
-
-          text_div.id="text-for-perf-test";
-          text_div_2.id="text-for-perf-test-2";
-          text_div.innerText="Hello";
-          text_div_2.innerText="World";
-
-          document.body.insertBefore(text_div, document.body.firstChild);
-          document.body.appendChild(text_div_2);
-
-          var selection = window.getSelection();
-          var textNode = text_div.childNodes[0];
-          selection.setBaseAndExtent(textNode, 0, textNode, 5);
-
-          window.requestAnimationFrame(function() {
-            text_div.style.color="green";
-          });
-        })();''')
-
-    # Wait two frames so that the selection information is sent to chromium
-    # and it is able to process input events interacting with selection.
-    action_runner.WaitForJavaScriptCondition(
-        'document.getElementById("text-for-perf-test").style.color == "green"')
-    action_runner.ExecuteJavaScript('''
-          window.requestAnimationFrame(function() {
-            document.getElementById("text-for-perf-test").style.color="red";
-          });
-        ''')
-    action_runner.WaitForJavaScriptCondition(
-        'document.getElementById("text-for-perf-test").style.color == "red"')
-
-    # Confirm that the selection is set correctly.
-    text = action_runner.EvaluateJavaScript('window.getSelection().toString()')
-    assert text == "Hello"
-
-    # Tap on the selected text to make the handles show up.
-    with action_runner.CreateGestureInteraction('TapAction'):
-      action_runner.TapElement('#text-for-perf-test')
-
-    text_div_bottom = float(action_runner.EvaluateJavaScript('''
-        document.getElementById("text-for-perf-test").getClientRects()[0].bottom
-        '''))
-    text_div_2_bottom = float(action_runner.EvaluateJavaScript('''
-        document.getElementById(
-            "text-for-perf-test-2").getClientRects()[0].bottom
-        '''))
-    body_rect_str = action_runner.EvaluateJavaScript('''
-        var r = window.__GestureCommon_GetBoundingVisibleRect(document.body);
-        r.left + " " + r.top + " " + r.height + " " + r.width;
-        ''')
-    body_rect_left, body_rect_top, body_rect_height, body_rect_width = map(
-        float, body_rect_str.split())
-
-    # Start the drag gesture 5 pixels below the bottom left corner of the
-    # first div in order to drag the left selection handle.
-    p1_left_ratio = .1
-    p1_top_ratio = float((text_div_bottom + 5 - body_rect_top) /
-                         body_rect_height)
-
-    # End the drag gesture below the bottom right corner of the second div,
-    # so that the selection end is in the second div and we can easily
-    # determine the position of the corresponding handle.
-    p2_top_ratio = float((text_div_2_bottom - body_rect_top) /
-                         body_rect_height)
-
-    with action_runner.CreateGestureInteraction('DragAction-1'):
-      action_runner.DragPage(left_start_ratio=p1_left_ratio,
-          top_start_ratio=p1_top_ratio, left_end_ratio=.99,
-          top_end_ratio=p2_top_ratio, speed_in_pixels_per_second=300,
-          use_touch=1)
-
-    # Confirm that the selection has changed.
-    text = action_runner.EvaluateJavaScript('window.getSelection().toString()')
-    assert text != "Hello"
-
-    # Determine the coordinates of the end of the selection
-    sel_end_str = action_runner.EvaluateJavaScript('''
-          var rects = window.getSelection().getRangeAt(0).getClientRects();
-          var last_rect = rects[rects.length - 1];
-          last_rect.right + " " + last_rect.bottom;
-        ''')
-    sel_end_x, sel_end_y = map(float, sel_end_str.split())
-
-    # Start the second drag gesture 5 pixels below the end of the selection
-    # in order to drag the selection handle.
-    p2_left_ratio = float((sel_end_x - body_rect_left) / body_rect_width)
-    p2_top_ratio = float((sel_end_y + 5 - body_rect_top) / body_rect_height)
-
-    with action_runner.CreateGestureInteraction('DragAction-2'):
-      action_runner.DragPage(left_start_ratio=p2_left_ratio,
-          top_start_ratio=p2_top_ratio, left_end_ratio=p1_left_ratio,
-          top_end_ratio=p1_top_ratio, speed_in_pixels_per_second=300,
-          use_touch=1)
-
-    # Confirm that the selection is back to the text in the first div.
-    text = action_runner.EvaluateJavaScript('window.getSelection().toString()')
-    assert text == "Hello"
-
-
-class TextSelectionSitesPageSet(story.StorySet):
-  def __init__(self):
-    super(TextSelectionSitesPageSet, self).__init__(
-      archive_data_file='data/top_10_mobile.json',
-      cloud_storage_bucket=story.PARTNER_BUCKET)
-
-    # A subset of top_10_mobile page set
-    page_urls = [
-        'https://www.google.co.uk/#hl=en&q=science',
-        'https://m.facebook.com/rihanna',
-        'http://search.yahoo.com/search;_ylt=?p=google',
-        'http://www.baidu.com/s?word=google',
-        'https://mobile.twitter.com/justinbieber?skip_interstitial=true',
-        'http://yandex.ru/touchsearch?text=science'
-    ]
-
-    for url in page_urls:
-      self.AddStory(SimpleTextSelectionPage(url, self))
diff --git a/ui/display/display.cc b/ui/display/display.cc
index 25573f8..4ec8ea0 100644
--- a/ui/display/display.cc
+++ b/ui/display/display.cc
@@ -90,6 +90,15 @@
   g_forced_device_scale_factor = -1.0;
 }
 
+// static
+void Display::SetForceDeviceScaleFactor(double dsf) {
+  // Reset any previously set values and unset the flag.
+  g_forced_device_scale_factor = -1.0;
+
+  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+      switches::kForceDeviceScaleFactor, base::StringPrintf("%.2f", dsf));
+}
+
 Display::Display() : Display(kInvalidDisplayId) {}
 
 Display::Display(int64_t id) : Display(id, gfx::Rect()) {}
diff --git a/ui/display/display.h b/ui/display/display.h
index 0e704a48..03c9ddd 100644
--- a/ui/display/display.h
+++ b/ui/display/display.h
@@ -86,6 +86,9 @@
   // ensures that the command line is reevaluated.
   static void ResetForceDeviceScaleFactorForTesting();
 
+  // Resets the cache and sets a new force device scale factor.
+  static void SetForceDeviceScaleFactor(double dsf);
+
   // Sets/Gets unique identifier associated with the display.
   // -1 means invalid display and it doesn't not exit.
   int64_t id() const { return id_; }
diff --git a/ui/display/display_unittest.cc b/ui/display/display_unittest.cc
index dee754b4..aa0b55d5 100644
--- a/ui/display/display_unittest.cc
+++ b/ui/display/display_unittest.cc
@@ -64,6 +64,13 @@
   Display::ResetForceDeviceScaleFactorForTesting();
 }
 
+TEST(DisplayTest, ForcedDeviceScaleFactor) {
+  Display::SetForceDeviceScaleFactor(2);
+
+  EXPECT_EQ(2, Display::GetForcedDeviceScaleFactor());
+  Display::ResetForceDeviceScaleFactorForTesting();
+}
+
 TEST(DisplayTest, DisplayHDRValues) {
   base::test::ScopedCommandLine scoped_command_line;
   base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine();