diff --git a/DEPS b/DEPS
index 9e7cd4c..9b9bb21 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # 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': '72d17915df27ddf10d504f76be379a560820fd01',
+  'v8_revision': 'c76bb271ec40c918ff43ee111dccd027397e854e',
   # 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.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '302cd78d00c280cb212a5934a7a8293851e9650c',
+  'pdfium_revision': 'ef002c85521278e3082517297cf60372d7751cb1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
diff --git a/ash/system/audio/volume_view.cc b/ash/system/audio/volume_view.cc
index efbdd2f..c7ed0f3 100644
--- a/ash/system/audio/volume_view.cc
+++ b/ash/system/audio/volume_view.cc
@@ -20,6 +20,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/vector_icon_types.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/button/custom_button.h"
@@ -130,7 +131,9 @@
       l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_VOLUME));
   tri_view_->AddView(TriView::Container::CENTER, slider_);
 
-  set_background(views::Background::CreateSolidBackground(kBackgroundColor));
+  set_background(views::Background::CreateThemedSolidBackground(
+      this, ui::NativeTheme::kColorId_BubbleBackground));
+
   Update();
 }
 
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 5c963cf..1857750 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -64,6 +64,7 @@
 #include "ui/gfx/skia_util.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/message_center_style.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/view.h"
@@ -472,7 +473,6 @@
       // This is the case where a volume control or brightness control bubble
       // is created.
       init_params.max_height = default_bubble_height_;
-      init_params.bg_color = kBackgroundColor;
     } else {
       init_params.bg_color = kHeaderBackgroundColor;
     }
diff --git a/ash/system/tray/tray_constants.cc b/ash/system/tray/tray_constants.cc
index c3ae671b..0ce7b54 100644
--- a/ash/system/tray/tray_constants.cc
+++ b/ash/system/tray/tray_constants.cc
@@ -64,7 +64,6 @@
 
 const int kTrayToggleButtonWidth = 68;
 
-const SkColor kBackgroundColor = SK_ColorWHITE;
 const SkColor kPublicAccountUserCardTextColor = SkColorSetRGB(0x66, 0x66, 0x66);
 const SkColor kPublicAccountUserCardNameColor = SK_ColorBLACK;
 
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index f2977a34..952dd005 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -77,7 +77,6 @@
 // The width of ToggleButton views including any border padding.
 extern const int kTrayToggleButtonWidth;
 
-extern const SkColor kBackgroundColor;
 extern const SkColor kPublicAccountUserCardTextColor;
 extern const SkColor kPublicAccountUserCardNameColor;
 
diff --git a/ash/system/tray/tray_details_view.cc b/ash/system/tray/tray_details_view.cc
index fee3c72..4098c86 100644
--- a/ash/system/tray/tray_details_view.cc
+++ b/ash/system/tray/tray_details_view.cc
@@ -21,6 +21,7 @@
 #include "ui/compositor/paint_recorder.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/skia_paint_util.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/label.h"
@@ -238,7 +239,8 @@
       tri_view_(nullptr),
       back_button_(nullptr) {
   SetLayoutManager(box_layout_);
-  set_background(views::Background::CreateSolidBackground(kBackgroundColor));
+  set_background(views::Background::CreateThemedSolidBackground(
+      this, ui::NativeTheme::kColorId_BubbleBackground));
 }
 
 TrayDetailsView::~TrayDetailsView() {}
@@ -295,8 +297,8 @@
   // Make the |scroller_| have a layer to clip |scroll_content_|'s children.
   // TODO(varkha): Make the sticky rows work with EnableViewPortLayer().
   scroller_->SetPaintToLayer();
-  scroller_->set_background(
-      views::Background::CreateSolidBackground(kBackgroundColor));
+  scroller_->set_background(views::Background::CreateThemedSolidBackground(
+      scroller_, ui::NativeTheme::kColorId_BubbleBackground));
   scroller_->layer()->SetMasksToBounds(true);
 
   AddChildView(scroller_);
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc
index d5bdd0a6..39d3200 100644
--- a/ash/system/tray/tray_popup_utils.cc
+++ b/ash/system/tray/tray_popup_utils.cc
@@ -21,6 +21,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
 #include "ui/views/animation/ink_drop_highlight.h"
 #include "ui/views/animation/ink_drop_impl.h"
@@ -226,9 +227,8 @@
   // Frequently the label will paint to a layer that's non-opaque, so subpixel
   // rendering won't work unless we explicitly set a background. See
   // crbug.com/686363
-  label->set_background(
-      views::Background::CreateSolidBackground(kBackgroundColor));
-  label->SetBackgroundColor(kBackgroundColor);
+  label->set_background(views::Background::CreateThemedSolidBackground(
+      label, ui::NativeTheme::kColorId_BubbleBackground));
   return label;
 }
 
@@ -287,8 +287,8 @@
 
 void TrayPopupUtils::ConfigureAsStickyHeader(views::View* view) {
   view->set_id(VIEW_ID_STICKY_HEADER);
-  view->set_background(
-      views::Background::CreateSolidBackground(kBackgroundColor));
+  view->set_background(views::Background::CreateThemedSolidBackground(
+      view, ui::NativeTheme::kColorId_BubbleBackground));
   view->SetBorder(
       views::CreateEmptyBorder(gfx::Insets(kMenuSeparatorVerticalPadding, 0)));
   view->SetPaintToLayer();
diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc
index bf12a2d..94f2db9 100644
--- a/ash/system/tray_accessibility.cc
+++ b/ash/system/tray_accessibility.cc
@@ -27,6 +27,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/resources/grit/ui_resources.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/custom_button.h"
@@ -130,7 +131,8 @@
     : label_(CreateLabel(enabled_state_bits)) {}
 
 void AccessibilityPopupView::Init() {
-  set_background(views::Background::CreateSolidBackground(kBackgroundColor));
+  set_background(views::Background::CreateThemedSolidBackground(
+      this, ui::NativeTheme::kColorId_BubbleBackground));
 
   views::GridLayout* layout = new views::GridLayout(this);
   SetLayoutManager(layout);
diff --git a/ash/system/user/user_card_view.cc b/ash/system/user/user_card_view.cc
index 93034cb..bd77ef93 100644
--- a/ash/system/user/user_card_view.cc
+++ b/ash/system/user/user_card_view.cc
@@ -293,7 +293,8 @@
   layout->set_cross_axis_alignment(
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
 
-  set_background(views::Background::CreateSolidBackground(kBackgroundColor));
+  set_background(views::Background::CreateThemedSolidBackground(
+      this, ui::NativeTheme::kColorId_BubbleBackground));
 
   Shell::Get()->media_controller()->AddObserver(this);
 
diff --git a/ash/system/user/user_view.cc b/ash/system/user/user_view.cc
index 7bdb4782..56a7729 100644
--- a/ash/system/user/user_view.cc
+++ b/ash/system/user/user_view.cc
@@ -37,6 +37,7 @@
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/separator.h"
@@ -73,8 +74,7 @@
 }
 
 // Creates the view shown in the user switcher popup ("AddUserMenuOption").
-views::View* CreateAddUserView(AddUserSessionPolicy policy,
-                               views::ButtonListener* listener) {
+views::View* CreateAddUserView(AddUserSessionPolicy policy) {
   auto* view = new views::View;
   const int icon_padding = (kMenuButtonSize - kMenuIconSize) / 2;
   auto* layout =
@@ -82,8 +82,8 @@
                            kTrayPopupLabelHorizontalPadding + icon_padding);
   layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight);
   view->SetLayoutManager(layout);
-  view->set_background(
-      views::Background::CreateSolidBackground(kBackgroundColor));
+  view->set_background(views::Background::CreateThemedSolidBackground(
+      view, ui::NativeTheme::kColorId_BubbleBackground));
 
   int message_id = 0;
   switch (policy) {
@@ -124,13 +124,6 @@
   view->SetBorder(views::CreateEmptyBorder(vertical_padding, icon_padding,
                                            vertical_padding,
                                            kTrayPopupLabelHorizontalPadding));
-  if (policy == AddUserSessionPolicy::ALLOWED) {
-    auto* button =
-        new ButtonFromView(view, listener, TrayPopupInkDropStyle::INSET_BOUNDS);
-    button->SetAccessibleName(
-        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SIGN_IN_ANOTHER_ACCOUNT));
-    return button;
-  }
 
   return view;
 }
@@ -228,11 +221,11 @@
 }
 
 UserView::~UserView() {
-  RemoveAddUserMenuOption();
+  HideUserDropdownWidget();
 }
 
 TrayUser::TestState UserView::GetStateForTest() const {
-  if (add_menu_option_)
+  if (user_dropdown_widget_)
     return add_user_enabled_ ? TrayUser::ACTIVE : TrayUser::ACTIVE_BUT_DISABLED;
 
   // If the container is the user card view itself, there's no ButtonFromView
@@ -257,13 +250,13 @@
 void UserView::ButtonPressed(views::Button* sender, const ui::Event& event) {
   if (sender == logout_button_) {
     ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_SIGN_OUT);
-    RemoveAddUserMenuOption();
+    HideUserDropdownWidget();
     Shell::Get()->system_tray_controller()->SignOut();
   } else if (sender == user_card_container_ &&
              IsMultiProfileSupportedAndUserActive()) {
-    ToggleAddUserMenuOption();
-  } else if (add_menu_option_ &&
-             sender->GetWidget() == add_menu_option_.get()) {
+    ToggleUserDropdownWidget();
+  } else if (user_dropdown_widget_ &&
+             sender->GetWidget() == user_dropdown_widget_.get()) {
     DCHECK_EQ(Shell::Get()->session_controller()->NumberOfLoggedInUsers(),
               sender->parent()->child_count() - 1);
     const int index_in_add_menu = sender->parent()->GetIndexOf(sender);
@@ -275,7 +268,7 @@
       const int user_index = index_in_add_menu;
       SwitchUser(user_index);
     }
-    RemoveAddUserMenuOption();
+    HideUserDropdownWidget();
     owner_->system_tray()->CloseSystemBubble();
   } else {
     NOTREACHED();
@@ -284,7 +277,7 @@
 
 void UserView::OnWillChangeFocus(View* focused_before, View* focused_now) {
   if (focused_now)
-    RemoveAddUserMenuOption();
+    HideUserDropdownWidget();
 }
 
 void UserView::OnDidChangeFocus(View* focused_before, View* focused_now) {
@@ -314,16 +307,16 @@
   AddChildViewAt(user_card_container_, 0);
 }
 
-void UserView::ToggleAddUserMenuOption() {
-  if (add_menu_option_) {
-    RemoveAddUserMenuOption();
+void UserView::ToggleUserDropdownWidget() {
+  if (user_dropdown_widget_) {
+    HideUserDropdownWidget();
     return;
   }
 
   // Note: We do not need to install a global event handler to delete this
   // item since it will destroyed automatically before the menu / user menu item
   // gets destroyed..
-  add_menu_option_.reset(new views::Widget);
+  user_dropdown_widget_.reset(new views::Widget);
   views::Widget::InitParams params;
   params.type = views::Widget::InitParams::TYPE_MENU;
   params.keep_on_top = true;
@@ -334,9 +327,9 @@
   WmWindow::Get(GetWidget()->GetNativeWindow())
       ->GetRootWindowController()
       ->ConfigureWidgetInitParamsForContainer(
-          add_menu_option_.get(), kShellWindowId_DragImageAndTooltipContainer,
-          &params);
-  add_menu_option_->Init(params);
+          user_dropdown_widget_.get(),
+          kShellWindowId_DragImageAndTooltipContainer, &params);
+  user_dropdown_widget_->Init(params);
 
   const SessionController* const session_controller =
       Shell::Get()->session_controller();
@@ -352,48 +345,63 @@
   int row_height = bounds.height();
 
   views::View* container = new UserDropdownWidgetContents(
-      base::Bind(&UserView::RemoveAddUserMenuOption, base::Unretained(this)));
+      base::Bind(&UserView::HideUserDropdownWidget, base::Unretained(this)));
+  views::View* add_user_view = CreateAddUserView(add_user_policy);
+  const SkColor bg_color = add_user_view->background()->get_color();
   container->SetBorder(views::CreatePaddedBorder(
-      views::CreateSolidSidedBorder(0, 0, 0, kSeparatorWidth, kBackgroundColor),
+      views::CreateSolidSidedBorder(0, 0, 0, kSeparatorWidth, bg_color),
       gfx::Insets(row_height, 0, 0, 0)));
+
+  // Create the contents aside from the empty window through which the active
+  // user is seen.
   views::View* user_dropdown_padding = new views::View();
   user_dropdown_padding->SetBorder(views::CreateSolidSidedBorder(
-      kMenuSeparatorVerticalPadding - kSeparatorWidth, 0, 0, 0,
-      kBackgroundColor));
+      kMenuSeparatorVerticalPadding - kSeparatorWidth, 0, 0, 0, bg_color));
   user_dropdown_padding->SetLayoutManager(
       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
   views::Separator* separator = new views::Separator();
   separator->SetPreferredHeight(kSeparatorWidth);
-  separator->SetColor(color_utils::GetResultingPaintColor(kMenuSeparatorColor,
-                                                          kBackgroundColor));
+  separator->SetColor(
+      color_utils::GetResultingPaintColor(kMenuSeparatorColor, bg_color));
   const int separator_horizontal_padding =
       (kTrayPopupItemMinStartWidth - kTrayItemSize) / 2;
-  separator->SetBorder(views::CreateSolidSidedBorder(
-      0, separator_horizontal_padding, 0, separator_horizontal_padding,
-      kBackgroundColor));
+  separator->SetBorder(
+      views::CreateSolidSidedBorder(0, separator_horizontal_padding, 0,
+                                    separator_horizontal_padding, bg_color));
   user_dropdown_padding->AddChildView(separator);
 
+  // Add other logged in users.
   for (int i = 1; i < session_controller->NumberOfLoggedInUsers(); ++i) {
     user_dropdown_padding->AddChildView(
         new ButtonFromView(new UserCardView(LoginStatus::USER, i), this,
                            TrayPopupInkDropStyle::INSET_BOUNDS));
   }
 
-  user_dropdown_padding->AddChildView(CreateAddUserView(add_user_policy, this));
+  // Add the "add user" option or the "can't add another user" message.
+  if (add_user_enabled_) {
+    auto* button = new ButtonFromView(add_user_view, this,
+                                      TrayPopupInkDropStyle::INSET_BOUNDS);
+    button->SetAccessibleName(
+        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SIGN_IN_ANOTHER_ACCOUNT));
+    user_dropdown_padding->AddChildView(button);
+  } else {
+    user_dropdown_padding->AddChildView(add_user_view);
+  }
+
   container->AddChildView(user_dropdown_padding);
   container->SetLayoutManager(new views::FillLayout());
-  add_menu_option_->SetContentsView(container);
+  user_dropdown_widget_->SetContentsView(container);
 
   bounds.set_height(container->GetPreferredSize().height());
-  add_menu_option_->SetBounds(bounds);
+  user_dropdown_widget_->SetBounds(bounds);
 
   // Suppress the appearance of the collective capture icon while the dropdown
   // is open (the icon will appear in the specific user rows).
   user_card_view_->SetSuppressCaptureIcon(true);
 
   // Show the content.
-  add_menu_option_->SetAlwaysOnTop(true);
-  add_menu_option_->Show();
+  user_dropdown_widget_->SetAlwaysOnTop(true);
+  user_dropdown_widget_->Show();
 
   // Install a listener to focus changes so that we can remove the card when
   // the focus gets changed. When called through the destruction of the bubble,
@@ -402,15 +410,15 @@
   focus_manager_->AddFocusChangeListener(this);
 }
 
-void UserView::RemoveAddUserMenuOption() {
-  if (!add_menu_option_)
+void UserView::HideUserDropdownWidget() {
+  if (!user_dropdown_widget_)
     return;
   focus_manager_->RemoveFocusChangeListener(this);
   focus_manager_ = nullptr;
   if (user_card_container_->GetFocusManager())
     user_card_container_->GetFocusManager()->ClearFocus();
   user_card_view_->SetSuppressCaptureIcon(false);
-  add_menu_option_.reset();
+  user_dropdown_widget_.reset();
 }
 
 }  // namespace tray
diff --git a/ash/system/user/user_view.h b/ash/system/user/user_view.h
index 535a4b5..de498772 100644
--- a/ash/system/user/user_view.h
+++ b/ash/system/user/user_view.h
@@ -62,10 +62,10 @@
 
   // Create the menu option to add another user. If |disabled| is set the user
   // cannot actively click on the item.
-  void ToggleAddUserMenuOption();
+  void ToggleUserDropdownWidget();
 
   // Removes the add user menu option.
-  void RemoveAddUserMenuOption();
+  void HideUserDropdownWidget();
 
   // If |user_card_view_| is clickable, this is a ButtonFromView that wraps it.
   // If |user_card_view_| is not clickable, this will be equal to
@@ -79,7 +79,7 @@
   SystemTrayItem* owner_;
 
   views::View* logout_button_ = nullptr;
-  std::unique_ptr<views::Widget> add_menu_option_;
+  std::unique_ptr<views::Widget> user_dropdown_widget_;
 
   // False when the add user panel is visible but not activatable.
   bool add_user_enabled_ = true;
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index e94aab1..090e98d8 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -342,7 +342,6 @@
       "//components/policy:generated",
       "//content/app/resources",
       "//crypto",
-      "//headless:headless_shell_browser_lib",
       "//net:net_resources",
       "//ppapi/features",
       "//third_party/cld",
@@ -433,7 +432,6 @@
         "//components/browser_watcher:browser_watcher_client",
         "//components/crash/content/app",
         "//content/public/app:child",
-        "//headless:headless_shell_child_lib",
       ]
 
       ldflags = [
diff --git a/chrome/VERSION b/chrome/VERSION
index fb1e6e7..8af47d7 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=60
 MINOR=0
-BUILD=3080
+BUILD=3081
 PATCH=0
diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc
index 3ac247f..2cc37702 100644
--- a/chrome/app/chrome_main.cc
+++ b/chrome/app/chrome_main.cc
@@ -89,20 +89,22 @@
 #else
   params.argc = argc;
   params.argv = argv;
+#endif
+
+#if !defined(OS_WIN)
   base::CommandLine::Init(params.argc, params.argv);
-#endif  // defined(OS_WIN)
-  base::CommandLine::Init(0, nullptr);
   const base::CommandLine* command_line(base::CommandLine::ForCurrentProcess());
   ALLOW_UNUSED_LOCAL(command_line);
+#endif
 
-#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
+#if defined(OS_LINUX) || defined(OS_MACOSX)
   if (command_line->HasSwitch(switches::kHeadless)) {
 #if defined(OS_MACOSX)
     SetUpBundleOverrides();
 #endif
-    return headless::HeadlessShellMain(params);
+    return headless::HeadlessShellMain(argc, argv);
   }
-#endif  // defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
+#endif  // defined(OS_LINUX) || defined(OS_MACOSX)
 
 #if defined(OS_CHROMEOS) && BUILDFLAG(ENABLE_PACKAGE_MASH_SERVICES)
   version_info::Channel channel = chrome::GetChannel();
diff --git a/chrome/browser/android/offline_pages/request_coordinator_factory.cc b/chrome/browser/android/offline_pages/request_coordinator_factory.cc
index 2384675..d4bdbb4 100644
--- a/chrome/browser/android/offline_pages/request_coordinator_factory.cc
+++ b/chrome/browser/android/offline_pages/request_coordinator_factory.cc
@@ -9,7 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
 #include "base/sequenced_task_runner.h"
-#include "base/threading/sequenced_worker_pool.h"
+#include "base/task_scheduler/post_task.h"
 #include "chrome/browser/android/offline_pages/background_loader_offliner.h"
 #include "chrome/browser/android/offline_pages/background_scheduler_bridge.h"
 #include "chrome/browser/android/offline_pages/downloads/offline_page_notification_bridge.h"
@@ -29,7 +29,6 @@
 #include "components/offline_pages/core/background/scheduler.h"
 #include "components/offline_pages/core/downloads/download_notifying_observer.h"
 #include "components/offline_pages/core/offline_page_feature.h"
-#include "content/public/browser/browser_thread.h"
 #include "net/nqe/network_quality_estimator.h"
 
 namespace offline_pages {
@@ -68,8 +67,9 @@
   }
 
   scoped_refptr<base::SequencedTaskRunner> background_task_runner =
-      content::BrowserThread::GetBlockingPool()->GetSequencedTaskRunner(
-          content::BrowserThread::GetBlockingPool()->GetSequenceToken());
+      base::CreateSequencedTaskRunnerWithTraits(
+          base::TaskTraits().MayBlock().WithPriority(
+              base::TaskPriority::BACKGROUND));
   Profile* profile = Profile::FromBrowserContext(context);
   base::FilePath queue_store_path =
       profile->GetPath().Append(chrome::kOfflinePageRequestQueueDirname);
diff --git a/chrome/browser/chromeos/login/supervised/supervised_user_creation_controller_new.cc b/chrome/browser/chromeos/login/supervised/supervised_user_creation_controller_new.cc
index f3a95676..5bbb9309 100644
--- a/chrome/browser/chromeos/login/supervised/supervised_user_creation_controller_new.cc
+++ b/chrome/browser/chromeos/login/supervised/supervised_user_creation_controller_new.cc
@@ -13,7 +13,7 @@
 #include "base/strings/string_util.h"
 #include "base/sys_info.h"
 #include "base/task_runner_util.h"
-#include "base/threading/sequenced_worker_pool.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h"
 #include "chrome/browser/chromeos/login/supervised/supervised_user_constants.h"
@@ -31,7 +31,6 @@
 #include "components/signin/core/account_id/account_id.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
-#include "content/public/browser/browser_thread.h"
 #include "crypto/random.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 
@@ -348,9 +347,12 @@
     creation_context_->token = token;
 
     PostTaskAndReplyWithResult(
-        content::BrowserThread::GetBlockingPool()
-            ->GetTaskRunnerWithShutdownBehavior(
-                  base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)
+        base::CreateTaskRunnerWithTraits(
+            base::TaskTraits()
+                .MayBlock()
+                .WithPriority(base::TaskPriority::BACKGROUND)
+                .WithShutdownBehavior(
+                    base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN))
             .get(),
         FROM_HERE,
         base::Bind(&StoreSupervisedUserFiles, creation_context_->token,
diff --git a/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc b/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
index baf1eeb9..950731d 100644
--- a/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
+++ b/chrome/browser/chromeos/login/users/supervised_user_manager_impl.cc
@@ -10,7 +10,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/threading/sequenced_worker_pool.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/values.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h"
@@ -516,9 +516,12 @@
   base::FilePath profile_dir = ProfileHelper::GetProfilePathByUserIdHash(
       ProfileHelper::Get()->GetUserByProfile(profile)->username_hash());
   PostTaskAndReplyWithResult(
-      content::BrowserThread::GetBlockingPool()
-          ->GetTaskRunnerWithShutdownBehavior(
-                base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)
+      base::CreateTaskRunnerWithTraits(
+          base::TaskTraits()
+              .MayBlock()
+              .WithPriority(base::TaskPriority::BACKGROUND)
+              .WithShutdownBehavior(
+                  base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN))
           .get(),
       FROM_HERE, base::Bind(&LoadSyncToken, profile_dir), callback);
 }
diff --git a/chrome/browser/component_updater/subresource_filter_component_installer.cc b/chrome/browser/component_updater/subresource_filter_component_installer.cc
index 0b3ff98..a7635db 100644
--- a/chrome/browser/component_updater/subresource_filter_component_installer.cc
+++ b/chrome/browser/component_updater/subresource_filter_component_installer.cc
@@ -112,8 +112,9 @@
 
 // static
 std::string SubresourceFilterComponentInstallerTraits::GetInstallerTag() {
-  std::string ruleset_flavor =
-      subresource_filter::GetActiveConfiguration().ruleset_flavor;
+  std::string ruleset_flavor = subresource_filter::GetActiveConfigurations()
+                                   ->the_one_and_only()
+                                   .ruleset_flavor;
   if (ruleset_flavor.empty())
     return ruleset_flavor;
 
diff --git a/chrome/browser/resources/settings/certificate_manager_page/certificate_password_decryption_dialog.html b/chrome/browser/resources/settings/certificate_manager_page/certificate_password_decryption_dialog.html
index 4154a5c..0dc89c8 100644
--- a/chrome/browser/resources/settings/certificate_manager_page/certificate_password_decryption_dialog.html
+++ b/chrome/browser/resources/settings/certificate_manager_page/certificate_password_decryption_dialog.html
@@ -20,8 +20,7 @@
         <paper-button class="cancel-button" on-tap="onCancelTap_">
           $i18n{cancel}
         </paper-button>
-        <paper-button id="ok" class="action-button" on-tap="onOkTap_"
-            disabled="[[!password_.length]]">
+        <paper-button id="ok" class="action-button" on-tap="onOkTap_">
           $i18n{ok}
         </paper-button>
       </div>
diff --git a/chrome/browser/resources/settings/search_page/search_page.html b/chrome/browser/resources/settings/search_page/search_page.html
index c05c572..f19514a3 100644
--- a/chrome/browser/resources/settings/search_page/search_page.html
+++ b/chrome/browser/resources/settings/search_page/search_page.html
@@ -80,7 +80,7 @@
 
         <template is="dom-if" if="[[hotwordInfo_.allowed]]">
           <!-- Hotword (OK Google) -->
-          <div class="settings-box two-line continuation">
+          <div class="settings-box two-line continuation indented">
             <settings-toggle-button id="hotwordSearchEnable" class="start"
                 pref="{{hotwordSearchEnablePref_}}"
                 label="$i18n{searchOkGoogleLabel}"
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html
index 8e1c38e..5204d006 100644
--- a/chrome/browser/resources/settings/settings_shared_css.html
+++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -334,7 +334,11 @@
         align-items: center;
         display: flex;
         flex-shrink: 0;
-        height: var(--settings-row-min-height);
+        height: 36px;
+      }
+
+      :-webkit-any(.settings-box, .list-item).two-line .secondary-action {
+        height: 46px;
       }
 
       /* Helper for a list frame to automatically avoid the separator line. */
diff --git a/chrome/browser/resources/settings/site_settings/site_list.html b/chrome/browser/resources/settings/site_settings/site_list.html
index 5d84fea2..0564325 100644
--- a/chrome/browser/resources/settings/site_settings/site_list.html
+++ b/chrome/browser/resources/settings/site_settings/site_list.html
@@ -18,11 +18,6 @@
 <dom-module id="site-list">
   <template>
     <style include="settings-shared">
-      paper-icon-button {
-        left: 8px;
-        right: 8px;
-      }
-
       .selectable {
         -webkit-user-select: text;
       }
@@ -66,17 +61,30 @@
       </div>
       <div class="list-frame menu-content vertical-list" id="listContainer">
         <template is="dom-repeat" items="[[sites]]">
-          <div class="list-item" actionable$="[[enableSiteSettings_]]"
-              on-tap="onOriginTap_">
-            <div class="favicon-image"
-                style$="[[computeSiteIcon(item.origin)]]">
-            </div>
-            <div class="middle no-min-width">
-              <div class="selectable text-elide">[[item.displayName]]</div>
+          <div class="list-item">
+            <div class="layout horizontal center flex"
+                actionable$="[[enableSiteSettings_]]" on-tap="onOriginTap_">
+              <div class="favicon-image"
+                  style$="[[computeSiteIcon(item.origin)]]">
+              </div>
+              <div class="middle no-min-width">
+                <div class="selectable text-elide">[[item.displayName]]</div>
 
-              <!-- This div must not contain extra whitespace. -->
-              <div class="selectable secondary text-elide" id="siteDescription"
-                  >[[computeSiteDescription_(item)]]</div>
+                <!-- This div must not contain extra whitespace. -->
+                <div class="selectable secondary text-elide"
+                    id="siteDescription">[[computeSiteDescription_(item)]]</div>
+              </div>
+              <template is="dom-if" if="[[enableSiteSettings_]]">
+                <div on-tap="onOriginTap_" actionable>
+                  <button class="subpage-arrow" is="paper-icon-button-light"
+                      aria-label$="[[item.displayName]]"
+                      aria-describedby="siteDescription"></button>
+                </div>
+                <!-- This div is intentionally empty. It creates a vertical grey
+                     bar. This can be merged into the html that follows this
+                     template after the |enableSiteSettings_| is removed. -->
+                <div class="secondary-action"></div>
+              </template>
             </div>
             <template is="dom-if" if="[[item.controlledBy]]">
               <cr-policy-pref-indicator pref="[[item]]"
@@ -87,19 +95,14 @@
             <paper-icon-button id="resetSite" icon="cr:delete"
                 hidden="[[isResetButtonHidden_(
                     item.enforcement, readOnlyList)]]"
-                on-tap="onResetButtonTap_" alt="$i18n{siteSettingsActionReset}">
+                on-tap="onResetButtonTap_"
+                alt="$i18n{siteSettingsActionReset}">
             </paper-icon-button>
             <paper-icon-button id="actionMenuButton" icon="cr:more-vert"
-                hidden="[[isActionMenuHidden_(item.enforcement, readOnlyList)]]"
+                hidden=
+                    "[[isActionMenuHidden_(item.enforcement, readOnlyList)]]"
                 on-tap="onShowActionMenuTap_" title="$i18n{moreActions}">
             </paper-icon-button>
-            <template is="dom-if" if="[[enableSiteSettings_]]">
-              <div on-tap="onOriginTap_" actionable>
-                <button class="subpage-arrow" is="paper-icon-button-light"
-                    aria-label$="[[item.displayName]]"
-                    aria-describedby="siteDescription"></button>
-              </div>
-            </template>
           </div>
         </template>
       </div>
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index d21988ce..ab5ca80 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -60,7 +60,6 @@
 #include "components/safe_browsing_db/v4_feature_list.h"
 #include "components/safe_browsing_db/v4_get_hash_protocol_manager.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features_test_support.h"
 #include "components/subresource_filter/core/common/test_ruleset_creator.h"
@@ -930,10 +929,6 @@
 
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  auto* driver_factory = subresource_filter::
-      ContentSubresourceFilterDriverFactory::FromWebContents(web_contents);
-  driver_factory->set_configuration_for_testing(
-      subresource_filter::GetActiveConfiguration());
 
   // Navigation to a phishing page should trigger an interstitial. If the user
   // clicks through it, the page load should proceed, but with subresource
@@ -2004,10 +1999,6 @@
 
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  auto* driver_factory = subresource_filter::
-      ContentSubresourceFilterDriverFactory::FromWebContents(web_contents);
-  driver_factory->set_configuration_for_testing(
-      subresource_filter::GetActiveConfiguration());
 
   // Navigation to a phishing page should trigger an interstitial. If the user
   // clicks through it, the page load should proceed, but with subresource
diff --git a/chrome/browser/site_per_process_interactive_browsertest.cc b/chrome/browser/site_per_process_interactive_browsertest.cc
index d3598fa..15a7953 100644
--- a/chrome/browser/site_per_process_interactive_browsertest.cc
+++ b/chrome/browser/site_per_process_interactive_browsertest.cc
@@ -327,7 +327,7 @@
   EXPECT_EQ(main_frame, web_contents->GetFocusedFrame());
 }
 
-#if (defined(OS_LINUX) && !defined(USE_OZONE))
+#if (defined(OS_LINUX) && !defined(USE_OZONE)) || defined(OS_WIN)
 // Ensures that renderers know to advance focus to sibling frames and parent
 // frames in the presence of mouse click initiated focus changes.
 // Verifies against regression of https://crbug.com/702330
@@ -367,6 +367,7 @@
   std::string script =
       "function onFocus(e) {"
       "  domAutomationController.setAutomationId(0);"
+      "  console.log(window.name + '-focused-' + e.target.id);"
       "  domAutomationController.send(window.name + '-focused-' + e.target.id);"
       "}"
       ""
@@ -380,7 +381,13 @@
       "  return Math.floor(rect.left) +','+"
       "         Math.floor(rect.top);"
       "}"
+      "function onClick(e) {"
+      " console.log('Click event ' + window.name + ' at: ' + e.x + ', ' + e.y "
+      "             + ' screen: ' + e.screenX + ', ' + e.screenY);"
+      "}"
       ""
+      "window.addEventListener('click', onClick);"
+      "console.log(document.location.origin);"
       "document.styleSheets[0].insertRule('input {width:100%;margin:0;}', 1);"
       "document.styleSheets[0].insertRule('h2 {margin:0;}', 1);"
       "var input1 = document.createElement('input');"
@@ -440,6 +447,7 @@
     std::string reply;
     SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
                      ui::VKEY_TAB, false, reverse /* shift */, false, false);
+    LOG(INFO) << "Press tab";
     EXPECT_TRUE(msg_queue.WaitForMessage(&reply));
     return reply;
   };
@@ -454,6 +462,7 @@
         ui_controls::SendMouseClick(ui_controls::LEFT);
 
         std::string reply;
+        LOG(INFO) << "Click element";
         EXPECT_TRUE(msg_queue.WaitForMessage(&reply));
         return reply;
       };
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index 9aa2ddb..6b9c9ac 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -381,9 +381,6 @@
   void ToggleFeatures(
       std::unique_ptr<ScopedSubresourceFilterFeatureToggle> features) {
     scoped_feature_toggle_ = std::move(features);
-    ContentSubresourceFilterDriverFactory* driver_factory =
-        ContentSubresourceFilterDriverFactory::FromWebContents(web_contents());
-    driver_factory->set_configuration_for_testing(GetActiveConfiguration());
   }
 
  private:
diff --git a/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc b/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc
index addc3e0e..6970f7d 100644
--- a/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc
+++ b/chrome/browser/ui/views/autofill/password_generation_popup_view_views.cc
@@ -155,9 +155,8 @@
       PasswordGenerationPopupController::kHorizontalPadding));
   AddChildView(help_label_);
 
-  set_background(views::Background::CreateSolidBackground(
-      GetNativeTheme()->GetSystemColor(
-          ui::NativeTheme::kColorId_ResultsTableNormalBackground)));
+  set_background(views::Background::CreateThemedSolidBackground(
+      this, ui::NativeTheme::kColorId_ResultsTableNormalBackground));
 }
 
 PasswordGenerationPopupViewViews::~PasswordGenerationPopupViewViews() {}
@@ -212,12 +211,11 @@
   if (controller_->password_selected())
     NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true);
 
-  password_view_->set_background(
-      views::Background::CreateSolidBackground(
-          GetNativeTheme()->GetSystemColor(
-              controller_->password_selected() ?
-                  ui::NativeTheme::kColorId_ResultsTableHoveredBackground :
-                  ui::NativeTheme::kColorId_ResultsTableNormalBackground)));
+  password_view_->set_background(views::Background::CreateThemedSolidBackground(
+      password_view_,
+      controller_->password_selected()
+          ? ui::NativeTheme::kColorId_ResultsTableHoveredBackground
+          : ui::NativeTheme::kColorId_ResultsTableNormalBackground));
 }
 
 void PasswordGenerationPopupViewViews::Layout() {
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc
index 926d73a7..aeaa2188 100644
--- a/chrome/browser/ui/views/sad_tab_view.cc
+++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -36,10 +36,8 @@
 SadTabView::SadTabView(content::WebContents* web_contents,
                        chrome::SadTabKind kind)
     : SadTab(web_contents, kind) {
-  // Set the background color.
-  set_background(
-      views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
-          ui::NativeTheme::kColorId_DialogBackground)));
+  set_background(views::Background::CreateThemedSolidBackground(
+      this, ui::NativeTheme::kColorId_DialogBackground));
 
   views::GridLayout* layout = new views::GridLayout(this);
   SetLayoutManager(layout);
@@ -61,7 +59,7 @@
   layout->StartRow(0, column_set_id);
   layout->AddView(image, 2, 1);
 
-  title_ = CreateLabel(l10n_util::GetStringUTF16(GetTitle()));
+  title_ = new views::Label(l10n_util::GetStringUTF16(GetTitle()));
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   title_->SetFontList(rb.GetFontList(ui::ResourceBundle::LargeFont));
   title_->SetMultiLine(true);
@@ -70,13 +68,10 @@
                               views::kPanelVerticalSpacing);
   layout->AddView(title_, 2, 1);
 
-  const SkColor text_color = GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_LabelDisabledColor);
-
-  message_ = CreateLabel(l10n_util::GetStringUTF16(GetMessage()));
+  message_ = new views::Label(l10n_util::GetStringUTF16(GetMessage()));
 
   message_->SetMultiLine(true);
-  message_->SetEnabledColor(text_color);
+  message_->SetEnabled(false);
   message_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   message_->SetLineHeight(views::kPanelSubVerticalSpacing);
 
@@ -86,8 +81,8 @@
 
   action_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
       this, l10n_util::GetStringUTF16(GetButtonTitle()));
-  help_link_ =
-      CreateLink(l10n_util::GetStringUTF16(GetHelpLinkTitle()), text_color);
+  help_link_ = new views::Link(l10n_util::GetStringUTF16(GetHelpLinkTitle()));
+  help_link_->set_listener(this);
   layout->StartRowWithPadding(0, column_set_id, 0,
                               views::kPanelVerticalSpacing);
   layout->AddView(help_link_, 1, 1, views::GridLayout::LEADING,
@@ -150,21 +145,6 @@
   View::OnPaint(canvas);
 }
 
-views::Label* SadTabView::CreateLabel(const base::string16& text) {
-  views::Label* label = new views::Label(text);
-  label->SetBackgroundColor(background()->get_color());
-  return label;
-}
-
-views::Link* SadTabView::CreateLink(const base::string16& text,
-                                    const SkColor& color) {
-  views::Link* link = new views::Link(text);
-  link->SetBackgroundColor(background()->get_color());
-  link->SetEnabledColor(color);
-  link->set_listener(this);
-  return link;
-}
-
 namespace chrome {
 
 SadTab* SadTab::Create(content::WebContents* web_contents,
diff --git a/chrome/browser/ui/views/sad_tab_view.h b/chrome/browser/ui/views/sad_tab_view.h
index 2c18287..01885e8a 100644
--- a/chrome/browser/ui/views/sad_tab_view.h
+++ b/chrome/browser/ui/views/sad_tab_view.h
@@ -51,10 +51,6 @@
   void OnPaint(gfx::Canvas* canvas) override;
 
  private:
-
-  views::Label* CreateLabel(const base::string16& text);
-  views::Link* CreateLink(const base::string16& text, const SkColor& color);
-
   bool painted_ = false;
   views::Label* message_;
   views::Link* help_link_;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc
index 17b5979..359d821 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -177,9 +177,7 @@
 
   View::ConvertPointToScreen(this, &menu_position);
 
-#if defined(OS_WIN)
-  int left_bound = GetSystemMetrics(SM_XVIRTUALSCREEN);
-#elif defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS)
   // A window won't overlap between displays on ChromeOS.
   // Use the left bound of the display on which
   // the menu button exists.
diff --git a/chrome/test/data/webui/settings/certificate_manager_page_test.js b/chrome/test/data/webui/settings/certificate_manager_page_test.js
index 8002bf57..d5838951 100644
--- a/chrome/test/data/webui/settings/certificate_manager_page_test.js
+++ b/chrome/test/data/webui/settings/certificate_manager_page_test.js
@@ -440,14 +440,13 @@
         var passwordInputElement =
             Polymer.dom(dialog.$.dialog).querySelector('paper-input');
         assertTrue(dialog.$.dialog.open);
-        assertTrue(dialog.$.ok.disabled);
 
-        // Test that the 'OK' button is disabled when the password field is
+        // Test that the 'OK' button is enabled even when the password field is
         // empty.
-        triggerInputEvent(passwordInputElement);
-        assertTrue(dialog.$.ok.disabled);
+        assertEquals('', passwordInputElement.value);
+        assertFalse(dialog.$.ok.disabled);
+
         passwordInputElement.value = 'foopassword';
-        triggerInputEvent(passwordInputElement);
         assertFalse(dialog.$.ok.disabled);
 
         // Simulate clicking 'OK'.
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 8154535..596c013 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -439,6 +439,8 @@
     "settings/timezone_settings.h",
     "settings/timezone_settings_helper.cc",
     "settings/timezone_settings_helper.h",
+    "system/cpu_temperature_reader.cc",
+    "system/cpu_temperature_reader.h",
     "system/devicemode.cc",
     "system/devicemode.h",
     "system/devicetype.cc",
@@ -677,6 +679,7 @@
     "process_proxy/process_output_watcher_unittest.cc",
     "process_proxy/process_proxy_unittest.cc",
     "settings/timezone_settings_unittest.cc",
+    "system/cpu_temperature_reader_unittest.cc",
     "system/name_value_pairs_parser_unittest.cc",
     "system/version_loader_unittest.cc",
     "timezone/timezone_unittest.cc",
diff --git a/chromeos/system/cpu_temperature_reader.cc b/chromeos/system/cpu_temperature_reader.cc
new file mode 100644
index 0000000..8a3e5c0
--- /dev/null
+++ b/chromeos/system/cpu_temperature_reader.cc
@@ -0,0 +1,117 @@
+// 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 "chromeos/system/cpu_temperature_reader.h"
+
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
+
+namespace chromeos {
+namespace system {
+
+using CPUTemperatureInfo = CPUTemperatureReader::CPUTemperatureInfo;
+
+namespace {
+
+// The location we read our CPU temperature and channel label from.
+constexpr char kDefaultHwmonDir[] = "/sys/class/hwmon/";
+constexpr char kDeviceDir[] = "device";
+constexpr char kHwmonDirectoryPattern[] = "hwmon*";
+constexpr char kCPUTempFilePattern[] = "temp*_input";
+
+// The contents of sysfs files might contain a newline at the end. Use this
+// function to read from a sysfs file and remove the newline.
+bool ReadFileContentsAndTrimWhitespace(const base::FilePath& path,
+                                       std::string* contents_out) {
+  if (!base::ReadFileToString(path, contents_out))
+    return false;
+  base::TrimWhitespaceASCII(*contents_out, base::TRIM_TRAILING, contents_out);
+  return true;
+}
+
+// Reads the temperature as degrees Celsius from the file at |path|, which is
+// expected to contain the temperature in millidegrees Celsius. Converts the
+// value into degrees and then stores it in |*temp_celsius_out|. returns true if
+// the value was successfully read from file, parsed as an int, and converted.
+bool ReadTemperatureFromPath(const base::FilePath& path,
+                             double* temp_celsius_out) {
+  std::string temperature_string;
+  if (!ReadFileContentsAndTrimWhitespace(path, &temperature_string))
+    return false;
+  uint32_t temperature = 0;
+  if (!base::StringToUint(temperature_string, &temperature))
+    return false;
+  *temp_celsius_out = temperature / 1000.0;
+  return true;
+}
+
+// Gets the label describing this temperature. Use the file "temp*_label" if it
+// is present, or fall back on the file "name" or |label_path|.
+std::string GetLabelFromPath(const base::FilePath& label_path) {
+  std::string label;
+  if (base::PathExists(label_path) &&
+      ReadFileContentsAndTrimWhitespace(base::FilePath(label_path), &label) &&
+      !label.empty()) {
+    return label;
+  }
+
+  base::FilePath name_path = label_path.DirName().Append("name");
+  if (base::PathExists(name_path) &&
+      ReadFileContentsAndTrimWhitespace(name_path, &label) && !label.empty()) {
+    return label;
+  }
+  return label_path.value();
+}
+
+}  // namespace
+
+CPUTemperatureReader::CPUTemperatureInfo::CPUTemperatureInfo() {}
+
+CPUTemperatureReader::CPUTemperatureInfo::~CPUTemperatureInfo() = default;
+
+CPUTemperatureReader::CPUTemperatureReader() : hwmon_dir_(kDefaultHwmonDir) {}
+
+CPUTemperatureReader::~CPUTemperatureReader() = default;
+
+std::vector<CPUTemperatureInfo> CPUTemperatureReader::GetCPUTemperatures() {
+  std::vector<CPUTemperatureInfo> result;
+
+  // Get directories /sys/class/hwmon/hwmon*.
+  base::FileEnumerator hwmon_enumerator(hwmon_dir_, false,
+                                        base::FileEnumerator::DIRECTORIES,
+                                        kHwmonDirectoryPattern);
+  for (base::FilePath hwmon_path = hwmon_enumerator.Next(); !hwmon_path.empty();
+       hwmon_path = hwmon_enumerator.Next()) {
+    // Get temp*_input files in hwmon*/ and hwmon*/device/.
+    if (base::PathExists(hwmon_path.Append(kDeviceDir))) {
+      hwmon_path = hwmon_path.Append(kDeviceDir);
+    }
+    base::FileEnumerator enumerator(
+        hwmon_path, false, base::FileEnumerator::FILES, kCPUTempFilePattern);
+    for (base::FilePath temperature_path = enumerator.Next();
+         !temperature_path.empty(); temperature_path = enumerator.Next()) {
+      CPUTemperatureInfo info;
+      if (!ReadTemperatureFromPath(temperature_path, &info.temp_celsius)) {
+        LOG(WARNING) << "Unable to read CPU temperature from "
+                     << temperature_path.value();
+        continue;
+      }
+      // Get appropriate temp*_label file.
+      std::string label_path = temperature_path.value();
+      base::ReplaceSubstringsAfterOffset(&label_path, 0, "input", "label");
+      info.label = GetLabelFromPath(base::FilePath(label_path));
+      result.push_back(info);
+    }
+  }
+  return result;
+}
+
+}  // namespace system
+}  // namespace chromeos
diff --git a/chromeos/system/cpu_temperature_reader.h b/chromeos/system/cpu_temperature_reader.h
new file mode 100644
index 0000000..45c77ed
--- /dev/null
+++ b/chromeos/system/cpu_temperature_reader.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 CHROMEOS_SYSTEM_CPU_TEMPERATURE_READER_H_
+#define CHROMEOS_SYSTEM_CPU_TEMPERATURE_READER_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+
+namespace chromeos {
+namespace system {
+
+// Used to read CPU temperature info from sysfs hwmon.
+class CPUTemperatureReader {
+ public:
+  // Contains info from a CPU temperature sensor.
+  struct CPUTemperatureInfo {
+    CPUTemperatureInfo();
+    ~CPUTemperatureInfo();
+
+    bool operator<(const CPUTemperatureInfo& other) const {
+      return std::make_pair(label, temp_celsius) <
+             std::make_pair(other.label, other.temp_celsius);
+    }
+
+    // The temperature read by a CPU temperature sensor in degrees Celsius.
+    double temp_celsius;
+
+    // The name of the CPU temperature zone monitored by this sensor. Used to
+    // identify the source of each temperature reading. Taken from sysfs "name"
+    // or "label" field, if it exists.
+    std::string label;
+  };
+
+  CPUTemperatureReader();
+  ~CPUTemperatureReader();
+
+  void set_hwmon_dir_for_test(const base::FilePath& dir) { hwmon_dir_ = dir; }
+
+  // Reads temperature from each thermal sensor of the CPU. Returns a vector
+  // containing a reading from each sensor. This is a blocking function that
+  // should be run on a thread that allows blocking operations.
+  std::vector<CPUTemperatureInfo> GetCPUTemperatures();
+
+ private:
+  // Sysfs hwmon directory path.
+  base::FilePath hwmon_dir_;
+
+  DISALLOW_COPY_AND_ASSIGN(CPUTemperatureReader);
+};
+
+}  // namespace system
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SYSTEM_CPU_TEMPERATURE_READER_H_
diff --git a/chromeos/system/cpu_temperature_reader_unittest.cc b/chromeos/system/cpu_temperature_reader_unittest.cc
new file mode 100644
index 0000000..54c00881
--- /dev/null
+++ b/chromeos/system/cpu_temperature_reader_unittest.cc
@@ -0,0 +1,136 @@
+// 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 "chromeos/system/cpu_temperature_reader.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace system {
+
+class CPUTemperatureReaderTest : public ::testing::Test {
+ public:
+  CPUTemperatureReaderTest() {
+    CHECK(dir_.CreateUniqueTempDir());
+    hwmon_path_ = dir_.GetPath();
+    reader_.set_hwmon_dir_for_test(hwmon_path_);
+  }
+
+  ~CPUTemperatureReaderTest() override = default;
+
+ protected:
+  using CPUTemperatureInfo = CPUTemperatureReader::CPUTemperatureInfo;
+
+  // Creates a subdirectory in |hwmon_path_| with name |name|. Returns the full
+  // path of the new subdirectory.
+  base::FilePath CreateHwmonSubdir(const std::string& name) {
+    base::FilePath subdir_path = hwmon_path_.Append(name);
+    CHECK(base::CreateDirectory(subdir_path));
+    return subdir_path;
+  }
+
+  // Creates a file at |path| containing data |contents|.
+  void CreateFileWithContents(const base::FilePath& path,
+                              const std::string& contents) {
+    CHECK_EQ(WriteFile(path, contents.data(), contents.size()),
+             static_cast<int>(contents.size()));
+  }
+
+  // Creates a temporary dir to act as the hwmon directory passed to |reader_|.
+  base::ScopedTempDir dir_;
+
+  // Path of the temporary dir created by |dir_|.
+  base::FilePath hwmon_path_;
+
+  // Instance of the class under test
+  CPUTemperatureReader reader_;
+};
+
+TEST_F(CPUTemperatureReaderTest, EmptyDir) {
+  base::FilePath subdir_empty = CreateHwmonSubdir("hwmon0");
+  base::FilePath subdir_not_temp = CreateHwmonSubdir("hwmon1");
+  CreateFileWithContents(subdir_not_temp.Append("not_cpu_temp"), "garbage");
+
+  EXPECT_EQ(0U, reader_.GetCPUTemperatures().size());
+}
+
+TEST_F(CPUTemperatureReaderTest, SingleDir) {
+  base::FilePath subdir = CreateHwmonSubdir("hwmon0");
+  CreateFileWithContents(subdir.Append("temp1_input"), "10000\n");
+
+  std::vector<CPUTemperatureInfo> cpu_temp_readings =
+      reader_.GetCPUTemperatures();
+
+  ASSERT_EQ(1U, cpu_temp_readings.size());
+  EXPECT_EQ(10.0f, cpu_temp_readings[0].temp_celsius);
+  EXPECT_EQ(subdir.Append("temp1_label").value(), cpu_temp_readings[0].label);
+}
+
+TEST_F(CPUTemperatureReaderTest, SingleDirWithLabel) {
+  base::FilePath subdir = CreateHwmonSubdir("hwmon0");
+  CreateFileWithContents(subdir.Append("temp2_input"), "20000\n");
+  CreateFileWithContents(subdir.Append("temp2_label"), "t2\n");
+
+  std::vector<CPUTemperatureInfo> cpu_temp_readings =
+      reader_.GetCPUTemperatures();
+
+  ASSERT_EQ(1U, cpu_temp_readings.size());
+  EXPECT_EQ(20.0f, cpu_temp_readings[0].temp_celsius);
+  EXPECT_EQ("t2", cpu_temp_readings[0].label);
+}
+
+TEST_F(CPUTemperatureReaderTest, SingleDirWithName) {
+  base::FilePath subdir = CreateHwmonSubdir("hwmon0");
+  CreateFileWithContents(subdir.Append("temp3_input"), "30000\n");
+  CreateFileWithContents(subdir.Append("temp3_label"), "\n");
+  CreateFileWithContents(subdir.Append("name"), "t3\n");
+
+  std::vector<CPUTemperatureInfo> cpu_temp_readings =
+      reader_.GetCPUTemperatures();
+
+  ASSERT_EQ(1U, cpu_temp_readings.size());
+  EXPECT_EQ(30.0f, cpu_temp_readings[0].temp_celsius);
+  EXPECT_EQ("t3", cpu_temp_readings[0].label);
+}
+
+TEST_F(CPUTemperatureReaderTest, MultipleDirs) {
+  base::FilePath subdir0 = CreateHwmonSubdir("hwmon0");
+  CreateFileWithContents(subdir0.Append("temp1_input"), "10000\n");
+
+  base::FilePath subdir1 = CreateHwmonSubdir("hwmon1");
+  CreateFileWithContents(subdir1.Append("temp2_input"), "20000\n");
+  CreateFileWithContents(subdir1.Append("temp2_label"), "t2\n");
+
+  // This should not result in a CPU temperature reading.
+  base::FilePath subdir2 = CreateHwmonSubdir("hwmon2");
+  CreateFileWithContents(subdir2.Append("not_cpu_temp"), "garbage");
+
+  base::FilePath subdir3 = CreateHwmonSubdir("hwmon3");
+  CreateFileWithContents(subdir3.Append("temp3_input"), "30000\n");
+  CreateFileWithContents(subdir3.Append("temp3_label"), "t3\n");
+
+  std::vector<CPUTemperatureInfo> cpu_temp_readings =
+      reader_.GetCPUTemperatures();
+
+  // The order in which these directories were read is not guaranteed. Sort them
+  // first.
+  std::sort(cpu_temp_readings.begin(), cpu_temp_readings.end());
+
+  ASSERT_EQ(3U, cpu_temp_readings.size());
+  EXPECT_EQ(10.0f, cpu_temp_readings[0].temp_celsius);
+  EXPECT_EQ(subdir0.Append("temp1_label").value(), cpu_temp_readings[0].label);
+  EXPECT_EQ(20.0f, cpu_temp_readings[1].temp_celsius);
+  EXPECT_EQ("t2", cpu_temp_readings[1].label);
+  EXPECT_EQ(30.0f, cpu_temp_readings[2].temp_celsius);
+  EXPECT_EQ("t3", cpu_temp_readings[2].label);
+}
+
+}  // namespace system
+}  // namespace chromeos
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc
index 05d70c1..df3f8c1 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc
@@ -86,7 +86,6 @@
     content::WebContents* web_contents,
     std::unique_ptr<SubresourceFilterClient> client)
     : content::WebContentsObserver(web_contents),
-      configuration_(GetActiveConfiguration()),
       client_(std::move(client)),
       throttle_manager_(
           base::MakeUnique<ContentSubresourceFilterThrottleManager>(
@@ -115,10 +114,14 @@
     ComputeActivationDecisionForMainFrameNavigation(
         content::NavigationHandle* navigation_handle) const {
   const GURL& url(navigation_handle->GetURL());
-  if (configuration_.activation_level == ActivationLevel::DISABLED)
+
+  auto configurations = GetActiveConfigurations();
+  if (configurations->the_one_and_only().activation_level ==
+      ActivationLevel::DISABLED)
     return ActivationDecision::ACTIVATION_DISABLED;
 
-  if (configuration_.activation_scope == ActivationScope::NO_SITES)
+  if (configurations->the_one_and_only().activation_scope ==
+      ActivationScope::NO_SITES)
     return ActivationDecision::ACTIVATION_DISABLED;
 
   if (!url.SchemeIsHTTPOrHTTPS())
@@ -129,7 +132,7 @@
   if (client_->ShouldSuppressActivation(navigation_handle))
     return ActivationDecision::URL_WHITELISTED;
 
-  switch (configuration_.activation_scope) {
+  switch (configurations->the_one_and_only().activation_scope) {
     case ActivationScope::ALL_SITES:
       return ActivationDecision::ACTIVATED;
     case ActivationScope::ACTIVATION_LIST: {
@@ -137,10 +140,11 @@
       // AddActivationListMatch to ensure the activation list only has relevant
       // entries.
       DCHECK(url.SchemeIsHTTPOrHTTPS() ||
-             !DidURLMatchActivationList(url, configuration_.activation_list));
-      bool should_activate =
-          DidURLMatchActivationList(url, configuration_.activation_list);
-      if (configuration_.activation_list ==
+             !DidURLMatchActivationList(
+                 url, configurations->the_one_and_only().activation_list));
+      bool should_activate = DidURLMatchActivationList(
+          url, configurations->the_one_and_only().activation_list);
+      if (configurations->the_one_and_only().activation_list ==
           ActivationList::PHISHING_INTERSTITIAL) {
         // Handling special case, where activation on the phishing sites also
         // mean the activation on the sites with social engineering metadata.
@@ -185,7 +189,8 @@
 
   RecordRedirectChainMatchPattern();
 
-  if (configuration_.should_whitelist_site_on_reload &&
+  auto configurations = GetActiveConfigurations();
+  if (configurations->the_one_and_only().should_whitelist_site_on_reload &&
       NavigationIsPageReload(url, referrer, transition)) {
     // Whitelist this host for the current as well as subsequent navigations.
     client_->WhitelistInCurrentWebContents(url);
@@ -199,17 +204,19 @@
     return;
   }
 
-  activation_level_ = configuration_.activation_level;
-  measure_performance_ = activation_level_ != ActivationLevel::DISABLED &&
-                         ShouldMeasurePerformanceForPageLoad(
-                             configuration_.performance_measurement_rate);
+  activation_level_ = configurations->the_one_and_only().activation_level;
+  measure_performance_ =
+      activation_level_ != ActivationLevel::DISABLED &&
+      ShouldMeasurePerformanceForPageLoad(
+          configurations->the_one_and_only().performance_measurement_rate);
   ActivationState state = ActivationState(activation_level_);
   state.measure_performance = measure_performance_;
   throttle_manager_->NotifyPageActivationComputed(navigation_handle, state);
 }
 
 void ContentSubresourceFilterDriverFactory::OnFirstSubresourceLoadDisallowed() {
-  if (configuration_.should_suppress_notifications)
+  auto configurations = GetActiveConfigurations();
+  if (configurations->the_one_and_only().should_suppress_notifications)
     return;
 
   client_->ToggleNotificationVisibility(activation_level_ ==
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
index f6c4865..b7969d7 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
+++ b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
@@ -17,7 +17,6 @@
 #include "base/time/time.h"
 #include "components/safe_browsing_db/util.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
-#include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
@@ -120,14 +119,6 @@
     return throttle_manager_.get();
   }
 
-  // TODO(https://crbug.com/708181): Allow tests to change the configuration
-  // after construction (which happens at WebContents creation) but before a
-  // navigation start. Can be removed once the Safe Browsing navigation throttle
-  // handles all activation decisions.
-  void set_configuration_for_testing(Configuration configuration) {
-    configuration_ = std::move(configuration);
-  }
-
   SubresourceFilterClient* client() { return client_.get(); }
 
  private:
@@ -158,8 +149,6 @@
   void RecordRedirectChainMatchPatternForList(
       ActivationList activation_list) const;
 
-  Configuration configuration_;
-
   std::unique_ptr<SubresourceFilterClient> client_;
 
   std::unique_ptr<ContentSubresourceFilterThrottleManager> throttle_manager_;
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc
index 8f75b85..592d21f5 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory_unittest.cc
@@ -469,7 +469,9 @@
 
  private:
   static bool expected_measure_performance() {
-    const double rate = GetActiveConfiguration().performance_measurement_rate;
+    const double rate = GetActiveConfigurations()
+                            ->the_one_and_only()
+                            .performance_measurement_rate;
     // Note: The case when 0 < rate < 1 is not deterministic, don't test it.
     EXPECT_TRUE(rate == 0 || rate == 1);
     return rate == 1;
@@ -530,7 +532,6 @@
       base::FeatureList::OVERRIDE_DISABLE_FEATURE, kActivationLevelEnabled,
       kActivationScopeAllSites,
       kActivationListSocialEngineeringAdsInterstitial);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
   const GURL url(kExampleUrlWithParams);
   NavigateAndExpectActivation({true}, {url}, NO_REDIRECTS_HIT,
                               ActivationDecision::ACTIVATION_DISABLED);
@@ -545,7 +546,6 @@
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       kActivationScopeActivationList,
       kActivationListSocialEngineeringAdsInterstitial);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
   NavigateAndExpectActivation({false}, {GURL(kExampleUrl)}, EMPTY,
                               ActivationDecision::ACTIVATION_LIST_NOT_MATCHED);
 }
@@ -558,7 +558,6 @@
   testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       kActivationScopeAllSites);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
   EmulateInPageNavigation({false}, EMPTY, ActivationDecision::ACTIVATED);
 }
 
@@ -569,7 +568,6 @@
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       kActivationScopeActivationList,
       kActivationListSocialEngineeringAdsInterstitial);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
   EmulateInPageNavigation({true}, NO_REDIRECTS_HIT,
                           ActivationDecision::ACTIVATED);
 }
@@ -582,7 +580,6 @@
       kActivationScopeActivationList,
       kActivationListSocialEngineeringAdsInterstitial,
       "1" /* performance_measurement_rate */);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
   EmulateInPageNavigation({true}, NO_REDIRECTS_HIT,
                           ActivationDecision::ACTIVATED);
 }
@@ -592,7 +589,6 @@
   testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       kActivationScopeAllSites);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
   const GURL url(kExampleUrl);
   NavigateAndExpectActivation({false}, {url}, EMPTY,
                               ActivationDecision::ACTIVATED);
@@ -607,7 +603,6 @@
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       kActivationScopeActivationList,
       kActivationListSocialEngineeringAdsInterstitial);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
   struct RedirectRedirectChainMatchPatternTestData {
     std::vector<bool> blacklisted_urls;
     std::vector<GURL> navigation_chain;
@@ -701,8 +696,6 @@
   testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       kActivationScopeAllSites);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
-
   NavigateAndExpectActivation({false}, {GURL(kExampleUrl)}, EMPTY,
                               ActivationDecision::ACTIVATED);
   EXPECT_CALL(*client(), ToggleNotificationVisibility(true)).Times(1);
@@ -718,8 +711,6 @@
       kActivationScopeAllSites, "" /* activation_lists */,
       "" /* performance_measurement_rate */,
       "true" /* suppress_notifications */);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
-
   NavigateAndExpectActivation({false}, {GURL(kExampleUrl)}, EMPTY,
                               ActivationDecision::ACTIVATED);
   EXPECT_CALL(*client(), ToggleNotificationVisibility(::testing::_)).Times(0);
@@ -761,7 +752,6 @@
         kActivationScopeAllSites, "" /* activation_lists */,
         "" /* performance_measurement_rate */, "" /* suppress_notifications */,
         "true" /* whitelist_site_on_reload */);
-    factory()->set_configuration_for_testing(GetActiveConfiguration());
 
     NavigateAndExpectActivation(
         {false}, {GURL(kExampleUrl)},
@@ -784,7 +774,6 @@
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, test_data.activation_level,
       kActivationScopeActivationList,
       kActivationListSocialEngineeringAdsInterstitial);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
 
   const GURL url(kExampleUrlWithParams);
   NavigateAndExpectActivation({true}, {url}, NO_REDIRECTS_HIT,
@@ -792,7 +781,8 @@
   factory()->client()->WhitelistInCurrentWebContents(url);
   NavigateAndExpectActivation(
       {true}, {GURL(kExampleUrlWithParams)}, NO_REDIRECTS_HIT,
-      GetActiveConfiguration().activation_level == ActivationLevel::DISABLED
+      GetActiveConfigurations()->the_one_and_only().activation_level ==
+              ActivationLevel::DISABLED
           ? ActivationDecision::ACTIVATION_DISABLED
           : ActivationDecision::URL_WHITELISTED);
 }
@@ -806,7 +796,6 @@
   testing::ScopedSubresourceFilterFeatureToggle scoped_feature_toggle(
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       kActivationScopeActivationList, test_data.activation_list);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
 
   const GURL test_url("https://example.com/nonsoceng?q=engsocnon");
   std::vector<GURL> navigation_chain;
@@ -830,7 +819,6 @@
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       test_data.activation_scope,
       kActivationListSocialEngineeringAdsInterstitial);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
 
   const GURL test_url(kExampleUrlWithParams);
 
@@ -844,7 +832,8 @@
     NavigateAndExpectActivation(
         {test_data.url_matches_activation_list}, {GURL(kExampleUrlWithParams)},
         expected_pattern,
-        GetActiveConfiguration().activation_scope == ActivationScope::NO_SITES
+        GetActiveConfigurations()->the_one_and_only().activation_scope ==
+                ActivationScope::NO_SITES
             ? ActivationDecision::ACTIVATION_DISABLED
             : ActivationDecision::URL_WHITELISTED);
   }
@@ -860,7 +849,6 @@
       base::FeatureList::OVERRIDE_ENABLE_FEATURE, kActivationLevelEnabled,
       test_data.activation_scope,
       kActivationListSocialEngineeringAdsInterstitial);
-  factory()->set_configuration_for_testing(GetActiveConfiguration());
 
   // data URLs are also not supported, but not listed here, as it's not possible
   // for a page to redirect to them after https://crbug.com/594215 is fixed.
@@ -874,7 +862,8 @@
     RedirectChainMatchPattern expected_pattern = EMPTY;
     NavigateAndExpectActivation(
         {test_data.url_matches_activation_list}, {GURL(url)}, expected_pattern,
-        GetActiveConfiguration().activation_scope == ActivationScope::NO_SITES
+        GetActiveConfigurations()->the_one_and_only().activation_scope ==
+                ActivationScope::NO_SITES
             ? ActivationDecision::ACTIVATION_DISABLED
             : ActivationDecision::UNSUPPORTED_SCHEME);
   }
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.cc b/components/subresource_filter/core/browser/subresource_filter_features.cc
index 2ad875ea..943c2e7 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -5,12 +5,15 @@
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 
 #include <string>
+#include <utility>
 
+#include "base/lazy_instance.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/synchronization/lock.h"
 #include "components/variations/variations_associated_data.h"
 
 namespace subresource_filter {
@@ -76,6 +79,45 @@
   return base::LowerCaseEqualsASCII(value, "true");
 }
 
+Configuration ParseFieldTrialConfiguration() {
+  Configuration configuration;
+
+  std::map<std::string, std::string> params;
+  base::GetFieldTrialParamsByFeature(kSafeBrowsingSubresourceFilter, &params);
+
+  configuration.activation_level = ParseActivationLevel(
+      TakeVariationParamOrReturnEmpty(&params, kActivationLevelParameterName));
+
+  configuration.activation_scope = ParseActivationScope(
+      TakeVariationParamOrReturnEmpty(&params, kActivationScopeParameterName));
+
+  configuration.activation_list = ParseActivationList(
+      TakeVariationParamOrReturnEmpty(&params, kActivationListsParameterName));
+
+  configuration.performance_measurement_rate =
+      ParsePerformanceMeasurementRate(TakeVariationParamOrReturnEmpty(
+          &params, kPerformanceMeasurementRateParameterName));
+
+  configuration.should_suppress_notifications =
+      ParseBool(TakeVariationParamOrReturnEmpty(
+          &params, kSuppressNotificationsParameterName));
+
+  configuration.ruleset_flavor =
+      TakeVariationParamOrReturnEmpty(&params, kRulesetFlavorParameterName);
+
+  configuration.should_whitelist_site_on_reload =
+      ParseBool(TakeVariationParamOrReturnEmpty(
+          &params, kWhitelistSiteOnReloadParameterName));
+
+  return configuration;
+}
+
+base::LazyInstance<base::Lock>::Leaky g_active_configurations_lock =
+    LAZY_INSTANCE_INITIALIZER;
+
+base::LazyInstance<scoped_refptr<ConfigurationList>>::Leaky
+    g_active_configurations = LAZY_INSTANCE_INITIALIZER;
+
 }  // namespace
 
 const base::Feature kSafeBrowsingSubresourceFilter{
@@ -115,37 +157,26 @@
 Configuration::Configuration(Configuration&&) = default;
 Configuration& Configuration::operator=(Configuration&&) = default;
 
-Configuration GetActiveConfiguration() {
-  Configuration active_configuration;
+ConfigurationList::ConfigurationList(Configuration config)
+    : config_(std::move(config)) {}
+ConfigurationList::~ConfigurationList() = default;
 
-  std::map<std::string, std::string> params;
-  base::GetFieldTrialParamsByFeature(kSafeBrowsingSubresourceFilter, &params);
-
-  active_configuration.activation_level = ParseActivationLevel(
-      TakeVariationParamOrReturnEmpty(&params, kActivationLevelParameterName));
-
-  active_configuration.activation_scope = ParseActivationScope(
-      TakeVariationParamOrReturnEmpty(&params, kActivationScopeParameterName));
-
-  active_configuration.activation_list = ParseActivationList(
-      TakeVariationParamOrReturnEmpty(&params, kActivationListsParameterName));
-
-  active_configuration.performance_measurement_rate =
-      ParsePerformanceMeasurementRate(TakeVariationParamOrReturnEmpty(
-          &params, kPerformanceMeasurementRateParameterName));
-
-  active_configuration.should_suppress_notifications =
-      ParseBool(TakeVariationParamOrReturnEmpty(
-          &params, kSuppressNotificationsParameterName));
-
-  active_configuration.ruleset_flavor =
-      TakeVariationParamOrReturnEmpty(&params, kRulesetFlavorParameterName);
-
-  active_configuration.should_whitelist_site_on_reload =
-      ParseBool(TakeVariationParamOrReturnEmpty(
-          &params, kWhitelistSiteOnReloadParameterName));
-
-  return active_configuration;
+scoped_refptr<ConfigurationList> GetActiveConfigurations() {
+  base::AutoLock lock(g_active_configurations_lock.Get());
+  if (!g_active_configurations.Get()) {
+    g_active_configurations.Get() =
+        base::MakeShared<ConfigurationList>(ParseFieldTrialConfiguration());
+  }
+  return g_active_configurations.Get();
 }
 
+namespace testing {
+
+void ClearCachedActiveConfigurations() {
+  base::AutoLock lock(g_active_configurations_lock.Get());
+  g_active_configurations.Get() = nullptr;
+}
+
+}  // namespace testing
+
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.h b/components/subresource_filter/core/browser/subresource_filter_features.h
index 9ac8d317c..487d0839 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.h
+++ b/components/subresource_filter/core/browser/subresource_filter_features.h
@@ -6,6 +6,8 @@
 #define COMPONENTS_SUBRESOURCE_FILTER_CORE_BROWSER_SUBRESOURCE_FILTER_FEATURES_H_
 
 #include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "components/subresource_filter/core/common/activation_level.h"
 #include "components/subresource_filter/core/common/activation_list.h"
 #include "components/subresource_filter/core/common/activation_scope.h"
@@ -53,8 +55,37 @@
   bool should_whitelist_site_on_reload = false;
 };
 
-// Retrieves the subresource filtering configuration to use. Expensive to call.
-Configuration GetActiveConfiguration();
+// TODO(engedy): Make this an actual list once all call sites are prepared to
+// handle multiple simultaneous configurations.
+class ConfigurationList : public base::RefCountedThreadSafe<ConfigurationList> {
+ public:
+  ConfigurationList(Configuration config);
+
+  const Configuration& the_one_and_only() const { return config_; }
+
+ private:
+  friend class base::RefCountedThreadSafe<ConfigurationList>;
+  ~ConfigurationList();
+
+  const Configuration config_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConfigurationList);
+};
+
+// Retrieves all currently enabled subresource filtering configurations. The
+// configurations are parsed on first access and then the result is cached.
+//
+// In tests, however, the config may change in-between navigations, so callers
+// should not hold on to the result for long.
+scoped_refptr<ConfigurationList> GetActiveConfigurations();
+
+namespace testing {
+
+// Clears the cached active ConfigurationList so that it will be recomputed on
+// next access. Used in tests when the variation parameters are altered.
+void ClearCachedActiveConfigurations();
+
+}  // namespace testing
 
 // Feature and variation parameter definitions -------------------------------
 
diff --git a/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc b/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
index 16b8354..5e43f17 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features_test_support.cc
@@ -65,10 +65,18 @@
   }
 
   scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
+
+  // Force the active ConfigurationList to be reparsed on next access so that
+  // the variation parameters come into effect.
+  ClearCachedActiveConfigurations();
 }
 
 ScopedSubresourceFilterFeatureToggle::~ScopedSubresourceFilterFeatureToggle() {
   variations::testing::ClearAllVariationParams();
+
+  // Force the active ConfigurationList to be reparsed on next access, so that
+  // the overrides from this instance are no longer in effect.
+  ClearCachedActiveConfigurations();
 }
 
 }  // namespace testing
diff --git a/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc b/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
index 2041868..86ba034 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
@@ -42,7 +42,9 @@
                                   : base::FeatureList::OVERRIDE_USE_DEFAULT,
         test_case.activation_level_param, kActivationScopeNoSites);
 
-    Configuration actual_configuration = GetActiveConfiguration();
+    auto active_configurations = GetActiveConfigurations();
+    const Configuration& actual_configuration =
+        active_configurations->the_one_and_only();
     EXPECT_EQ(test_case.expected_activation_level,
               actual_configuration.activation_level);
     EXPECT_EQ(ActivationScope::NO_SITES, actual_configuration.activation_scope);
@@ -79,7 +81,9 @@
                                   : base::FeatureList::OVERRIDE_USE_DEFAULT,
         kActivationLevelDisabled, test_case.activation_scope_param);
 
-    Configuration actual_configuration = GetActiveConfiguration();
+    auto active_configurations = GetActiveConfigurations();
+    const Configuration& actual_configuration =
+        active_configurations->the_one_and_only();
     EXPECT_EQ(ActivationLevel::DISABLED, actual_configuration.activation_level);
     EXPECT_EQ(test_case.expected_activation_scope,
               actual_configuration.activation_scope);
@@ -130,7 +134,9 @@
                                   : base::FeatureList::OVERRIDE_USE_DEFAULT,
         test_case.activation_level_param, test_case.activation_scope_param);
 
-    Configuration actual_configuration = GetActiveConfiguration();
+    auto active_configurations = GetActiveConfigurations();
+    const Configuration& actual_configuration =
+        active_configurations->the_one_and_only();
     EXPECT_EQ(test_case.expected_activation_level,
               actual_configuration.activation_level);
     EXPECT_EQ(test_case.expected_activation_scope,
@@ -186,7 +192,9 @@
         kActivationLevelDisabled, kActivationScopeNoSites,
         test_case.activation_list_param);
 
-    Configuration actual_configuration = GetActiveConfiguration();
+    auto active_configurations = GetActiveConfigurations();
+    const Configuration& actual_configuration =
+        active_configurations->the_one_and_only();
     EXPECT_EQ(test_case.expected_activation_list,
               actual_configuration.activation_list);
   }
@@ -223,7 +231,9 @@
         {{kPerformanceMeasurementRateParameterName,
           test_case.perf_measurement_param}});
 
-    Configuration actual_configuration = GetActiveConfiguration();
+    auto active_configurations = GetActiveConfigurations();
+    const Configuration& actual_configuration =
+        active_configurations->the_one_and_only();
     EXPECT_EQ(test_case.expected_perf_measurement_rate,
               actual_configuration.performance_measurement_rate);
   }
@@ -257,7 +267,9 @@
         {{kSuppressNotificationsParameterName,
           test_case.suppress_notifications_param}});
 
-    Configuration actual_configuration = GetActiveConfiguration();
+    auto active_configurations = GetActiveConfigurations();
+    const Configuration& actual_configuration =
+        active_configurations->the_one_and_only();
     EXPECT_EQ(test_case.expected_suppress_notifications_value,
               actual_configuration.should_suppress_notifications);
   }
@@ -291,7 +303,9 @@
         {{kWhitelistSiteOnReloadParameterName,
           test_case.whitelist_site_on_reload_param}});
 
-    Configuration actual_configuration = GetActiveConfiguration();
+    auto active_configurations = GetActiveConfigurations();
+    const Configuration& actual_configuration =
+        active_configurations->the_one_and_only();
     EXPECT_EQ(test_case.expected_whitelist_site_on_reload_value,
               actual_configuration.should_whitelist_site_on_reload);
   }
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index 7500a8f..ea0b322 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -70,7 +70,6 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_factory.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
-#include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/runner/common/client_util.h"
 
 #if defined(OS_POSIX)
@@ -451,13 +450,6 @@
     service_manager_connection_ = ServiceManagerConnection::Create(
         mojo::MakeRequest<service_manager::mojom::Service>(std::move(handle)),
         GetIOTaskRunner());
-
-    // TODO(rockot): Remove this once all child-to-browser interface connections
-    // are made via a Connector rather than directly through an
-    // InterfaceProvider, and all exposed interfaces are exposed via a
-    // ConnectionFilter.
-    service_manager_connection_->SetupInterfaceRequestProxies(
-        GetInterfaceRegistry(), nullptr);
   }
 
   sync_message_filter_ = channel_->CreateSyncMessageFilter();
@@ -637,14 +629,6 @@
   return service_manager_connection_.get();
 }
 
-service_manager::InterfaceRegistry* ChildThreadImpl::GetInterfaceRegistry() {
-  if (!interface_registry_.get()) {
-    interface_registry_ = base::MakeUnique<service_manager::InterfaceRegistry>(
-        service_manager::mojom::kServiceManager_ConnectorSpec);
-  }
-  return interface_registry_.get();
-}
-
 service_manager::Connector* ChildThreadImpl::GetConnector() {
   return service_manager_connection_->GetConnector();
 }
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h
index d2f57d5..af17dac 100644
--- a/content/child/child_thread_impl.h
+++ b/content/child/child_thread_impl.h
@@ -92,7 +92,6 @@
   void RecordAction(const base::UserMetricsAction& action) override;
   void RecordComputedAction(const std::string& action) override;
   ServiceManagerConnection* GetServiceManagerConnection() override;
-  service_manager::InterfaceRegistry* GetInterfaceRegistry() override;
   service_manager::Connector* GetConnector() override;
 
   // Returns the service_manager::ServiceInfo for the child process & the
@@ -246,7 +245,6 @@
                         const service_manager::ServiceInfo& remote_info);
 
   std::unique_ptr<mojo::edk::ScopedIPCSupport> mojo_ipc_support_;
-  std::unique_ptr<service_manager::InterfaceRegistry> interface_registry_;
   std::unique_ptr<ServiceManagerConnection> service_manager_connection_;
 
   bool connected_to_browser_ = false;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index bf9aca2..f56c199 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -367,6 +367,7 @@
     "//components/tracing:startup_tracing",
     "//content:resources",
     "//content/app/resources",
+    "//content/public/common:service_names",
     "//device/base/synchronization",
     "//device/bluetooth",
     "//gpu",
diff --git a/content/common/service_manager/service_manager_connection_impl.cc b/content/common/service_manager/service_manager_connection_impl.cc
index 9e81238a..25065a4a 100644
--- a/content/common/service_manager/service_manager_connection_impl.cc
+++ b/content/common/service_manager/service_manager_connection_impl.cc
@@ -19,9 +19,9 @@
 #include "content/common/child.mojom.h"
 #include "content/common/service_manager/embedded_service_runner.h"
 #include "content/public/common/connection_filter.h"
+#include "content/public/common/service_names.mojom.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/system/message_pipe.h"
-#include "services/service_manager/public/cpp/interface_registry.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/service_manager/public/interfaces/constants.mojom.h"
@@ -112,15 +112,6 @@
                    filter_id));
   }
 
-  // Safe to call any time before Start() is called.
-  void SetDefaultBinderForBrowserConnection(
-      const service_manager::InterfaceRegistry::Binder& binder) {
-    DCHECK(!started_);
-    default_browser_binder_ = base::Bind(
-        &IOThreadContext::CallBinderOnTaskRunner,
-        base::ThreadTaskRunnerHandle::Get(), binder);
-  }
-
   void AddEmbeddedService(const std::string& name, const ServiceInfo& info) {
     io_task_runner_->PostTask(
         FROM_HERE, base::Bind(&ServiceManagerConnectionImpl::IOThreadContext::
@@ -232,11 +223,6 @@
       connection_filters_.erase(it);
   }
 
-  void OnBrowserConnectionLost() {
-    DCHECK(io_thread_checker_.CalledOnValidThread());
-    has_browser_connection_ = false;
-  }
-
   void AddEmbeddedServiceRequestHandlerOnIoThread(const std::string& name,
                                                   const ServiceInfo& info) {
     DCHECK(io_thread_checker_.CalledOnValidThread());
@@ -276,18 +262,22 @@
                        const std::string& interface_name,
                        mojo::ScopedMessagePipeHandle interface_pipe) override {
     DCHECK(io_thread_checker_.CalledOnValidThread());
-
-    std::string remote_service = source_info.identity.name();
-    // Only expose the ServiceFactory interface to the Service Manager.
-    if (remote_service == service_manager::mojom::kServiceName &&
+    if (source_info.identity.name() == service_manager::mojom::kServiceName &&
         interface_name == service_manager::mojom::ServiceFactory::Name_) {
       factory_bindings_.AddBinding(
           this, mojo::MakeRequest<service_manager::mojom::ServiceFactory>(
                     std::move(interface_pipe)));
-      return;
-    }
+    } else if (source_info.identity.name() == mojom::kBrowserServiceName &&
+               interface_name == mojom::Child::Name_) {
+      DCHECK(!child_binding_.is_bound());
+      child_binding_.Bind(
+          mojo::MakeRequest<mojom::Child>(std::move(interface_pipe)));
 
-    {
+      InitializeCallback handler =
+          base::ResetAndReturn(&browser_info_available_callback_);
+      callback_task_runner_->PostTask(FROM_HERE,
+                                      base::Bind(handler, source_info));
+    } else {
       base::AutoLock lock(lock_);
       for (auto& entry : connection_filters_) {
         entry.second->OnBindInterface(source_info, interface_name,
@@ -298,32 +288,10 @@
           return;
       }
     }
-
-    if (remote_service == "content_browser") {
-      if (interface_name == mojom::Child::Name_ && !has_browser_connection_) {
-        has_browser_connection_ = true;
-        InitializeCallback handler =
-            base::ResetAndReturn(&browser_info_available_callback_);
-        callback_task_runner_->PostTask(FROM_HERE,
-                                        base::Bind(handler, source_info));
-
-        child_binding_.Bind(std::move(interface_pipe));
-        child_binding_.set_connection_error_handler(
-            base::Bind(&IOThreadContext::OnBrowserConnectionLost, this));
-      } else {
-        default_browser_binder_.Run(interface_name, std::move(interface_pipe));
-      }
-    }
-  }
-
-  bool OnServiceManagerConnectionLost() override {
-    ClearConnectionFiltersOnIOThread();
-    callback_task_runner_->PostTask(FROM_HERE, stop_callback_);
-    return true;
   }
 
   /////////////////////////////////////////////////////////////////////////////
-  // service_manager::mojom::ServiceFactory implementation
+  // service_manager::mojom::ServiceFactory:
 
   void CreateService(service_manager::mojom::ServiceRequest request,
                      const std::string& name) override {
@@ -334,15 +302,6 @@
     it->second.Run(std::move(request));
   }
 
-  static void CallBinderOnTaskRunner(
-      scoped_refptr<base::SequencedTaskRunner> task_runner,
-      const service_manager::InterfaceRegistry::Binder& binder,
-      const std::string& interface_name,
-      mojo::ScopedMessagePipeHandle request_handle) {
-    task_runner->PostTask(FROM_HERE, base::Bind(binder, interface_name,
-                                                base::Passed(&request_handle)));
-  }
-
   base::ThreadChecker io_thread_checker_;
   bool started_ = false;
 
@@ -364,19 +323,8 @@
   // Callback to run if the service is stopped by the service manager.
   base::Closure stop_callback_;
 
-  // Called once a connection has been received from the browser process & the
-  // default binder (below) has been set up.
-  bool has_browser_connection_ = false;
-
   service_manager::ServiceInfo local_info_;
 
-  // Default binder callback used for the browser connection's
-  // InterfaceRegistry.
-  //
-  // TODO(rockot): Remove this once all interfaces exposed to the browser are
-  // exposed via a ConnectionFilter.
-  service_manager::InterfaceRegistry::Binder default_browser_binder_;
-
   std::unique_ptr<service_manager::ServiceContext> service_context_;
   mojo::BindingSet<service_manager::mojom::ServiceFactory> factory_bindings_;
   int next_filter_id_ = kInvalidConnectionFilterId;
@@ -491,18 +439,6 @@
   connection_lost_handler_ = closure;
 }
 
-void ServiceManagerConnectionImpl::SetupInterfaceRequestProxies(
-    service_manager::InterfaceRegistry* registry,
-    service_manager::InterfaceProvider* provider) {
-  // It's safe to bind |registry| as a raw pointer because the caller must
-  // guarantee that it outlives |this|, and |this| is bound as a weak ptr here.
-  context_->SetDefaultBinderForBrowserConnection(
-      base::Bind(&ServiceManagerConnectionImpl::GetInterface,
-                 weak_factory_.GetWeakPtr(), registry));
-
-  // TODO(beng): remove provider parameter.
-}
-
 int ServiceManagerConnectionImpl::AddConnectionFilter(
     std::unique_ptr<ConnectionFilter> filter) {
   return context_->AddConnectionFilter(std::move(filter));
diff --git a/content/common/service_manager/service_manager_connection_impl.h b/content/common/service_manager/service_manager_connection_impl.h
index c950283..6ef897e 100644
--- a/content/common/service_manager/service_manager_connection_impl.h
+++ b/content/common/service_manager/service_manager_connection_impl.h
@@ -40,9 +40,6 @@
   const service_manager::ServiceInfo& GetLocalInfo() const override;
   const service_manager::ServiceInfo& GetBrowserInfo() const override;
   void SetConnectionLostClosure(const base::Closure& closure) override;
-  void SetupInterfaceRequestProxies(
-      service_manager::InterfaceRegistry* registry,
-      service_manager::InterfaceProvider* provider) override;
   int AddConnectionFilter(std::unique_ptr<ConnectionFilter> filter) override;
   void RemoveConnectionFilter(int filter_id) override;
   void AddEmbeddedService(const std::string& name,
diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
index 0012b5b..8a8cc8e 100644
--- a/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
+++ b/content/public/android/java/src/org/chromium/content/browser/SelectionPopupController.java
@@ -844,7 +844,15 @@
                 mSelectionRect.set(left, top, right, bottom);
                 mHasSelection = true;
                 mUnselectAllOnDismiss = true;
-                if (mSelectionClient == null || !mSelectionClient.sendsSelectionPopupUpdates()) {
+                // When this event comes as the result of SelectAll, SelectionClient should not
+                // change the selection range (http://crbug.com/714106). We assume that two or
+                // more selected words means SelectAll.
+                // TODO(amaralp): Find a better way to know that SELECTION_HANDLES_SHOWN was
+                // caused by SelectAll.
+                boolean oneWordSelected =
+                        !getSelectedText().isEmpty() && !getSelectedText().matches(".*\\s+.*");
+                if (!oneWordSelected || mSelectionClient == null
+                        || !mSelectionClient.sendsSelectionPopupUpdates()) {
                     showActionModeOrClearOnFailure();
                 } else {
                     // Rely on |mSelectionClient| sending a classification request and the request
diff --git a/content/public/child/child_thread.h b/content/public/child/child_thread.h
index 9287ee8a..575ee808 100644
--- a/content/public/child/child_thread.h
+++ b/content/public/child/child_thread.h
@@ -22,7 +22,6 @@
 
 namespace service_manager {
 class Connector;
-class InterfaceRegistry;
 }
 
 namespace content {
@@ -71,10 +70,6 @@
   // service_manager::Connector can be obtained).
   virtual ServiceManagerConnection* GetServiceManagerConnection() = 0;
 
-  // Returns the InterfaceRegistry that this process uses to expose interfaces
-  // to the browser.
-  virtual service_manager::InterfaceRegistry* GetInterfaceRegistry() = 0;
-
   // Returns a connector that can be used to bind interfaces exposed by other
   // services.
   virtual service_manager::Connector* GetConnector() = 0;
diff --git a/content/public/common/service_manager_connection.h b/content/public/common/service_manager_connection.h
index 5e5378cd..674cf2d 100644
--- a/content/public/common/service_manager_connection.h
+++ b/content/public/common/service_manager_connection.h
@@ -15,10 +15,7 @@
 #include "services/service_manager/public/interfaces/service.mojom.h"
 
 namespace service_manager {
-class Connection;
 class Connector;
-class InterfaceProvider;
-class InterfaceRegistry;
 }
 
 namespace content {
@@ -92,22 +89,6 @@
   // run immediately before returning from this function.
   virtual void SetConnectionLostClosure(const base::Closure& closure) = 0;
 
-  // Provides an InterfaceRegistry to forward incoming interface requests to
-  // on the ServiceManagerConnection's own thread if they aren't bound by the
-  // connection's internal InterfaceRegistry on the IO thread.
-  //
-  // Also configures |interface_provider| to forward all of its outgoing
-  // interface requests to the connection's internal remote interface provider.
-  //
-  // Note that neither |interface_registry| or |interface_provider| is owned
-  // and both MUST outlive the ServiceManagerConnection.
-  //
-  // TODO(rockot): Remove this. It's a temporary solution to avoid porting all
-  // relevant code to ConnectionFilters at once.
-  virtual void SetupInterfaceRequestProxies(
-      service_manager::InterfaceRegistry* registry,
-      service_manager::InterfaceProvider* provider) = 0;
-
   static const int kInvalidConnectionFilterId = 0;
 
   // Allows the caller to filter inbound connections and/or expose interfaces
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index 0c02a0d3..aeb37bb6 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -19,9 +19,6 @@
 #include "ipc/ipc_sync_message.h"
 #include "ipc/message_filter.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/service_manager/public/cpp/interface_provider.h"
-#include "services/service_manager/public/cpp/interface_registry.h"
-#include "services/service_manager/public/interfaces/interface_provider_spec.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/web/WebScriptController.h"
 
@@ -253,14 +250,6 @@
   return nullptr;
 }
 
-service_manager::InterfaceRegistry* MockRenderThread::GetInterfaceRegistry() {
-  if (!interface_registry_) {
-    interface_registry_ = base::MakeUnique<service_manager::InterfaceRegistry>(
-        service_manager::mojom::kServiceManager_ConnectorSpec);
-  }
-  return interface_registry_.get();
-}
-
 service_manager::Connector* MockRenderThread::GetConnector() {
   if (!connector_) {
     connector_ =
diff --git a/content/public/test/mock_render_thread.h b/content/public/test/mock_render_thread.h
index f6cca73..3a00754 100644
--- a/content/public/test/mock_render_thread.h
+++ b/content/public/test/mock_render_thread.h
@@ -17,7 +17,6 @@
 #include "ipc/ipc_test_sink.h"
 #include "ipc/message_filter.h"
 #include "services/service_manager/public/interfaces/connector.mojom.h"
-#include "services/service_manager/public/interfaces/interface_provider.mojom.h"
 #include "third_party/WebKit/public/web/WebPopupType.h"
 
 struct FrameHostMsg_CreateChildFrame_Params;
@@ -89,7 +88,6 @@
   void ReleaseCachedFonts() override;
 #endif
   ServiceManagerConnection* GetServiceManagerConnection() override;
-  service_manager::InterfaceRegistry* GetInterfaceRegistry() override;
   service_manager::Connector* GetConnector() override;
   void SetFieldTrialGroup(const std::string& trial_name,
                           const std::string& group_name) override;
@@ -167,7 +165,6 @@
   base::ObserverList<RenderThreadObserver> observers_;
 
   cc::TestSharedBitmapManager shared_bitmap_manager_;
-  std::unique_ptr<service_manager::InterfaceRegistry> interface_registry_;
   std::unique_ptr<service_manager::Connector> connector_;
   service_manager::mojom::ConnectorRequest pending_connector_request_;
 
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 31a5095..b9d73b20 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -74,8 +74,6 @@
     "sensors/sensor_manager_android_unittest.cc",
     "sensors/sensor_manager_chromeos_unittest.cc",
     "test/run_all_unittests.cc",
-    "u2f/u2f_apdu_unittest.cc",
-    "u2f/u2f_message_unittest.cc",
   ]
 
   deps = [
@@ -134,13 +132,20 @@
       "hid/test_report_descriptors.cc",
       "hid/test_report_descriptors.h",
       "serial/serial_io_handler_posix_unittest.cc",
+      "u2f/u2f_apdu_unittest.cc",
       "u2f/u2f_hid_device_unittest.cc",
+      "u2f/u2f_message_unittest.cc",
+      "u2f/u2f_register_unittest.cc",
+      "u2f/u2f_request_unittest.cc",
+      "u2f/u2f_sign_unittest.cc",
     ]
     deps += [
       "//device/hid",
       "//device/hid:mocks",
       "//device/serial",
       "//device/serial:test_support",
+      "//device/u2f",
+      "//device/u2f:mocks",
     ]
   }
 
@@ -170,7 +175,6 @@
     deps += [
       "//device/base",
       "//device/base:mocks",
-      "//device/u2f",
       "//device/usb",
       "//device/usb:test_support",
       "//device/usb/mojo",
diff --git a/device/u2f/BUILD.gn b/device/u2f/BUILD.gn
index 91fd6a2..0fcfe4b 100644
--- a/device/u2f/BUILD.gn
+++ b/device/u2f/BUILD.gn
@@ -19,6 +19,13 @@
     "u2f_message.h",
     "u2f_packet.cc",
     "u2f_packet.h",
+    "u2f_register.cc",
+    "u2f_register.h",
+    "u2f_request.cc",
+    "u2f_request.h",
+    "u2f_return_code.h",
+    "u2f_sign.cc",
+    "u2f_sign.h",
   ]
 
   deps = [
@@ -30,6 +37,21 @@
   ]
 }
 
+source_set("mocks") {
+  testonly = true
+
+  sources = [
+    "mock_u2f_device.cc",
+    "mock_u2f_device.h",
+  ]
+
+  deps = [
+    ":u2f",
+    "//base",
+    "//testing/gmock",
+  ]
+}
+
 fuzzer_test("u2f_apdu_fuzzer") {
   sources = [
     "u2f_apdu_fuzzer.cc",
diff --git a/device/u2f/mock_u2f_device.cc b/device/u2f/mock_u2f_device.cc
new file mode 100644
index 0000000..4f8bdc4
--- /dev/null
+++ b/device/u2f/mock_u2f_device.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mock_u2f_device.h"
+
+namespace device {
+
+MockU2fDevice::MockU2fDevice() {}
+
+MockU2fDevice::~MockU2fDevice() {}
+
+void MockU2fDevice::DeviceTransact(std::unique_ptr<U2fApduCommand> command,
+                                   const DeviceCallback& cb) {
+  DeviceTransactPtr(command.get(), cb);
+}
+
+// static
+void MockU2fDevice::NotSatisfied(U2fApduCommand* cmd,
+                                 const DeviceCallback& cb) {
+  cb.Run(true, base::MakeUnique<U2fApduResponse>(
+                   std::vector<uint8_t>(),
+                   U2fApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED));
+}
+
+// static
+void MockU2fDevice::WrongData(U2fApduCommand* cmd, const DeviceCallback& cb) {
+  cb.Run(true,
+         base::MakeUnique<U2fApduResponse>(
+             std::vector<uint8_t>(), U2fApduResponse::Status::SW_WRONG_DATA));
+}
+
+// static
+void MockU2fDevice::NoErrorSign(U2fApduCommand* cmd, const DeviceCallback& cb) {
+  cb.Run(true, base::MakeUnique<U2fApduResponse>(
+                   std::vector<uint8_t>({kSign}),
+                   U2fApduResponse::Status::SW_NO_ERROR));
+}
+
+// static
+void MockU2fDevice::NoErrorRegister(U2fApduCommand* cmd,
+                                    const DeviceCallback& cb) {
+  cb.Run(true, base::MakeUnique<U2fApduResponse>(
+                   std::vector<uint8_t>({kRegister}),
+                   U2fApduResponse::Status::SW_NO_ERROR));
+}
+
+// static
+void MockU2fDevice::WinkDoNothing(const WinkCallback& cb) {
+  cb.Run();
+}
+
+}  // namespace device
diff --git a/device/u2f/mock_u2f_device.h b/device/u2f/mock_u2f_device.h
new file mode 100644
index 0000000..f4e65da
--- /dev/null
+++ b/device/u2f/mock_u2f_device.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 DEVICE_U2F_MOCK_U2F_DEVICE_H_
+#define DEVICE_U2F_MOCK_U2F_DEVICE_H_
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "u2f_apdu_command.h"
+#include "u2f_device.h"
+
+namespace device {
+
+class MockU2fDevice : public U2fDevice {
+ public:
+  static constexpr uint8_t kSign = 0x1;
+  static constexpr uint8_t kRegister = 0x5;
+
+  MockU2fDevice();
+  ~MockU2fDevice() override;
+
+  MOCK_METHOD1(TryWink, void(const WinkCallback& cb));
+  MOCK_METHOD0(GetId, std::string(void));
+  // GMock cannot mock a method taking a std::unique_ptr<T>
+  MOCK_METHOD2(DeviceTransactPtr,
+               void(U2fApduCommand* command, const DeviceCallback& cb));
+  void DeviceTransact(std::unique_ptr<U2fApduCommand> command,
+                      const DeviceCallback& cb) override;
+  static void TransactNoError(std::unique_ptr<U2fApduCommand> command,
+                              const DeviceCallback& cb);
+  static void NotSatisfied(U2fApduCommand* cmd, const DeviceCallback& cb);
+  static void WrongData(U2fApduCommand* cmd, const DeviceCallback& cb);
+  static void NoErrorSign(U2fApduCommand* cmd, const DeviceCallback& cb);
+  static void NoErrorRegister(U2fApduCommand* cmd, const DeviceCallback& cb);
+  static void WinkDoNothing(const WinkCallback& cb);
+};
+
+}  // namespace device
+
+#endif  // DEVICE_U2F_MOCK_U2F_DEVICE_H_
diff --git a/device/u2f/u2f_apdu_response.h b/device/u2f/u2f_apdu_response.h
index 44bf7d6..2e5d74e 100644
--- a/device/u2f/u2f_apdu_response.h
+++ b/device/u2f/u2f_apdu_response.h
@@ -23,6 +23,7 @@
     SW_NO_ERROR = 0x9000,
     SW_CONDITIONS_NOT_SATISFIED = 0x6985,
     SW_WRONG_DATA = 0x6A80,
+    SW_WRONG_LENGTH = 0x6700,
   };
 
   U2fApduResponse(std::vector<uint8_t> message, Status response_status);
diff --git a/device/u2f/u2f_device.cc b/device/u2f/u2f_device.cc
index dc9e5ec..1b2f2b7 100644
--- a/device/u2f/u2f_device.cc
+++ b/device/u2f/u2f_device.cc
@@ -10,7 +10,8 @@
 
 namespace device {
 
-U2fDevice::U2fDevice() : weak_factory_(this) {}
+U2fDevice::U2fDevice()
+    : channel_id_(kBroadcastChannel), capabilities_(0), weak_factory_(this) {}
 
 U2fDevice::~U2fDevice() {}
 
@@ -20,7 +21,7 @@
   std::unique_ptr<U2fApduCommand> register_cmd =
       U2fApduCommand::CreateRegister(app_param, challenge_param);
   if (!register_cmd) {
-    callback.Run(ReturnCode::INVALID_PARAMS, std::vector<uint8_t>());
+    callback.Run(U2fReturnCode::INVALID_PARAMS, std::vector<uint8_t>());
     return;
   }
   DeviceTransact(std::move(register_cmd),
@@ -35,7 +36,7 @@
   std::unique_ptr<U2fApduCommand> sign_cmd =
       U2fApduCommand::CreateSign(app_param, challenge_param, key_handle);
   if (!sign_cmd) {
-    callback.Run(ReturnCode::INVALID_PARAMS, std::vector<uint8_t>());
+    callback.Run(U2fReturnCode::INVALID_PARAMS, std::vector<uint8_t>());
     return;
   }
   DeviceTransact(std::move(sign_cmd),
@@ -59,22 +60,22 @@
     bool success,
     std::unique_ptr<U2fApduResponse> register_response) {
   if (!success || !register_response) {
-    callback.Run(ReturnCode::FAILURE, std::vector<uint8_t>());
+    callback.Run(U2fReturnCode::FAILURE, std::vector<uint8_t>());
     return;
   }
   switch (register_response->status()) {
     case U2fApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
-      callback.Run(ReturnCode::CONDITIONS_NOT_SATISFIED,
+      callback.Run(U2fReturnCode::CONDITIONS_NOT_SATISFIED,
                    std::vector<uint8_t>());
       break;
     case U2fApduResponse::Status::SW_NO_ERROR:
-      callback.Run(ReturnCode::SUCCESS, register_response->data());
+      callback.Run(U2fReturnCode::SUCCESS, register_response->data());
       break;
     case U2fApduResponse::Status::SW_WRONG_DATA:
-      callback.Run(ReturnCode::INVALID_PARAMS, std::vector<uint8_t>());
+      callback.Run(U2fReturnCode::INVALID_PARAMS, std::vector<uint8_t>());
       break;
     default:
-      callback.Run(ReturnCode::FAILURE, std::vector<uint8_t>());
+      callback.Run(U2fReturnCode::FAILURE, std::vector<uint8_t>());
       break;
   }
 }
@@ -83,22 +84,21 @@
                                bool success,
                                std::unique_ptr<U2fApduResponse> sign_response) {
   if (!success || !sign_response) {
-    callback.Run(ReturnCode::FAILURE, std::vector<uint8_t>());
+    callback.Run(U2fReturnCode::FAILURE, std::vector<uint8_t>());
     return;
   }
   switch (sign_response->status()) {
     case U2fApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED:
-      callback.Run(ReturnCode::CONDITIONS_NOT_SATISFIED,
+      callback.Run(U2fReturnCode::CONDITIONS_NOT_SATISFIED,
                    std::vector<uint8_t>());
       break;
     case U2fApduResponse::Status::SW_NO_ERROR:
-      callback.Run(ReturnCode::SUCCESS, sign_response->data());
+      callback.Run(U2fReturnCode::SUCCESS, sign_response->data());
       break;
     case U2fApduResponse::Status::SW_WRONG_DATA:
-      callback.Run(ReturnCode::INVALID_PARAMS, std::vector<uint8_t>());
-      break;
+    case U2fApduResponse::Status::SW_WRONG_LENGTH:
     default:
-      callback.Run(ReturnCode::FAILURE, std::vector<uint8_t>());
+      callback.Run(U2fReturnCode::INVALID_PARAMS, std::vector<uint8_t>());
       break;
   }
 }
diff --git a/device/u2f/u2f_device.h b/device/u2f/u2f_device.h
index 06b9d2f..77c8133 100644
--- a/device/u2f/u2f_device.h
+++ b/device/u2f/u2f_device.h
@@ -10,6 +10,7 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "u2f_apdu_response.h"
+#include "u2f_return_code.h"
 
 namespace device {
 
@@ -23,15 +24,9 @@
     U2F_V2,
     UNKNOWN,
   };
-  enum class ReturnCode : uint8_t {
-    SUCCESS,
-    FAILURE,
-    INVALID_PARAMS,
-    CONDITIONS_NOT_SATISFIED,
-  };
 
   using MessageCallback =
-      base::Callback<void(ReturnCode, std::vector<uint8_t>)>;
+      base::Callback<void(U2fReturnCode, std::vector<uint8_t>)>;
   using VersionCallback =
       base::Callback<void(bool success, ProtocolVersion version)>;
   using DeviceCallback =
diff --git a/device/u2f/u2f_hid_device.cc b/device/u2f/u2f_hid_device.cc
index 62eda92..27e1f7b 100644
--- a/device/u2f/u2f_hid_device.cc
+++ b/device/u2f/u2f_hid_device.cc
@@ -25,7 +25,6 @@
       state_(State::INIT),
       device_info_(device_info),
       weak_factory_(this) {
-  channel_id_ = kBroadcastChannel;
 }
 
 U2fHidDevice::~U2fHidDevice() {
diff --git a/device/u2f/u2f_register.cc b/device/u2f/u2f_register.cc
new file mode 100644
index 0000000..0f43e260
--- /dev/null
+++ b/device/u2f/u2f_register.cc
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "u2f_register.h"
+
+#include "base/memory/ptr_util.h"
+
+namespace device {
+
+U2fRegister::U2fRegister(const std::vector<uint8_t>& challenge_hash,
+                         const std::vector<uint8_t>& app_param,
+                         const ResponseCallback& cb)
+    : U2fRequest(cb),
+      challenge_hash_(challenge_hash),
+      app_param_(app_param),
+      weak_factory_(this) {}
+
+U2fRegister::~U2fRegister() {}
+
+// static
+std::unique_ptr<U2fRequest> U2fRegister::TryRegistration(
+    const std::vector<uint8_t>& challenge_hash,
+    const std::vector<uint8_t>& app_param,
+    const ResponseCallback& cb) {
+  std::unique_ptr<U2fRequest> request =
+      base::MakeUnique<U2fRegister>(challenge_hash, app_param, cb);
+  request->Start();
+  return request;
+}
+
+void U2fRegister::TryDevice() {
+  DCHECK(current_device_);
+
+  current_device_->Register(
+      app_param_, challenge_hash_,
+      base::Bind(&U2fRegister::OnTryDevice, weak_factory_.GetWeakPtr()));
+}
+
+void U2fRegister::OnTryDevice(U2fReturnCode return_code,
+                              std::vector<uint8_t> response_data) {
+  switch (return_code) {
+    case U2fReturnCode::SUCCESS:
+      state_ = State::COMPLETE;
+      cb_.Run(return_code, response_data);
+      break;
+    case U2fReturnCode::CONDITIONS_NOT_SATISFIED:
+      // Waiting for user touch, move on and try this device later
+      state_ = State::IDLE;
+      Transition();
+      break;
+    default:
+      state_ = State::IDLE;
+      // An error has occured, quit trying this device
+      current_device_ = nullptr;
+      Transition();
+      break;
+  }
+}
+
+}  // namespace device
diff --git a/device/u2f/u2f_register.h b/device/u2f/u2f_register.h
new file mode 100644
index 0000000..466127ed
--- /dev/null
+++ b/device/u2f/u2f_register.h
@@ -0,0 +1,37 @@
+// 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 DEVICE_U2F_U2F_REGISTER_H_
+#define DEVICE_U2F_U2F_REGISTER_H_
+
+#include <vector>
+
+#include "u2f_request.h"
+
+namespace device {
+
+class U2fRegister : public U2fRequest {
+ public:
+  U2fRegister(const std::vector<uint8_t>& challenge_hash,
+              const std::vector<uint8_t>& app_param,
+              const ResponseCallback& cb);
+  ~U2fRegister() override;
+
+  static std::unique_ptr<U2fRequest> TryRegistration(
+      const std::vector<uint8_t>& challenge_hash,
+      const std::vector<uint8_t>& app_param,
+      const ResponseCallback& cb);
+
+ private:
+  void TryDevice() override;
+  void OnTryDevice(U2fReturnCode, std::vector<uint8_t>);
+
+  std::vector<uint8_t> challenge_hash_;
+  std::vector<uint8_t> app_param_;
+  base::WeakPtrFactory<U2fRegister> weak_factory_;
+};
+
+}  // namespace device
+
+#endif  // DEVICE_U2F_U2F_REGISTER_H_
diff --git a/device/u2f/u2f_register_unittest.cc b/device/u2f/u2f_register_unittest.cc
new file mode 100644
index 0000000..2d0d2d4
--- /dev/null
+++ b/device/u2f/u2f_register_unittest.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 <list>
+
+#include "base/run_loop.h"
+#include "base/test/test_io_thread.h"
+#include "device/base/mock_device_client.h"
+#include "device/hid/mock_hid_service.h"
+#include "device/test/test_device_client.h"
+#include "mock_u2f_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "u2f_register.h"
+
+namespace device {
+
+class U2fRegisterTest : public testing::Test {
+ public:
+  U2fRegisterTest() : io_thread_(base::TestIOThread::kAutoStart) {}
+
+  void SetUp() override {
+    MockHidService* hid_service = device_client_.hid_service();
+    hid_service->FirstEnumerationComplete();
+  }
+
+ protected:
+  base::MessageLoopForUI message_loop_;
+  base::TestIOThread io_thread_;
+  device::MockDeviceClient device_client_;
+};
+
+class TestRegisterCallback {
+ public:
+  TestRegisterCallback()
+      : callback_(base::Bind(&TestRegisterCallback::ReceivedCallback,
+                             base::Unretained(this))) {}
+  ~TestRegisterCallback() {}
+
+  void ReceivedCallback(U2fReturnCode status_code,
+                        std::vector<uint8_t> response) {
+    response_ = std::make_pair(status_code, response);
+    closure_.Run();
+  }
+
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& WaitForCallback() {
+    closure_ = run_loop_.QuitClosure();
+    run_loop_.Run();
+    return response_;
+  }
+
+  const U2fRequest::ResponseCallback& callback() { return callback_; }
+
+ private:
+  std::pair<U2fReturnCode, std::vector<uint8_t>> response_;
+  base::Closure closure_;
+  U2fRequest::ResponseCallback callback_;
+  base::RunLoop run_loop_;
+};
+
+TEST_F(U2fRegisterTest, TestRegisterSuccess) {
+  std::unique_ptr<MockU2fDevice> device(new MockU2fDevice());
+  EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister));
+  EXPECT_CALL(*device.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+  TestRegisterCallback cb;
+  std::unique_ptr<U2fRequest> request = U2fRegister::TryRegistration(
+      std::vector<uint8_t>(32), std::vector<uint8_t>(32), cb.callback());
+  request->Start();
+  request->AddDeviceForTesting(std::move(device));
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& response =
+      cb.WaitForCallback();
+  EXPECT_EQ(U2fReturnCode::SUCCESS, response.first);
+  ASSERT_LT(static_cast<size_t>(0), response.second.size());
+  EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kRegister), response.second[0]);
+}
+
+TEST_F(U2fRegisterTest, TestDelayedSuccess) {
+  std::unique_ptr<MockU2fDevice> device(new MockU2fDevice());
+
+  // Go through the state machine twice before success
+  EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied))
+      .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister));
+  EXPECT_CALL(*device.get(), TryWink(testing::_))
+      .Times(2)
+      .WillRepeatedly(testing::Invoke(MockU2fDevice::WinkDoNothing));
+  TestRegisterCallback cb;
+
+  std::unique_ptr<U2fRequest> request = U2fRegister::TryRegistration(
+      std::vector<uint8_t>(32), std::vector<uint8_t>(32), cb.callback());
+  request->Start();
+  request->AddDeviceForTesting(std::move(device));
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& response =
+      cb.WaitForCallback();
+  EXPECT_EQ(U2fReturnCode::SUCCESS, response.first);
+  ASSERT_LT(static_cast<size_t>(0), response.second.size());
+  EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kRegister), response.second[0]);
+}
+
+TEST_F(U2fRegisterTest, TestMultipleDevices) {
+  // Second device will have a successful touch
+  std::unique_ptr<MockU2fDevice> device0(new MockU2fDevice());
+  std::unique_ptr<MockU2fDevice> device1(new MockU2fDevice());
+
+  EXPECT_CALL(*device0.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied));
+  // One wink per device
+  EXPECT_CALL(*device0.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+  EXPECT_CALL(*device1.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister));
+  EXPECT_CALL(*device1.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+
+  TestRegisterCallback cb;
+  std::unique_ptr<U2fRequest> request = U2fRegister::TryRegistration(
+      std::vector<uint8_t>(32), std::vector<uint8_t>(32), cb.callback());
+  request->Start();
+  request->AddDeviceForTesting(std::move(device0));
+  request->AddDeviceForTesting(std::move(device1));
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& response =
+      cb.WaitForCallback();
+  EXPECT_EQ(U2fReturnCode::SUCCESS, response.first);
+  ASSERT_LT(static_cast<size_t>(0), response.second.size());
+  EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kRegister), response.second[0]);
+}
+
+}  // namespace device
diff --git a/device/u2f/u2f_request.cc b/device/u2f/u2f_request.cc
new file mode 100644
index 0000000..a010352
--- /dev/null
+++ b/device/u2f/u2f_request.cc
@@ -0,0 +1,148 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "u2f_request.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "device/base/device_client.h"
+#include "u2f_hid_device.h"
+
+namespace device {
+
+U2fRequest::U2fRequest(const ResponseCallback& cb)
+    : state_(State::INIT),
+      cb_(cb),
+      hid_service_observer_(this),
+      weak_factory_(this) {
+  filter_.SetUsagePage(0xf1d0);
+}
+
+void U2fRequest::Transition() {
+  switch (state_) {
+    case State::IDLE:
+      IterateDevice();
+      if (!current_device_) {
+        // No devices available
+        state_ = State::OFF;
+        break;
+      }
+      state_ = State::WINK;
+      current_device_->TryWink(
+          base::Bind(&U2fRequest::Transition, weak_factory_.GetWeakPtr()));
+      break;
+    case State::WINK:
+      state_ = State::BUSY;
+      TryDevice();
+    default:
+      break;
+  }
+}
+
+void U2fRequest::Start() {
+  if (state_ == State::INIT) {
+    state_ = State::BUSY;
+    Enumerate();
+  }
+}
+
+void U2fRequest::Enumerate() {
+  HidService* hid_service = DeviceClient::Get()->GetHidService();
+  DCHECK(hid_service);
+  hid_service_observer_.Add(hid_service);
+  hid_service->GetDevices(
+      base::Bind(&U2fRequest::OnEnumerate, weak_factory_.GetWeakPtr()));
+}
+
+void U2fRequest::OnEnumerate(
+    const std::vector<scoped_refptr<HidDeviceInfo>>& devices) {
+  for (auto device_info : devices) {
+    if (filter_.Matches(device_info))
+      devices_.push_back(base::MakeUnique<U2fHidDevice>(device_info));
+  }
+
+  state_ = State::IDLE;
+  Transition();
+}
+
+void U2fRequest::OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) {
+  // Ignore non-U2F devices
+  if (!filter_.Matches(device_info))
+    return;
+
+  auto device = base::MakeUnique<U2fHidDevice>(device_info);
+  AddDevice(std::move(device));
+}
+
+void U2fRequest::OnDeviceRemoved(scoped_refptr<HidDeviceInfo> device_info) {
+  // Ignore non-U2F devices
+  if (!filter_.Matches(device_info))
+    return;
+
+  auto device = base::MakeUnique<U2fHidDevice>(device_info);
+
+  // Check if the active device was removed
+  if (current_device_ && current_device_->GetId() == device->GetId()) {
+    current_device_ = nullptr;
+    state_ = State::IDLE;
+    Transition();
+    return;
+  }
+
+  // Remove the device if it exists in either device list
+  devices_.remove_if([&device](const std::unique_ptr<U2fDevice>& this_device) {
+    return this_device->GetId() == device->GetId();
+  });
+  attempted_devices_.remove_if(
+      [&device](const std::unique_ptr<U2fDevice>& this_device) {
+        return this_device->GetId() == device->GetId();
+      });
+}
+
+void U2fRequest::IterateDevice() {
+  // Move active device to attempted device list
+  if (current_device_)
+    attempted_devices_.push_back(std::move(current_device_));
+
+  // If there is an additional device on device list, make it active.
+  // Otherwise, if all devices have been tried, move attempted devices back to
+  // the main device list.
+  if (devices_.size() > 0) {
+    current_device_ = std::move(devices_.front());
+    devices_.pop_front();
+  } else if (attempted_devices_.size() > 0) {
+    devices_ = std::move(attempted_devices_);
+    // After trying every device, wait 200ms before trying again
+    delay_callback_.Reset(
+        base::Bind(&U2fRequest::OnWaitComplete, weak_factory_.GetWeakPtr()));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, delay_callback_.callback(),
+        base::TimeDelta::FromMilliseconds(200));
+  }
+}
+
+void U2fRequest::OnWaitComplete() {
+  state_ = State::IDLE;
+  Transition();
+}
+
+void U2fRequest::AddDevice(std::unique_ptr<U2fDevice> device) {
+  devices_.push_back(std::move(device));
+
+  // Start the state machine if this is the only device
+  if (state_ == State::OFF) {
+    state_ = State::IDLE;
+    delay_callback_.Cancel();
+    Transition();
+  }
+}
+
+void U2fRequest::AddDeviceForTesting(std::unique_ptr<U2fDevice> device) {
+  AddDevice(std::move(device));
+}
+
+U2fRequest::~U2fRequest() {}
+
+}  // namespace device
diff --git a/device/u2f/u2f_request.h b/device/u2f/u2f_request.h
new file mode 100644
index 0000000..902a2ca
--- /dev/null
+++ b/device/u2f/u2f_request.h
@@ -0,0 +1,65 @@
+// 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 DEVICE_U2F_U2F_REQUEST_H_
+#define DEVICE_U2F_U2F_REQUEST_H_
+
+#include "base/cancelable_callback.h"
+#include "base/scoped_observer.h"
+#include "device/hid/hid_device_filter.h"
+#include "device/hid/hid_service.h"
+#include "u2f_device.h"
+
+namespace device {
+class U2fRequest : HidService::Observer {
+ public:
+  using ResponseCallback = base::Callback<void(U2fReturnCode status_code,
+                                               std::vector<uint8_t> response)>;
+
+  U2fRequest(const ResponseCallback& callback);
+  virtual ~U2fRequest();
+
+  void Start();
+  void AddDeviceForTesting(std::unique_ptr<U2fDevice> device);
+
+ protected:
+  enum class State {
+    INIT,
+    BUSY,
+    WINK,
+    IDLE,
+    OFF,
+    COMPLETE,
+  };
+
+  void Transition();
+  virtual void TryDevice() = 0;
+
+  std::unique_ptr<U2fDevice> current_device_;
+  State state_;
+  ResponseCallback cb_;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestAddRemoveDevice);
+  FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestIterateDevice);
+  FRIEND_TEST_ALL_PREFIXES(U2fRequestTest, TestBasicMachine);
+
+  void Enumerate();
+  void IterateDevice();
+  void OnWaitComplete();
+  void AddDevice(std::unique_ptr<U2fDevice> device);
+  void OnDeviceAdded(scoped_refptr<HidDeviceInfo> device_info) override;
+  void OnDeviceRemoved(scoped_refptr<HidDeviceInfo> device_info) override;
+  void OnEnumerate(const std::vector<scoped_refptr<HidDeviceInfo>>& devices);
+
+  std::list<std::unique_ptr<U2fDevice>> devices_;
+  std::list<std::unique_ptr<U2fDevice>> attempted_devices_;
+  base::CancelableClosure delay_callback_;
+  HidDeviceFilter filter_;
+  ScopedObserver<HidService, HidService::Observer> hid_service_observer_;
+  base::WeakPtrFactory<U2fRequest> weak_factory_;
+};
+}  // namespace device
+
+#endif  // DEVICE_U2F_U2F_REQUEST_H_
diff --git a/device/u2f/u2f_request_unittest.cc b/device/u2f/u2f_request_unittest.cc
new file mode 100644
index 0000000..53c2130a
--- /dev/null
+++ b/device/u2f/u2f_request_unittest.cc
@@ -0,0 +1,159 @@
+// 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 <list>
+
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/test/test_io_thread.h"
+#include "device/base/mock_device_client.h"
+#include "device/hid/mock_hid_service.h"
+#include "device/test/test_device_client.h"
+#include "device/test/usb_test_gadget.h"
+#include "device/u2f/u2f_hid_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "u2f_request.h"
+
+namespace device {
+namespace {
+#if defined(OS_MACOSX)
+const uint64_t kTestDeviceId0 = 42;
+const uint64_t kTestDeviceId1 = 43;
+const uint64_t kTestDeviceId2 = 44;
+#else
+const char* kTestDeviceId0 = "device0";
+const char* kTestDeviceId1 = "device1";
+const char* kTestDeviceId2 = "device2";
+#endif
+
+class FakeU2fRequest : public U2fRequest {
+ public:
+  FakeU2fRequest(const ResponseCallback& cb) : U2fRequest(cb) {}
+  ~FakeU2fRequest() override {}
+
+  void TryDevice() override {
+    cb_.Run(U2fReturnCode::SUCCESS, std::vector<uint8_t>());
+  }
+};
+}  // namespace
+
+class TestResponseCallback {
+ public:
+  TestResponseCallback()
+      : callback_(base::Bind(&TestResponseCallback::ReceivedCallback,
+                             base::Unretained(this))) {}
+  ~TestResponseCallback() {}
+
+  void ReceivedCallback(U2fReturnCode status, std::vector<uint8_t> data) {
+    closure_.Run();
+  }
+
+  void WaitForCallback() {
+    closure_ = run_loop_.QuitClosure();
+    run_loop_.Run();
+  }
+
+  U2fRequest::ResponseCallback& callback() { return callback_; }
+
+ private:
+  base::Closure closure_;
+  U2fRequest::ResponseCallback callback_;
+  base::RunLoop run_loop_;
+};
+
+class U2fRequestTest : public testing::Test {
+ public:
+  U2fRequestTest() : io_thread_(base::TestIOThread::kAutoStart) {}
+
+ protected:
+  base::MessageLoopForUI message_loop_;
+  base::TestIOThread io_thread_;
+  device::MockDeviceClient device_client_;
+};
+
+TEST_F(U2fRequestTest, TestAddRemoveDevice) {
+  MockHidService* hid_service = device_client_.hid_service();
+  HidCollectionInfo c_info;
+  hid_service->FirstEnumerationComplete();
+
+  TestResponseCallback cb;
+  FakeU2fRequest request(cb.callback());
+  request.Enumerate();
+  EXPECT_EQ(static_cast<size_t>(0), request.devices_.size());
+
+  // Add one U2F device
+  c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0));
+  scoped_refptr<HidDeviceInfo> u2f_device = make_scoped_refptr(
+      new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
+                        kHIDBusTypeUSB, c_info, 64, 64, 0));
+  hid_service->AddDevice(u2f_device);
+  EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
+
+  // Add one non-U2F device. Verify that it is not added to our device list.
+  scoped_refptr<HidDeviceInfo> other_device = make_scoped_refptr(
+      new HidDeviceInfo(kTestDeviceId2, 0, 0, "Other Device", "OtherDevice",
+                        kHIDBusTypeUSB, std::vector<uint8_t>()));
+  hid_service->AddDevice(other_device);
+  EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
+
+  // Remove the non-U2F device and verify that device list was unchanged.
+  hid_service->RemoveDevice(kTestDeviceId2);
+  EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
+
+  // Remove the U2F device and verify that device list is empty.
+  hid_service->RemoveDevice(kTestDeviceId0);
+  EXPECT_EQ(static_cast<size_t>(0), request.devices_.size());
+}
+
+TEST_F(U2fRequestTest, TestIterateDevice) {
+  TestResponseCallback cb;
+  FakeU2fRequest request(cb.callback());
+  HidCollectionInfo c_info;
+  // Add one U2F device and one non-U2f device
+  c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0));
+  scoped_refptr<HidDeviceInfo> device0 = make_scoped_refptr(
+      new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
+                        kHIDBusTypeUSB, c_info, 64, 64, 0));
+  request.devices_.push_back(base::MakeUnique<U2fHidDevice>(device0));
+  scoped_refptr<HidDeviceInfo> device1 = make_scoped_refptr(
+      new HidDeviceInfo(kTestDeviceId1, 0, 0, "Test Fido Device", "123FIDO",
+                        kHIDBusTypeUSB, c_info, 64, 64, 0));
+  request.devices_.push_back(base::MakeUnique<U2fHidDevice>(device1));
+
+  // Move first device to current
+  request.IterateDevice();
+  ASSERT_NE(nullptr, request.current_device_);
+  EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
+
+  // Move second device to current, first to attempted
+  request.IterateDevice();
+  ASSERT_NE(nullptr, request.current_device_);
+  EXPECT_EQ(static_cast<size_t>(1), request.attempted_devices_.size());
+
+  // Move second device from current to attempted, move attempted to devices as
+  // all devices have been attempted
+  request.IterateDevice();
+  ASSERT_EQ(nullptr, request.current_device_);
+  EXPECT_EQ(static_cast<size_t>(2), request.devices_.size());
+  EXPECT_EQ(static_cast<size_t>(0), request.attempted_devices_.size());
+}
+
+TEST_F(U2fRequestTest, TestBasicMachine) {
+  MockHidService* hid_service = device_client_.hid_service();
+  hid_service->FirstEnumerationComplete();
+  TestResponseCallback cb;
+  FakeU2fRequest request(cb.callback());
+  request.Start();
+  // Add one U2F device
+  HidCollectionInfo c_info;
+  c_info.usage = HidUsageAndPage(1, static_cast<HidUsageAndPage::Page>(0xf1d0));
+  scoped_refptr<HidDeviceInfo> u2f_device = make_scoped_refptr(
+      new HidDeviceInfo(kTestDeviceId0, 0, 0, "Test Fido Device", "123FIDO",
+                        kHIDBusTypeUSB, c_info, 64, 64, 0));
+  hid_service->AddDevice(u2f_device);
+  cb.WaitForCallback();
+  EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
+}
+
+}  // namespace device
diff --git a/device/u2f/u2f_return_code.h b/device/u2f/u2f_return_code.h
new file mode 100644
index 0000000..4aa0b42
--- /dev/null
+++ b/device/u2f/u2f_return_code.h
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_U2F_U2F_RETURN_CODE_H_
+#define DEVICE_U2F_U2F_RETURN_CODE_H_
+
+namespace device {
+
+enum class U2fReturnCode : uint8_t {
+  SUCCESS,
+  FAILURE,
+  INVALID_PARAMS,
+  CONDITIONS_NOT_SATISFIED,
+};
+
+}  // namespace device
+
+#endif  // DEVICE_U2F_U2F_RETURN_CODE_H_
diff --git a/device/u2f/u2f_sign.cc b/device/u2f/u2f_sign.cc
new file mode 100644
index 0000000..d537c48
--- /dev/null
+++ b/device/u2f/u2f_sign.cc
@@ -0,0 +1,92 @@
+// 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 "u2f_sign.h"
+
+#include "base/memory/ptr_util.h"
+
+namespace device {
+
+U2fSign::U2fSign(const std::vector<std::vector<uint8_t>>& registered_keys,
+                 const std::vector<uint8_t>& challenge_hash,
+                 const std::vector<uint8_t>& app_param,
+                 const ResponseCallback& cb)
+    : U2fRequest(cb),
+      registered_keys_(registered_keys),
+      challenge_hash_(challenge_hash),
+      app_param_(app_param),
+      weak_factory_(this) {}
+
+U2fSign::~U2fSign() {}
+
+// static
+std::unique_ptr<U2fRequest> U2fSign::TrySign(
+    const std::vector<std::vector<uint8_t>>& registered_keys,
+    const std::vector<uint8_t>& challenge_hash,
+    const std::vector<uint8_t>& app_param,
+    const ResponseCallback& cb) {
+  std::unique_ptr<U2fRequest> request =
+      base::MakeUnique<U2fSign>(registered_keys, challenge_hash, app_param, cb);
+  request->Start();
+  return request;
+}
+
+void U2fSign::TryDevice() {
+  DCHECK(current_device_);
+
+  if (registered_keys_.size() == 0) {
+    // Send registration (Fake enroll) if no keys were provided
+    current_device_->Register(
+        kBogusAppParam, kBogusChallenge,
+        base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(),
+                   registered_keys_.cbegin()));
+    return;
+  }
+  // Try signing current device with the first registered key
+  auto it = registered_keys_.cbegin();
+  current_device_->Sign(
+      app_param_, challenge_hash_, *it,
+      base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(), it));
+}
+
+void U2fSign::OnTryDevice(std::vector<std::vector<uint8_t>>::const_iterator it,
+                          U2fReturnCode return_code,
+                          std::vector<uint8_t> response_data) {
+  switch (return_code) {
+    case U2fReturnCode::SUCCESS:
+      state_ = State::COMPLETE;
+      cb_.Run(return_code, response_data);
+      break;
+    case U2fReturnCode::CONDITIONS_NOT_SATISFIED: {
+      // Key handle is accepted by this device, but waiting on user touch. Move
+      // on and try this device again later.
+      state_ = State::IDLE;
+      Transition();
+      break;
+    }
+    case U2fReturnCode::INVALID_PARAMS:
+      if (++it != registered_keys_.end()) {
+        // Key is not for this device. Try signing with the next key.
+        current_device_->Sign(
+            app_param_, challenge_hash_, *it,
+            base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(), it));
+      } else {
+        // No provided key was accepted by this device. Send registration
+        // (Fake enroll) request to device.
+        current_device_->Register(
+            kBogusAppParam, kBogusChallenge,
+            base::Bind(&U2fSign::OnTryDevice, weak_factory_.GetWeakPtr(),
+                       registered_keys_.cbegin()));
+      }
+      break;
+    default:
+      // Some sort of failure occured. Abandon this device and move on.
+      state_ = State::IDLE;
+      current_device_ = nullptr;
+      Transition();
+      break;
+  }
+}
+
+}  // namespace device
diff --git a/device/u2f/u2f_sign.h b/device/u2f/u2f_sign.h
new file mode 100644
index 0000000..e924137
--- /dev/null
+++ b/device/u2f/u2f_sign.h
@@ -0,0 +1,51 @@
+// 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 DEVICE_U2F_U2F_SIGN_H_
+#define DEVICE_U2F_U2F_SIGN_H_
+
+#include <vector>
+
+#include "u2f_request.h"
+
+namespace device {
+
+class U2fSign : public U2fRequest {
+ public:
+  U2fSign(const std::vector<std::vector<uint8_t>>& registered_keys,
+          const std::vector<uint8_t>& challenge_hash,
+          const std::vector<uint8_t>& app_param,
+          const ResponseCallback& cb);
+  ~U2fSign() override;
+
+  static std::unique_ptr<U2fRequest> TrySign(
+      const std::vector<std::vector<uint8_t>>& registered_keys,
+      const std::vector<uint8_t>& challenge_hash,
+      const std::vector<uint8_t>& app_param,
+      const ResponseCallback& cb);
+
+ private:
+  void TryDevice() override;
+  void OnTryDevice(std::vector<std::vector<uint8_t>>::const_iterator,
+                   U2fReturnCode,
+                   std::vector<uint8_t>);
+
+  const std::vector<std::vector<uint8_t>> registered_keys_;
+  std::vector<uint8_t> challenge_hash_;
+  std::vector<uint8_t> app_param_;
+  const std::vector<uint8_t> kBogusAppParam = {
+      0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+      0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+      0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41};
+  const std::vector<uint8_t> kBogusChallenge = {
+      0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+      0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+      0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42};
+
+  base::WeakPtrFactory<U2fSign> weak_factory_;
+};
+
+}  // namespace device
+
+#endif  // DEVICE_U2F_U2F_SIGN_H_
diff --git a/device/u2f/u2f_sign_unittest.cc b/device/u2f/u2f_sign_unittest.cc
new file mode 100644
index 0000000..8c846217
--- /dev/null
+++ b/device/u2f/u2f_sign_unittest.cc
@@ -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.
+
+#include <list>
+
+#include "base/run_loop.h"
+#include "base/test/test_io_thread.h"
+#include "device/base/mock_device_client.h"
+#include "device/hid/mock_hid_service.h"
+#include "device/test/test_device_client.h"
+#include "mock_u2f_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "u2f_sign.h"
+
+namespace device {
+class U2fSignTest : public testing::Test {
+ public:
+  U2fSignTest() : io_thread_(base::TestIOThread::kAutoStart) {}
+
+  void SetUp() override {
+    MockHidService* hid_service = device_client_.hid_service();
+    hid_service->FirstEnumerationComplete();
+  }
+
+ protected:
+  base::MessageLoopForUI message_loop_;
+  base::TestIOThread io_thread_;
+  device::MockDeviceClient device_client_;
+};
+
+class TestSignCallback {
+ public:
+  TestSignCallback()
+      : callback_(base::Bind(&TestSignCallback::ReceivedCallback,
+                             base::Unretained(this))) {}
+  ~TestSignCallback() {}
+
+  void ReceivedCallback(U2fReturnCode status_code,
+                        std::vector<uint8_t> response) {
+    response_ = std::make_pair(status_code, response);
+    closure_.Run();
+  }
+
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& WaitForCallback() {
+    closure_ = run_loop_.QuitClosure();
+    run_loop_.Run();
+    return response_;
+  }
+
+  const U2fRequest::ResponseCallback& callback() { return callback_; }
+
+ private:
+  std::pair<U2fReturnCode, std::vector<uint8_t>> response_;
+  base::Closure closure_;
+  U2fRequest::ResponseCallback callback_;
+  base::RunLoop run_loop_;
+};
+
+TEST_F(U2fSignTest, TestSignSuccess) {
+  std::vector<uint8_t> key(32, 0xA);
+  std::vector<std::vector<uint8_t>> handles = {key};
+  std::unique_ptr<MockU2fDevice> device(new MockU2fDevice());
+  EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign));
+  EXPECT_CALL(*device.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+  TestSignCallback cb;
+  std::unique_ptr<U2fRequest> request =
+      U2fSign::TrySign(handles, std::vector<uint8_t>(32),
+                       std::vector<uint8_t>(32), cb.callback());
+  request->Start();
+  request->AddDeviceForTesting(std::move(device));
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& response =
+      cb.WaitForCallback();
+  EXPECT_EQ(U2fReturnCode::SUCCESS, response.first);
+  // Correct key was sent so a sign response is expected
+  ASSERT_LT(static_cast<size_t>(0), response.second.size());
+  EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kSign), response.second[0]);
+}
+
+TEST_F(U2fSignTest, TestDelayedSuccess) {
+  std::vector<uint8_t> key(32, 0xA);
+  std::vector<std::vector<uint8_t>> handles = {key};
+  std::unique_ptr<MockU2fDevice> device(new MockU2fDevice());
+
+  // Go through the state machine twice before success
+  EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied))
+      .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign));
+  EXPECT_CALL(*device.get(), TryWink(testing::_))
+      .Times(2)
+      .WillRepeatedly(testing::Invoke(MockU2fDevice::WinkDoNothing));
+  TestSignCallback cb;
+
+  std::unique_ptr<U2fRequest> request =
+      U2fSign::TrySign(handles, std::vector<uint8_t>(32),
+                       std::vector<uint8_t>(32), cb.callback());
+  request->Start();
+  request->AddDeviceForTesting(std::move(device));
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& response =
+      cb.WaitForCallback();
+  EXPECT_EQ(U2fReturnCode::SUCCESS, response.first);
+  // Correct key was sent so a sign response is expected
+  ASSERT_LT(static_cast<size_t>(0), response.second.size());
+  EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kSign), response.second[0]);
+}
+
+TEST_F(U2fSignTest, TestMultipleHandles) {
+  std::vector<uint8_t> key(32, 0xA);
+  std::vector<uint8_t> wrong_key0(32, 0xB);
+  std::vector<uint8_t> wrong_key1(32, 0xC);
+  std::vector<uint8_t> wrong_key2(32, 0xD);
+  // Put wrong key first to ensure that it will be tested before the correct key
+  std::vector<std::vector<uint8_t>> handles = {wrong_key0, wrong_key1,
+                                               wrong_key2, key};
+  std::unique_ptr<MockU2fDevice> device(new MockU2fDevice());
+
+  // Wrong key would respond with SW_WRONG_DATA
+  EXPECT_CALL(*device.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WrongData))
+      .WillOnce(testing::Invoke(MockU2fDevice::WrongData))
+      .WillOnce(testing::Invoke(MockU2fDevice::WrongData))
+      .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign));
+  // Only one wink expected per device
+  EXPECT_CALL(*device.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+
+  TestSignCallback cb;
+  std::unique_ptr<U2fRequest> request =
+      U2fSign::TrySign(handles, std::vector<uint8_t>(32),
+                       std::vector<uint8_t>(32), cb.callback());
+  request->Start();
+  request->AddDeviceForTesting(std::move(device));
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& response =
+      cb.WaitForCallback();
+  EXPECT_EQ(U2fReturnCode::SUCCESS, response.first);
+  // Correct key was sent so a sign response is expected
+  ASSERT_LT(static_cast<size_t>(0), response.second.size());
+  EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kSign), response.second[0]);
+}
+
+TEST_F(U2fSignTest, TestMultipleDevices) {
+  std::vector<uint8_t> key0(32, 0xA);
+  std::vector<uint8_t> key1(32, 0xB);
+  // Second device will have a successful touch
+  std::vector<std::vector<uint8_t>> handles = {key0, key1};
+  std::unique_ptr<MockU2fDevice> device0(new MockU2fDevice());
+  std::unique_ptr<MockU2fDevice> device1(new MockU2fDevice());
+
+  EXPECT_CALL(*device0.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WrongData))
+      .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied));
+  // One wink per device
+  EXPECT_CALL(*device0.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+  EXPECT_CALL(*device1.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::NoErrorSign));
+  EXPECT_CALL(*device1.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+
+  TestSignCallback cb;
+  std::unique_ptr<U2fRequest> request =
+      U2fSign::TrySign(handles, std::vector<uint8_t>(32),
+                       std::vector<uint8_t>(32), cb.callback());
+  request->Start();
+  request->AddDeviceForTesting(std::move(device0));
+  request->AddDeviceForTesting(std::move(device1));
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& response =
+      cb.WaitForCallback();
+  EXPECT_EQ(U2fReturnCode::SUCCESS, response.first);
+  // Correct key was sent so a sign response is expected
+  ASSERT_LT(static_cast<size_t>(0), response.second.size());
+  EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kSign), response.second[0]);
+}
+
+TEST_F(U2fSignTest, TestFakeEnroll) {
+  std::vector<uint8_t> key0(32, 0xA);
+  std::vector<uint8_t> key1(32, 0xB);
+  // Second device will be have a successful touch
+  std::vector<std::vector<uint8_t>> handles = {key0, key1};
+  std::unique_ptr<MockU2fDevice> device0(new MockU2fDevice());
+  std::unique_ptr<MockU2fDevice> device1(new MockU2fDevice());
+
+  EXPECT_CALL(*device0.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WrongData))
+      .WillOnce(testing::Invoke(MockU2fDevice::NotSatisfied));
+  // One wink per device
+  EXPECT_CALL(*device0.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+  // Both keys will be tried, when both fail, register is tried on that device
+  EXPECT_CALL(*device1.get(), DeviceTransactPtr(testing::_, testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WrongData))
+      .WillOnce(testing::Invoke(MockU2fDevice::WrongData))
+      .WillOnce(testing::Invoke(MockU2fDevice::NoErrorRegister));
+  EXPECT_CALL(*device1.get(), TryWink(testing::_))
+      .WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
+
+  TestSignCallback cb;
+  std::unique_ptr<U2fRequest> request =
+      U2fSign::TrySign(handles, std::vector<uint8_t>(32),
+                       std::vector<uint8_t>(32), cb.callback());
+  request->Start();
+  request->AddDeviceForTesting(std::move(device0));
+  request->AddDeviceForTesting(std::move(device1));
+  std::pair<U2fReturnCode, std::vector<uint8_t>>& response =
+      cb.WaitForCallback();
+  EXPECT_EQ(U2fReturnCode::SUCCESS, response.first);
+  // Device that responded had no correct keys, so a registration response is
+  // expected
+  ASSERT_LT(static_cast<size_t>(0), response.second.size());
+  EXPECT_EQ(static_cast<uint8_t>(MockU2fDevice::kRegister), response.second[0]);
+}
+
+}  // namespace device
diff --git a/extensions/renderer/api_binding.cc b/extensions/renderer/api_binding.cc
index 39d5dea..9abc53e 100644
--- a/extensions/renderer/api_binding.cc
+++ b/extensions/renderer/api_binding.cc
@@ -289,9 +289,9 @@
 
 v8::Local<v8::Object> APIBinding::CreateInstance(
     v8::Local<v8::Context> context,
-    v8::Isolate* isolate,
     const AvailabilityCallback& is_available) {
   DCHECK(IsContextValid(context));
+  v8::Isolate* isolate = context->GetIsolate();
   if (object_template_.IsEmpty())
     InitializeTemplate(isolate);
   DCHECK(!object_template_.IsEmpty());
@@ -498,11 +498,7 @@
   // GetCurrentContext() should always be correct.
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
 
-  std::vector<v8::Local<v8::Value>> argument_list;
-  if (arguments->Length() > 0) {
-    // Just copying handles should never fail.
-    CHECK(arguments->GetRemaining(&argument_list));
-  }
+  std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll();
 
   bool invalid_invocation = false;
   v8::Local<v8::Function> custom_callback;
diff --git a/extensions/renderer/api_binding.h b/extensions/renderer/api_binding.h
index 7728e3f..972df11 100644
--- a/extensions/renderer/api_binding.h
+++ b/extensions/renderer/api_binding.h
@@ -72,7 +72,6 @@
   // Returns a new v8::Object for the API this APIBinding represents.
   v8::Local<v8::Object> CreateInstance(
       v8::Local<v8::Context> context,
-      v8::Isolate* isolate,
       const AvailabilityCallback& is_available);
 
   APIBindingHooks* hooks() { return binding_hooks_.get(); }
diff --git a/extensions/renderer/api_binding_unittest.cc b/extensions/renderer/api_binding_unittest.cc
index c589d08..d17e720 100644
--- a/extensions/renderer/api_binding_unittest.cc
+++ b/extensions/renderer/api_binding_unittest.cc
@@ -313,7 +313,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
   EXPECT_EQ(
       0u,
       binding_object->GetOwnPropertyNames(context).ToLocalChecked()->Length());
@@ -329,7 +329,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   ExpectPass(binding_object, "obj.oneString('foo');", "['foo']", false);
   ExpectPass(binding_object, "obj.oneString('');", "['']", false);
@@ -423,7 +423,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   const char kExpected[] =
       "{'ALPHA':'alpha','CAMEL_CASE':'camelCase','HYPHEN_ATED':'Hyphen-ated',"
@@ -450,7 +450,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   EXPECT_EQ(
       "{\"\":\"\",\"OTHER\":\"other\"}",
@@ -497,7 +497,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   ExpectPass(binding_object, "obj.takesRefObj({prop1: 'foo'})",
              "[{'prop1':'foo'}]", false);
@@ -546,7 +546,7 @@
   };
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(is_available));
+      binding()->CreateInstance(context, base::Bind(is_available));
 
   auto is_defined = [&binding_object, context](const std::string& name) {
     v8::Local<v8::Value> val =
@@ -574,7 +574,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   // Event behavior is tested in the APIEventHandler unittests as well as the
   // APIBindingsSystem tests, so we really only need to check that the events
@@ -612,7 +612,7 @@
   v8::HandleScope handle_scope(isolate());
   v8::Local<v8::Context> context = MainContext();
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
   EXPECT_EQ("17",
             GetStringPropertyFromObject(binding_object, context, "prop1"));
   EXPECT_EQ(R"({"subprop1":"some value","subprop2":true})",
@@ -664,7 +664,7 @@
   v8::HandleScope handle_scope(isolate());
   v8::Local<v8::Context> context = MainContext();
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
   EXPECT_EQ(R"({"alphaProp":"alphaVal"})",
             GetStringPropertyFromObject(binding_object, context, "alpha"));
   EXPECT_EQ(
@@ -680,7 +680,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   v8::Local<v8::Function> func =
       FunctionFromString(context, "(function(obj) { obj.oneString('foo'); })");
@@ -700,10 +700,10 @@
   SetFunctions(kFunctions);
   InitializeBinding();
 
-  v8::Local<v8::Object> binding_object_a = binding()->CreateInstance(
-      context_a, isolate(), base::Bind(&AllowAllAPIs));
-  v8::Local<v8::Object> binding_object_b = binding()->CreateInstance(
-      context_b, isolate(), base::Bind(&AllowAllAPIs));
+  v8::Local<v8::Object> binding_object_a =
+      binding()->CreateInstance(context_a, base::Bind(&AllowAllAPIs));
+  v8::Local<v8::Object> binding_object_b =
+      binding()->CreateInstance(context_b, base::Bind(&AllowAllAPIs));
 
   ExpectPass(context_a, binding_object_a, "obj.oneString('foo');", "['foo']",
              false);
@@ -744,7 +744,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   // First try calling the oneString() method, which has a custom hook
   // installed.
@@ -785,7 +785,7 @@
   InitializeBinding();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   // First try calling with an invalid invocation. An error should be raised and
   // the hook should never have been called, since the arguments didn't match.
@@ -837,7 +837,7 @@
   InitializeBinding();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   // Call the method with a hook. Since the hook updates arguments before
   // validation, we should be able to pass in invalid arguments and still
@@ -895,7 +895,7 @@
   InitializeBinding();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   v8::Local<v8::Function> function =
       FunctionFromString(context,
@@ -937,7 +937,7 @@
   InitializeBinding();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   v8::Local<v8::Function> function =
       FunctionFromString(context,
@@ -998,7 +998,7 @@
   InitializeBinding();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   v8::Local<v8::Function> function =
       FunctionFromString(context,
@@ -1048,7 +1048,7 @@
   InitializeBinding();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   {
     // Test an invocation that we expect to throw an exception.
@@ -1104,7 +1104,7 @@
   InitializeBinding();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   // Try calling the method with an invalid signature. Since it's invalid, we
   // should never enter the hook.
@@ -1152,7 +1152,7 @@
   InitializeBinding();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   // Call the method with a valid signature. The hook should be entered and
   // manipulate the arguments.
@@ -1168,7 +1168,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> binding_object =
-      binding()->CreateInstance(context, isolate(), base::Bind(&AllowAllAPIs));
+      binding()->CreateInstance(context, base::Bind(&AllowAllAPIs));
 
   v8::Local<v8::Function> function =
       FunctionFromString(context, "(function(obj) { obj.oneString('foo');})");
diff --git a/extensions/renderer/api_bindings_system.cc b/extensions/renderer/api_bindings_system.cc
index 344041f..29e38c8 100644
--- a/extensions/renderer/api_bindings_system.cc
+++ b/extensions/renderer/api_bindings_system.cc
@@ -31,7 +31,6 @@
 v8::Local<v8::Object> APIBindingsSystem::CreateAPIInstance(
     const std::string& api_name,
     v8::Local<v8::Context> context,
-    v8::Isolate* isolate,
     const APIBinding::AvailabilityCallback& is_available,
     APIBindingHooks** hooks_out) {
   std::unique_ptr<APIBinding>& binding = api_bindings_[api_name];
@@ -39,7 +38,7 @@
     binding = CreateNewAPIBinding(api_name);
   if (hooks_out)
     *hooks_out = binding->hooks();
-  return binding->CreateInstance(context, isolate, is_available);
+  return binding->CreateInstance(context, is_available);
 }
 
 std::unique_ptr<APIBinding> APIBindingsSystem::CreateNewAPIBinding(
diff --git a/extensions/renderer/api_bindings_system.h b/extensions/renderer/api_bindings_system.h
index 9f96dbb..138d42d9 100644
--- a/extensions/renderer/api_bindings_system.h
+++ b/extensions/renderer/api_bindings_system.h
@@ -54,7 +54,6 @@
   v8::Local<v8::Object> CreateAPIInstance(
       const std::string& api_name,
       v8::Local<v8::Context> context,
-      v8::Isolate* isolate,
       const APIBinding::AvailabilityCallback& is_available,
       APIBindingHooks** hooks_out);
 
diff --git a/extensions/renderer/api_bindings_system_unittest.cc b/extensions/renderer/api_bindings_system_unittest.cc
index 84f7d91..b00c8465 100644
--- a/extensions/renderer/api_bindings_system_unittest.cc
+++ b/extensions/renderer/api_bindings_system_unittest.cc
@@ -205,10 +205,10 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> alpha_api = bindings_system()->CreateAPIInstance(
-      kAlphaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), nullptr);
+      kAlphaAPIName, context, base::Bind(&AllowAllAPIs), nullptr);
   ASSERT_FALSE(alpha_api.IsEmpty());
   v8::Local<v8::Object> beta_api = bindings_system()->CreateAPIInstance(
-      kBetaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), nullptr);
+      kBetaAPIName, context, base::Bind(&AllowAllAPIs), nullptr);
   ASSERT_FALSE(beta_api.IsEmpty());
 
   {
@@ -334,7 +334,7 @@
   binding_hooks->SetDelegate(std::move(test_hooks));
 
   v8::Local<v8::Object> alpha_api = bindings_system()->CreateAPIInstance(
-      kAlphaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), nullptr);
+      kAlphaAPIName, context, base::Bind(&AllowAllAPIs), nullptr);
   ASSERT_FALSE(alpha_api.IsEmpty());
 
   {
@@ -372,7 +372,7 @@
 
   APIBindingHooks* hooks = nullptr;
   v8::Local<v8::Object> alpha_api = bindings_system()->CreateAPIInstance(
-      kAlphaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), &hooks);
+      kAlphaAPIName, context, base::Bind(&AllowAllAPIs), &hooks);
   ASSERT_FALSE(alpha_api.IsEmpty());
   ASSERT_TRUE(hooks);
   v8::Local<v8::Object> js_hooks = hooks->GetJSHookInterface(context);
@@ -415,7 +415,7 @@
   // alpha API yet, since this tests that we can lazily populate the type
   // information.
   v8::Local<v8::Object> gamma_api = bindings_system()->CreateAPIInstance(
-      kGammaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), nullptr);
+      kGammaAPIName, context, base::Bind(&AllowAllAPIs), nullptr);
   ASSERT_FALSE(gamma_api.IsEmpty());
 
   {
@@ -449,7 +449,7 @@
   binding_hooks->SetDelegate(std::move(test_hooks));
 
   v8::Local<v8::Object> api = bindings_system()->CreateAPIInstance(
-      kAlphaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), nullptr);
+      kAlphaAPIName, context, base::Bind(&AllowAllAPIs), nullptr);
 
   v8::Local<v8::Value> event =
       GetPropertyFromObject(api, context, "alphaEvent");
diff --git a/extensions/renderer/chrome_setting.cc b/extensions/renderer/chrome_setting.cc
index 43dad58..81f1ca2 100644
--- a/extensions/renderer/chrome_setting.cc
+++ b/extensions/renderer/chrome_setting.cc
@@ -133,11 +133,7 @@
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
 
-  std::vector<v8::Local<v8::Value>> argument_list;
-  if (arguments->Length() > 0) {
-    // Just copying handles should never fail.
-    CHECK(arguments->GetRemaining(&argument_list));
-  }
+  std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll();
 
   std::string full_name = "types.ChromeSetting." + method_name;
   std::unique_ptr<base::ListValue> converted_arguments;
diff --git a/extensions/renderer/declarative_event.cc b/extensions/renderer/declarative_event.cc
index 4e00229..9365c8d 100644
--- a/extensions/renderer/declarative_event.cc
+++ b/extensions/renderer/declarative_event.cc
@@ -174,13 +174,7 @@
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
 
-  // TODO(devlin): This pattern is getting common. We should probably pull it
-  // out somewhere.
-  std::vector<v8::Local<v8::Value>> argument_list;
-  if (arguments->Length() > 0) {
-    // Just copying handles should never fail.
-    CHECK(arguments->GetRemaining(&argument_list));
-  }
+  std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll();
 
   // The events API has two undocumented parameters for each function: the name
   // of the event, and the "webViewInstanceId". Currently, stub 0 for webview
diff --git a/extensions/renderer/declarative_event_unittest.cc b/extensions/renderer/declarative_event_unittest.cc
index 4b33591d..c5771d8 100644
--- a/extensions/renderer/declarative_event_unittest.cc
+++ b/extensions/renderer/declarative_event_unittest.cc
@@ -199,8 +199,7 @@
   v8::Local<v8::Context> context = MainContext();
 
   v8::Local<v8::Object> api = bindings_system()->CreateAPIInstance(
-      kDeclarativeAPIName, context, isolate(), base::Bind(&AllowAllAPIs),
-      nullptr);
+      kDeclarativeAPIName, context, base::Bind(&AllowAllAPIs), nullptr);
   ASSERT_FALSE(api.IsEmpty());
 
   v8::Local<v8::Value> declarative_event =
diff --git a/extensions/renderer/native_extension_bindings_system.cc b/extensions/renderer/native_extension_bindings_system.cc
index a2a9fa8..f4d5d5c 100644
--- a/extensions/renderer/native_extension_bindings_system.cc
+++ b/extensions/renderer/native_extension_bindings_system.cc
@@ -198,8 +198,7 @@
                                         APIBindingsSystem* bindings_system) {
   APIBindingHooks* hooks = nullptr;
   v8::Local<v8::Object> binding_object = bindings_system->CreateAPIInstance(
-      name, context, context->GetIsolate(),
-      base::Bind(&IsAPIMethodAvailable, script_context), &hooks);
+      name, context, base::Bind(&IsAPIMethodAvailable, script_context), &hooks);
 
   gin::Handle<APIBindingBridge> bridge_handle = gin::CreateHandle(
       context->GetIsolate(),
diff --git a/extensions/renderer/storage_area.cc b/extensions/renderer/storage_area.cc
index d3017399..b952460 100644
--- a/extensions/renderer/storage_area.cc
+++ b/extensions/renderer/storage_area.cc
@@ -183,11 +183,7 @@
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
 
-  std::vector<v8::Local<v8::Value>> argument_list;
-  if (arguments->Length() > 0) {
-    // Just copying handles should never fail.
-    CHECK(arguments->GetRemaining(&argument_list));
-  }
+  std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll();
 
   std::unique_ptr<base::ListValue> converted_arguments;
   v8::Local<v8::Function> callback;
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index b3407de0..3922634 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -19,9 +19,9 @@
   }
 }
 
-group("headless_lib") {
+group("headless") {
   deps = [
-    "//headless:headless",
+    "//headless:headless_lib",
   ]
 }
 
@@ -201,7 +201,7 @@
   ]
 }
 
-component("headless") {
+static_library("headless_lib") {
   sources = generated_devtools_api + [
               "app/headless_shell_switches.cc",
               "app/headless_shell_switches.h",
@@ -243,6 +243,10 @@
               "lib/headless_crash_reporter_client.h",
               "lib/headless_content_client.cc",
               "lib/headless_content_client.h",
+              "lib/headless_content_main_delegate.cc",
+              "lib/headless_content_main_delegate.h",
+              "lib/renderer/headless_content_renderer_client.cc",
+              "lib/renderer/headless_content_renderer_client.h",
               "public/headless_browser.cc",
               "public/headless_browser.h",
               "public/headless_browser_context.h",
@@ -323,6 +327,9 @@
     "//components/crash/content/browser",
     "//components/security_state/content",
     "//components/security_state/core",
+    "//content/public/app:both",
+    "//content/public/browser",
+    "//content/public/child:child",
     "//content/public/common",
     "//content/public/common:service_names",
     "//services/service_manager/public/cpp",
@@ -334,28 +341,19 @@
     "//url",
   ]
 
-  if (is_component_build) {
-    sources += [
-      "lib/headless_content_main_delegate.cc",
-      "lib/headless_content_main_delegate.h",
-      "lib/renderer/headless_content_renderer_client.cc",
-      "lib/renderer/headless_content_renderer_client.h",
-    ]
-
-    if (enable_basic_printing) {
-      deps += [
-        "//components/printing/browser",
-        "//components/printing/renderer",
-      ]
-    }
-  }
-
   if (is_mac) {
     deps += [ ":mac_helpers" ]
   } else {
     deps += [ "//ui/aura" ]
   }
 
+  if (enable_basic_printing) {
+    deps += [
+      "//components/printing/browser",
+      "//components/printing/renderer",
+    ]
+  }
+
   if (headless_use_embedded_resources) {
     deps += [ ":embed_resources" ]
     sources += [
@@ -369,32 +367,8 @@
   if (use_ozone) {
     deps += [ "//ui/ozone" ]
   }
-  configs += [ ":headless_implementation" ]
-}
 
-# Headless renderer is a convenience source set that includes headless classes
-# that depend on the reenderer. These are not added in case of a component build
-# since in that case they are already included in the headless component.
-source_set("headless_renderer") {
-  deps = [
-    ":headless",
-  ]
-  if (!is_component_build) {
-    sources = [
-      "lib/headless_content_main_delegate.cc",
-      "lib/headless_content_main_delegate.h",
-      "lib/renderer/headless_content_renderer_client.cc",
-      "lib/renderer/headless_content_renderer_client.h",
-    ]
-    deps += [ "//ui/base" ]
-    if (enable_basic_printing) {
-      deps += [
-        "//components/printing/browser",
-        "//components/printing/renderer",
-      ]
-    }
-    configs += [ ":headless_implementation" ]
-  }
+  configs += [ ":headless_implementation" ]
 }
 
 group("headless_tests") {
@@ -419,7 +393,7 @@
   ]
 
   deps = [
-    ":headless",
+    ":headless_lib",
     "//base/test:run_all_unittests",
     "//base/test:test_support",
     "//testing/gmock",
@@ -513,80 +487,14 @@
   deps = [
     ":embedder_mojo_for_testing",
     ":headless_browser_tests_pak",
-    ":headless_renderer",
     "//base",
     "//content/test:test_support",
+    "//headless:headless_lib",
     "//testing/gmock",
     "//testing/gtest",
   ]
 }
 
-# Headless library with only browser dependencies. This is used when no child
-# dependencies are needed in the target (e.g. chrome:main_dll).
-static_library("headless_shell_browser_lib") {
-  if (is_multi_dll_chrome) {
-    defines = [ "CHROME_MULTIPLE_DLL_BROWSER" ]
-  }
-
-  sources = [
-    "app/headless_shell.cc",
-    "app/headless_shell.h",
-    "app/headless_shell_switches.cc",
-    "app/headless_shell_switches.h",
-    "app/shell_navigation_request.cc",
-    "app/shell_navigation_request.h",
-    "lib/headless_content_main_delegate.cc",
-    "lib/headless_content_main_delegate.h",
-    "public/headless_shell.h",
-  ]
-
-  deps = [
-    ":headless",
-    "//content/public/browser",
-    "//content/public/common",
-  ]
-
-  if (is_win) {
-    deps += [
-      "//content:sandbox_helper_win",
-      "//sandbox",
-    ]
-  }
-
-  configs += [ ":headless_implementation" ]
-}
-
-# Headless library with child specific dependencies (e.g., renderer). This
-# is used when no browser depencendies are needed (e.g. chrome:child_dll).
-static_library("headless_shell_child_lib") {
-  if (is_multi_dll_chrome) {
-    defines = [ "CHROME_MULTIPLE_DLL_CHILD" ]
-  }
-
-  sources = [
-    "app/headless_shell.cc",
-    "app/headless_shell.h",
-    "app/headless_shell_switches.cc",
-    "app/headless_shell_switches.h",
-    "app/shell_navigation_request.cc",
-    "app/shell_navigation_request.h",
-    "lib/headless_content_main_delegate.cc",
-    "lib/headless_content_main_delegate.h",
-    "public/headless_shell.h",
-  ]
-
-  deps = [
-    ":headless_renderer",
-    "//content/public/child:child",
-    "//net",
-    "//ui/base",
-  ]
-
-  configs += [ ":headless_implementation" ]
-}
-
-# Headless library with all included dependencies. Use this library unless you
-# have browser/child dependencies restrictions.
 static_library("headless_shell_lib") {
   sources = [
     "app/headless_shell.cc",
@@ -599,19 +507,10 @@
   ]
 
   deps = [
-    ":headless_renderer",
-    "//content/public/app:both",
-    "//content/public/browser",
-    "//content/public/child:child",
-    "//content/public/common",
+    "//headless:headless_lib",
   ]
 
-  if (is_win) {
-    deps += [
-      "//content:sandbox_helper_win",
-      "//sandbox",
-    ]
-  }
+  configs += [ ":headless_implementation" ]
 }
 
 executable("headless_shell") {
@@ -620,8 +519,18 @@
   ]
 
   deps = [
-    ":headless_shell_lib",
+    "//headless:headless_shell_lib",
   ]
+
+  if (is_win) {
+    deps += [
+      "//build/win:default_exe_manifest",
+      "//content:sandbox_helper_win",
+      "//sandbox",
+    ]
+  }
+
+  configs += [ ":headless_implementation" ]
 }
 
 process_version("version_header") {
@@ -639,6 +548,6 @@
   ]
 
   deps = [
-    ":headless_shell_lib",
+    "//headless:headless_shell_lib",
   ]
 }
diff --git a/headless/DEPS b/headless/DEPS
index 70959d7..2e51ac4e 100644
--- a/headless/DEPS
+++ b/headless/DEPS
@@ -3,7 +3,6 @@
   "+components/crash/content/browser",
   "+content/public/app",
   "+content/public/browser",
-  "+content/public/renderer",
   "+content/public/common",
   "+content/public/test",
   "+mojo/public",
@@ -17,6 +16,5 @@
   "+ui/gfx/geometry",
   "+ui/gl",
   "+ui/ozone/public",
-  "+sandbox/win/src",
   "+services/service_manager/public",
 ]
diff --git a/headless/app/headless_example.cc b/headless/app/headless_example.cc
index d344796b..56932ef 100644
--- a/headless/app/headless_example.cc
+++ b/headless/app/headless_example.cc
@@ -11,7 +11,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
-#include "build/build_config.h"
 #include "headless/public/devtools/domains/page.h"
 #include "headless/public/devtools/domains/runtime.h"
 #include "headless/public/headless_browser.h"
@@ -20,11 +19,6 @@
 #include "headless/public/headless_web_contents.h"
 #include "ui/gfx/geometry/size.h"
 
-#if defined(OS_WIN)
-#include "content/public/app/sandbox_helper_win.h"
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
 // This class contains the main application logic, i.e., waiting for a page to
 // load and printing its DOM. Note that browser initialization happens outside
 // this class.
@@ -164,24 +158,15 @@
 }
 
 int main(int argc, const char** argv) {
-#if !defined(OS_WIN)
   // This function must be the first thing we call to make sure child processes
   // such as the renderer are started properly. The headless library starts
   // child processes by forking and exec'ing the main application.
   headless::RunChildProcessIfNeeded(argc, argv);
-#endif
 
   // Create a headless browser instance. There can be one of these per process
   // and it can only be initialized once.
   headless::HeadlessBrowser::Options::Builder builder(argc, argv);
 
-#if defined(OS_WIN)
-  // In windows, you must initialize and set the sandbox, or pass it along
-  // if it has already been initialized.
-  sandbox::SandboxInterfaceInfo sandbox_info = {0};
-  content::InitializeSandboxInfo(&sandbox_info);
-  builder.SetSandboxInfo(&sandbox_info);
-#endif
   // Here you can customize browser options. As an example we set the window
   // size.
   builder.SetWindowSize(gfx::Size(800, 600));
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index e12d12c..ff670db0 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -18,7 +18,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
-#include "content/public/app/content_main.h"
 #include "headless/app/headless_shell.h"
 #include "headless/app/headless_shell_switches.h"
 #include "headless/public/headless_devtools_target.h"
@@ -29,10 +28,6 @@
 #include "net/http/http_util.h"
 #include "ui/gfx/geometry/size.h"
 
-#if defined(OS_WIN)
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
 namespace headless {
 namespace {
 // Address where to listen to incoming DevTools connections.
@@ -429,10 +424,10 @@
 }
 
 void HeadlessShell::OnFileWritten(const base::FilePath file_name,
-                                  const size_t length,
+                                  const int length,
                                   base::File::Error error_code,
                                   int write_result) {
-  if (write_result < static_cast<int>(length)) {
+  if (write_result < length) {
     // TODO(eseckler): Support recovering from partial writes.
     LOG(ERROR) << "Writing to file " << file_name.value()
                << " was unsuccessful: "
@@ -502,20 +497,11 @@
   return true;
 }
 
-#if defined(OS_WIN)
-int HeadlessShellMain(HINSTANCE instance,
-                      sandbox::SandboxInterfaceInfo* sandbox_info) {
-  base::CommandLine::Init(0, nullptr);
-  HeadlessBrowser::Options::Builder builder(0, nullptr);
-  builder.SetInstance(instance);
-  builder.SetSandboxInfo(std::move(sandbox_info));
-#else
 int HeadlessShellMain(int argc, const char** argv) {
   base::CommandLine::Init(argc, argv);
   RunChildProcessIfNeeded(argc, argv);
-  HeadlessBrowser::Options::Builder builder(argc, argv);
-#endif  // defined(OS_WIN)
   HeadlessShell shell;
+  HeadlessBrowser::Options::Builder builder(argc, argv);
 
   // Enable devtools if requested.
   const base::CommandLine& command_line(
@@ -523,7 +509,7 @@
   if (!ValidateCommandLine(command_line))
     return EXIT_FAILURE;
 
-  if (command_line.HasSwitch(switches::kEnableCrashReporter))
+  if (command_line.HasSwitch(::switches::kEnableCrashReporter))
     builder.SetCrashReporterEnabled(true);
   if (command_line.HasSwitch(switches::kCrashDumpsDir)) {
     builder.SetCrashDumpsDir(
@@ -611,12 +597,4 @@
       base::Bind(&HeadlessShell::OnStart, base::Unretained(&shell)));
 }
 
-int HeadlessShellMain(const content::ContentMainParams& params) {
-#if defined(OS_WIN)
-  return HeadlessShellMain(params.instance, params.sandbox_info);
-#else
-  return HeadlessShellMain(params.argc, params.argv);
-#endif
-}
-
 }  // namespace headless
diff --git a/headless/app/headless_shell.h b/headless/app/headless_shell.h
index e38f017..91535105 100644
--- a/headless/app/headless_shell.h
+++ b/headless/app/headless_shell.h
@@ -86,7 +86,7 @@
                     const base::FilePath file_name,
                     base::File::Error error_code);
   void OnFileWritten(const base::FilePath file_name,
-                     const size_t length,
+                     const int length,
                      base::File::Error error_code,
                      int write_result);
   void OnFileClosed(base::File::Error error_code);
diff --git a/headless/app/headless_shell_main.cc b/headless/app/headless_shell_main.cc
index 5f63fcb..3ae4553 100644
--- a/headless/app/headless_shell_main.cc
+++ b/headless/app/headless_shell_main.cc
@@ -2,20 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "build/build_config.h"
 #include "headless/public/headless_shell.h"
 
-#if defined(OS_WIN)
-#include "content/public/app/sandbox_helper_win.h"
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
 int main(int argc, const char** argv) {
-#if defined(OS_WIN)
-  sandbox::SandboxInterfaceInfo sandbox_info = {0};
-  content::InitializeSandboxInfo(&sandbox_info);
-  return headless::HeadlessShellMain(0, &sandbox_info);
-#else
   return headless::HeadlessShellMain(argc, argv);
-#endif  // defined(OS_WIN)
 }
diff --git a/headless/app/headless_shell_switches.cc b/headless/app/headless_shell_switches.cc
index 45e8a11..86ced81 100644
--- a/headless/app/headless_shell_switches.cc
+++ b/headless/app/headless_shell_switches.cc
@@ -12,9 +12,6 @@
 // transparent.
 const char kDefaultBackgroundColor[] = "default-background-color";
 
-// Enable chrash reporter for headless.
-const char kEnableCrashReporter[] = "enable-crash-reporter";
-
 // The directory breakpad should store minidumps in.
 const char kCrashDumpsDir[] = "crash-dumps-dir";
 
diff --git a/headless/app/headless_shell_switches.h b/headless/app/headless_shell_switches.h
index 69b24b61..dd22903f 100644
--- a/headless/app/headless_shell_switches.h
+++ b/headless/app/headless_shell_switches.h
@@ -11,7 +11,6 @@
 namespace switches {
 
 extern const char kDefaultBackgroundColor[];
-extern const char kEnableCrashReporter[];
 extern const char kCrashDumpsDir[];
 extern const char kDeterministicFetch[];
 extern const char kDumpDom[];
diff --git a/headless/lib/browser/DEPS b/headless/lib/browser/DEPS
index 25bce98..bc59dd2 100644
--- a/headless/lib/browser/DEPS
+++ b/headless/lib/browser/DEPS
@@ -5,6 +5,7 @@
   "+printing",
   "+storage/browser/quota",
   "+ui/aura",
+  "+sandbox/win/src",
 ]
 specific_include_rules = {
   "headless_clipboard.h": [
diff --git a/headless/lib/browser/headless_browser_impl.cc b/headless/lib/browser/headless_browser_impl.cc
index c45408f9..747639c 100644
--- a/headless/lib/browser/headless_browser_impl.cc
+++ b/headless/lib/browser/headless_browser_impl.cc
@@ -27,6 +27,11 @@
 #include "ui/events/devices/device_data_manager.h"
 #include "ui/gfx/geometry/size.h"
 
+#if defined(OS_WIN)
+#include "content/public/app/sandbox_helper_win.h"
+#include "sandbox/win/src/sandbox_types.h"
+#endif
+
 namespace headless {
 namespace {
 
@@ -35,10 +40,9 @@
     const base::Callback<void(HeadlessBrowser*)>& on_browser_start_callback) {
   content::ContentMainParams params(nullptr);
 #if defined(OS_WIN)
-  // Sandbox info has to be set and initialized.
-  CHECK(options.sandbox_info);
-  params.instance = options.instance;
-  params.sandbox_info = std::move(options.sandbox_info);
+  sandbox::SandboxInterfaceInfo sandbox_info = {0};
+  content::InitializeSandboxInfo(&sandbox_info);
+  params.sandbox_info = &sandbox_info;
 #elif !defined(OS_ANDROID)
   params.argc = options.argc;
   params.argv = options.argv;
@@ -196,7 +200,6 @@
   return find_it->second.get();
 }
 
-#if !defined(OS_WIN)
 void RunChildProcessIfNeeded(int argc, const char** argv) {
   base::CommandLine::Init(argc, argv);
   const base::CommandLine& command_line(
@@ -215,7 +218,6 @@
   exit(RunContentMain(builder.Build(),
                       base::Callback<void(HeadlessBrowser*)>()));
 }
-#endif  // !defined(OS_WIN)
 
 int HeadlessBrowserMain(
     HeadlessBrowser::Options options,
diff --git a/headless/lib/headless_browser_browsertest.cc b/headless/lib/headless_browser_browsertest.cc
index b39dce3..8b6e20c7 100644
--- a/headless/lib/headless_browser_browsertest.cc
+++ b/headless/lib/headless_browser_browsertest.cc
@@ -400,7 +400,7 @@
 class ProtocolHandlerWithCookies
     : public net::URLRequestJobFactory::ProtocolHandler {
  public:
-  explicit ProtocolHandlerWithCookies(net::CookieList* sent_cookies);
+  ProtocolHandlerWithCookies(net::CookieList* sent_cookies);
   ~ProtocolHandlerWithCookies() override {}
 
   net::URLRequestJob* MaybeCreateJob(
@@ -634,7 +634,11 @@
 // TODO(skyostil): This test currently relies on being able to run a shell
 // script.
 #if defined(OS_POSIX)
-IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, RendererCommandPrefixTest) {
+#define MAYBE_RendererCommandPrefixTest RendererCommandPrefixTest
+#else
+#define MAYBE_RendererCommandPrefixTest DISABLED_RendererCommandPrefixTest
+#endif  // defined(OS_POSIX)
+IN_PROC_BROWSER_TEST_F(HeadlessBrowserTest, MAYBE_RendererCommandPrefixTest) {
   base::ThreadRestrictions::SetIOAllowed(true);
   base::FilePath launcher_stamp;
   base::CreateTemporaryFile(&launcher_stamp);
@@ -672,7 +676,6 @@
   base::DeleteFile(launcher_script, false);
   base::DeleteFile(launcher_stamp, false);
 }
-#endif  // defined(OS_POSIX)
 
 class CrashReporterTest : public HeadlessBrowserTest,
                           public HeadlessWebContents::Observer,
@@ -683,8 +686,7 @@
 
   void SetUp() override {
     base::ThreadRestrictions::SetIOAllowed(true);
-    base::CreateNewTempDirectory(FILE_PATH_LITERAL("CrashReporterTest"),
-                                 &crash_dumps_dir_);
+    base::CreateNewTempDirectory("CrashReporterTest", &crash_dumps_dir_);
     EXPECT_FALSE(options()->enable_crash_reporter);
     options()->enable_crash_reporter = true;
     options()->crash_dumps_dir = crash_dumps_dir_;
@@ -752,7 +754,7 @@
                             base::FileEnumerator::FILES);
     base::FilePath minidump = it.Next();
     EXPECT_FALSE(minidump.empty());
-    EXPECT_EQ(FILE_PATH_LITERAL(".dmp"), minidump.Extension());
+    EXPECT_EQ(".dmp", minidump.Extension());
     EXPECT_TRUE(it.Next().empty());
   }
 
diff --git a/headless/lib/headless_content_main_delegate.cc b/headless/lib/headless_content_main_delegate.cc
index 8526274a..ee1f655 100644
--- a/headless/lib/headless_content_main_delegate.cc
+++ b/headless/lib/headless_content_main_delegate.cc
@@ -22,6 +22,7 @@
 #include "headless/lib/browser/headless_content_browser_client.h"
 #include "headless/lib/headless_crash_reporter_client.h"
 #include "headless/lib/headless_macros.h"
+#include "headless/lib/renderer/headless_content_renderer_client.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/gfx/switches.h"
@@ -32,10 +33,6 @@
 #include "headless/embedded_resource_pak.h"
 #endif
 
-#if !defined(CHROME_MULTIPLE_DLL_BROWSER)
-#include "headless/lib/renderer/headless_content_renderer_client.h"
-#endif
-
 #if defined(OS_MACOSX)
 #include "components/crash/content/app/crashpad.h"
 #endif
@@ -189,8 +186,10 @@
 #else
   if (command_line.HasSwitch(switches::kEnableLogging))
     InitLogging(command_line);
-#endif  // defined(OS_WIN)
+#endif
+#if !defined(OS_MACOSX)
   InitCrashReporter(command_line);
+#endif  // defined(OS_WIN)
   InitializeResourceBundle();
 }
 
@@ -278,23 +277,15 @@
 
 content::ContentBrowserClient*
 HeadlessContentMainDelegate::CreateContentBrowserClient() {
-#if defined(CHROME_MULTIPLE_DLL_CHILD)
-  return nullptr;
-#else
   browser_client_ =
       base::MakeUnique<HeadlessContentBrowserClient>(browser_.get());
   return browser_client_.get();
-#endif
 }
 
 content::ContentRendererClient*
 HeadlessContentMainDelegate::CreateContentRendererClient() {
-#if defined(CHROME_MULTIPLE_DLL_BROWSER)
-  return nullptr;
-#else
   renderer_client_ = base::MakeUnique<HeadlessContentRendererClient>();
   return renderer_client_.get();
-#endif
 }
 
 }  // namespace headless
diff --git a/headless/lib/headless_content_main_delegate.h b/headless/lib/headless_content_main_delegate.h
index eb8cd61..0aca886 100644
--- a/headless/lib/headless_content_main_delegate.h
+++ b/headless/lib/headless_content_main_delegate.h
@@ -11,8 +11,6 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "content/public/app/content_main_delegate.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/renderer/content_renderer_client.h"
 #include "headless/lib/browser/headless_platform_event_source.h"
 #include "headless/lib/headless_content_client.h"
 #include "headless/public/headless_export.h"
@@ -24,6 +22,8 @@
 namespace headless {
 
 class HeadlessBrowserImpl;
+class HeadlessContentBrowserClient;
+class HeadlessContentRendererClient;
 
 // Exported for tests.
 class HEADLESS_EXPORT HeadlessContentMainDelegate
@@ -57,8 +57,8 @@
 
   static HeadlessContentMainDelegate* GetInstance();
 
-  std::unique_ptr<content::ContentRendererClient> renderer_client_;
-  std::unique_ptr<content::ContentBrowserClient> browser_client_;
+  std::unique_ptr<HeadlessContentBrowserClient> browser_client_;
+  std::unique_ptr<HeadlessContentRendererClient> renderer_client_;
   HeadlessContentClient content_client_;
   HeadlessPlatformEventSource platform_event_source_;
 
diff --git a/headless/public/headless_browser.cc b/headless/public/headless_browser.cc
index 58067c7..64e31a8 100644
--- a/headless/public/headless_browser.cc
+++ b/headless/public/headless_browser.cc
@@ -9,10 +9,6 @@
 #include "content/public/common/user_agent.h"
 #include "headless/public/version.h"
 
-#if defined(OS_WIN)
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
 using Options = headless::HeadlessBrowser::Options;
 using Builder = headless::HeadlessBrowser::Options::Builder;
 
@@ -31,10 +27,6 @@
 Options::Options(int argc, const char** argv)
     : argc(argc),
       argv(argv),
-#if defined(OS_WIN)
-      instance(0),
-      sandbox_info(nullptr),
-#endif
       message_pump(nullptr),
       single_process_mode(false),
       disable_sandbox(false),
@@ -113,18 +105,6 @@
   return *this;
 }
 
-#if defined(OS_WIN)
-Builder& Builder::SetInstance(HINSTANCE instance) {
-  options_.instance = instance;
-  return *this;
-}
-
-Builder& Builder::SetSandboxInfo(sandbox::SandboxInterfaceInfo* sandbox_info) {
-  options_.sandbox_info = sandbox_info;
-  return *this;
-}
-#endif  // defined(OS_WIN)
-
 Builder& Builder::SetUserDataDir(const base::FilePath& user_data_dir) {
   options_.user_data_dir = user_data_dir;
   return *this;
diff --git a/headless/public/headless_browser.h b/headless/public/headless_browser.h
index b57c21a..4b18f56 100644
--- a/headless/public/headless_browser.h
+++ b/headless/public/headless_browser.h
@@ -22,10 +22,6 @@
 #include "net/base/ip_endpoint.h"
 #include "ui/gfx/geometry/size.h"
 
-#if defined(OS_WIN)
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
 namespace base {
 class MessagePump;
 class SingleThreadTaskRunner;
@@ -93,7 +89,7 @@
 };
 
 // Embedding API overrides for the headless browser.
-struct HEADLESS_EXPORT HeadlessBrowser::Options {
+struct HeadlessBrowser::Options {
   class Builder;
 
   Options(Options&& options);
@@ -105,14 +101,6 @@
   int argc;
   const char** argv;
 
-#if defined(OS_WIN)
-  // Set hardware instance if available, otherwise it defaults to 0.
-  HINSTANCE instance;
-
-  // Set with sandbox information. This has to be already initialized.
-  sandbox::SandboxInterfaceInfo* sandbox_info;
-#endif
-
   // Address at which DevTools should listen for connections. Disabled by
   // default.
   net::IPEndPoint devtools_endpoint;
@@ -184,7 +172,7 @@
   DISALLOW_COPY_AND_ASSIGN(Options);
 };
 
-class HEADLESS_EXPORT HeadlessBrowser::Options::Builder {
+class HeadlessBrowser::Options::Builder {
  public:
   Builder(int argc, const char** argv);
   Builder();
@@ -198,10 +186,6 @@
   Builder& SetDisableSandbox(bool disable_sandbox);
   Builder& SetGLImplementation(const std::string& gl_implementation);
   Builder& AddMojoServiceName(const std::string& mojo_service_name);
-#if defined(OS_WIN)
-  Builder& SetInstance(HINSTANCE instance);
-  Builder& SetSandboxInfo(sandbox::SandboxInterfaceInfo* sandbox_info);
-#endif
 
   // Per-context settings.
 
@@ -245,9 +229,7 @@
 //
 // [1]
 // https://chromium.googlesource.com/chromium/src/+/master/docs/linux_zygote.md
-#if !defined(OS_WIN)
-HEADLESS_EXPORT void RunChildProcessIfNeeded(int argc, const char** argv);
-#endif
+void RunChildProcessIfNeeded(int argc, const char** argv);
 
 // Main entry point for running the headless browser. This function constructs
 // the headless browser instance, passing it to the given
@@ -255,7 +237,7 @@
 // the main loop, it will only return after HeadlessBrowser::Shutdown() is
 // called, returning the exit code for the process. It is not possible to
 // initialize the browser again after it has been torn down.
-HEADLESS_EXPORT int HeadlessBrowserMain(
+int HeadlessBrowserMain(
     HeadlessBrowser::Options options,
     const base::Callback<void(HeadlessBrowser*)>& on_browser_start_callback);
 
diff --git a/headless/public/headless_shell.h b/headless/public/headless_shell.h
index 3f1caca2..0fd9066 100644
--- a/headless/public/headless_shell.h
+++ b/headless/public/headless_shell.h
@@ -5,27 +5,13 @@
 #ifndef HEADLESS_PUBLIC_HEADLESS_SHELL_H_
 #define HEADLESS_PUBLIC_HEADLESS_SHELL_H_
 
-#include "content/public/app/content_main.h"
-
-#if defined(OS_WIN)
-#include "sandbox/win/src/sandbox_types.h"
-#endif
+#include "headless/public/headless_export.h"
 
 namespace headless {
 
-// Start the headless shell applications from a |ContentMainParams| object.
-// Note that the |ContentMainDelegate| is ignored and
-// |HeadlessContentMainDelegate| is used instead.
-int HeadlessShellMain(const content::ContentMainParams& params);
-
 // Start the Headless Shell application. Intended to be called early in main().
 // Returns the exit code for the process.
-#if defined(OS_WIN)
-int HeadlessShellMain(HINSTANCE instance,
-                      sandbox::SandboxInterfaceInfo* sandbox_info);
-#else
-int HeadlessShellMain(int argc, const char** argv);
-#endif  // defined(OS_WIN)
+HEADLESS_EXPORT int HeadlessShellMain(int argc, const char** argv);
 }  // namespace headless
 
 #endif  // HEADLESS_PUBLIC_HEADLESS_SHELL_H_
diff --git a/headless/public/util/deterministic_http_protocol_handler.cc b/headless/public/util/deterministic_http_protocol_handler.cc
index db0866a3..1f61f98 100644
--- a/headless/public/util/deterministic_http_protocol_handler.cc
+++ b/headless/public/util/deterministic_http_protocol_handler.cc
@@ -10,7 +10,6 @@
 #include "headless/public/util/deterministic_dispatcher.h"
 #include "headless/public/util/generic_url_request_job.h"
 #include "headless/public/util/http_url_fetcher.h"
-#include "net/http/http_response_headers.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_job_factory_impl.h"
 
diff --git a/headless/public/util/generic_url_request_job.h b/headless/public/util/generic_url_request_job.h
index 679e5ca..8830934e 100644
--- a/headless/public/util/generic_url_request_job.h
+++ b/headless/public/util/generic_url_request_job.h
@@ -14,7 +14,6 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
-#include "headless/public/headless_export.h"
 #include "headless/public/util/managed_dispatch_url_request_job.h"
 #include "headless/public/util/url_fetcher.h"
 #include "net/base/net_errors.h"
@@ -35,7 +34,7 @@
 class URLRequestDispatcher;
 
 // Wrapper around net::URLRequest with helpers to access select metadata.
-class HEADLESS_EXPORT Request {
+class Request {
  public:
   virtual uint64_t GetRequestId() const = 0;
 
@@ -84,7 +83,7 @@
 
 // Details of a pending request received by GenericURLRequestJob which must be
 // either Allowed, Blocked, Modified or have it's response Mocked.
-class HEADLESS_EXPORT PendingRequest {
+class PendingRequest {
  public:
   virtual const Request* GetRequest() const = 0;
 
@@ -129,7 +128,7 @@
       public PendingRequest,
       public Request {
  public:
-  class HEADLESS_EXPORT Delegate {
+  class Delegate {
    public:
     // Notifies the delegate of an PendingRequest which must either be
     // allowed, blocked, modifed or it's response mocked. Called on an arbitrary
diff --git a/headless/public/util/testing/generic_url_request_mocks.cc b/headless/public/util/testing/generic_url_request_mocks.cc
index 54f1ed9..c047e051 100644
--- a/headless/public/util/testing/generic_url_request_mocks.cc
+++ b/headless/public/util/testing/generic_url_request_mocks.cc
@@ -8,7 +8,6 @@
 
 #include "base/logging.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "net/http/http_response_headers.h"
 
 namespace net {
 class URLRequestJob;
diff --git a/headless/test/headless_browser_test.cc b/headless/test/headless_browser_test.cc
index 1ad15d8..39c40d7 100644
--- a/headless/test/headless_browser_test.cc
+++ b/headless/test/headless_browser_test.cc
@@ -211,9 +211,9 @@
   if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION)
     return;
 
+  FAIL() << "Abnormal renderer termination";
   FinishAsynchronousTest();
   render_process_exited_ = true;
-  FAIL() << "Abnormal renderer termination";
 }
 
 void HeadlessAsyncDevTooledBrowserTest::RunTest() {
diff --git a/media/base/video_frame_metadata.h b/media/base/video_frame_metadata.h
index 069bcb28..ce34169 100644
--- a/media/base/video_frame_metadata.h
+++ b/media/base/video_frame_metadata.h
@@ -42,12 +42,6 @@
     // contexts.
     COPY_REQUIRED,
 
-    // Indicates that the frame is owned by the decoder and that destroying the
-    // decoder will make the frame unrenderable. TODO(sandersd): Remove once OSX
-    // and Windows hardware decoders support frames which outlive the decoder.
-    // http://crbug.com/595716 and http://crbug.com/602708.
-    DECODER_OWNS_FRAME,
-
     // Indicates if the current frame is the End of its current Stream. Use
     // Get/SetBoolean() for this Key.
     END_OF_STREAM,
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index b81e151..e1f11db4 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -880,9 +880,7 @@
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("media", "WebMediaPlayerImpl:paint");
 
-  // TODO(sandersd): Move this check into GetCurrentFrameFromCompositor() when
-  // we have other ways to check if decoder owns video frame.
-  // See http://crbug.com/595716 and http://crbug.com/602708
+  // We can't copy from protected frames.
   if (cdm_)
     return;
 
@@ -958,9 +956,7 @@
   DCHECK(main_task_runner_->BelongsToCurrentThread());
   TRACE_EVENT0("media", "WebMediaPlayerImpl:copyVideoTextureToPlatformTexture");
 
-  // TODO(sandersd): Move this check into GetCurrentFrameFromCompositor() when
-  // we have other ways to check if decoder owns video frame.
-  // See http://crbug.com/595716 and http://crbug.com/602708
+  // We can't copy from protected frames.
   if (cdm_)
     return false;
 
diff --git a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
index c2ab234f..ef97261 100644
--- a/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
+++ b/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -43,7 +43,7 @@
  * and their capabilities, using android.hardware.camera2.CameraManager.
  **/
 @JNINamespace("media")
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+@TargetApi(Build.VERSION_CODES.M)
 public class VideoCaptureCamera2 extends VideoCapture {
     // Inner class to extend a CameraDevice state change listener.
     private class CrStateListener extends CameraDevice.StateCallback {
@@ -805,7 +805,9 @@
                 break;
             }
         }
-        // TODO(mcasas): query |cameraCharacteristics| for CONTROL_AE_LOCK_AVAILABLE (API 23)
+        if (cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE)) {
+            exposureModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
+        }
         builder.setExposureModes(integerArrayListToArray(exposureModes));
 
         int jniExposureMode = AndroidMeteringMode.CONTINUOUS;
@@ -838,7 +840,9 @@
                 break;
             }
         }
-        // TODO(mcasas): query |cameraCharacteristics| for CONTROL_AWE_LOCK_AVAILABLE (API 23)
+        if (cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE)) {
+            whiteBalanceModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
+        }
         builder.setWhiteBalanceModes(integerArrayListToArray(whiteBalanceModes));
 
         final int whiteBalanceMode = mPreviewRequest.get(CaptureRequest.CONTROL_AWB_MODE);
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc
index dde9947..e5d592ef 100644
--- a/media/filters/decoder_stream.cc
+++ b/media/filters/decoder_stream.cc
@@ -56,7 +56,7 @@
       decoder_selector_(new DecoderSelector<StreamType>(task_runner,
                                                         std::move(decoders),
                                                         media_log)),
-      decoded_frames_since_fallback_(0),
+      decoder_produced_a_frame_(false),
       decoding_eos_(false),
       pending_decode_requests_(0),
       duration_tracker_(8),
@@ -284,8 +284,6 @@
     DCHECK(decoder_);
   }
 
-  previous_decoder_ = std::move(decoder_);
-  decoded_frames_since_fallback_ = 0;
   decoder_ = std::move(selected_decoder);
   if (decrypting_demuxer_stream) {
     decrypting_demuxer_stream_ = std::move(decrypting_demuxer_stream);
@@ -346,7 +344,7 @@
 
   // We don't know if the decoder will error out on first decode yet. Save the
   // buffer to feed it to the fallback decoder later if needed.
-  if (!decoded_frames_since_fallback_)
+  if (!decoder_produced_a_frame_)
     pending_buffers_.push_back(buffer);
 
   // It's possible for a buffer to arrive from the demuxer right after the
@@ -431,10 +429,10 @@
 
   switch (status) {
     case DecodeStatus::DECODE_ERROR:
-      if (!decoded_frames_since_fallback_) {
+      if (!decoder_produced_a_frame_) {
         pending_decode_requests_ = 0;
 
-        // Prevent all pending decode requests and outputs form those requests
+        // Prevent all pending decode requests and outputs from those requests
         // from being called back.
         fallback_weak_factory_.InvalidateWeakPtrs();
 
@@ -500,10 +498,9 @@
   if (!reset_cb_.is_null())
     return;
 
+  decoder_produced_a_frame_ = true;
   traits_.OnDecodeDone(output);
 
-  ++decoded_frames_since_fallback_;
-
   // |decoder_| sucessfully decoded a frame. No need to keep buffers for a
   // fallback decoder.
   // Note: |fallback_buffers_| might still have buffers, and we will keep
@@ -520,13 +517,6 @@
 
   // Store decoded output.
   ready_outputs_.push_back(output);
-
-  // Destruct any previous decoder once we've decoded enough frames to ensure
-  // that it's no longer in use.
-  if (previous_decoder_ &&
-      decoded_frames_since_fallback_ > limits::kMaxVideoFrames) {
-    previous_decoder_.reset();
-  }
 }
 
 template <DemuxerStream::Type StreamType>
@@ -564,9 +554,7 @@
 
   DCHECK(task_runner_->BelongsToCurrentThread());
   DCHECK(pending_demuxer_read_);
-  if (decoded_frames_since_fallback_) {
-    DCHECK(pending_demuxer_read_ || state_ == STATE_ERROR) << state_;
-  } else {
+  if (!decoder_produced_a_frame_) {
     DCHECK(state_ == STATE_ERROR || state_ == STATE_REINITIALIZING_DECODER ||
            state_ == STATE_NORMAL)
         << state_;
@@ -577,8 +565,7 @@
   // If parallel decode requests are supported, multiple read requests might
   // have been sent to the demuxer. The buffers might arrive while the decoder
   // is reinitializing after falling back on first decode error.
-  if (state_ == STATE_REINITIALIZING_DECODER &&
-      !decoded_frames_since_fallback_) {
+  if (state_ == STATE_REINITIALIZING_DECODER && !decoder_produced_a_frame_) {
     switch (status) {
       case DemuxerStream::kOk:
         // Save valid buffers to be consumed by the new decoder.
diff --git a/media/filters/decoder_stream.h b/media/filters/decoder_stream.h
index e0f26c9..28a11eb 100644
--- a/media/filters/decoder_stream.h
+++ b/media/filters/decoder_stream.h
@@ -112,10 +112,6 @@
     config_change_observer_cb_ = config_change_observer;
   }
 
-  const Decoder* get_previous_decoder_for_testing() const {
-    return previous_decoder_.get();
-  }
-
   int get_pending_buffers_size_for_testing() const {
     return pending_buffers_.size();
   }
@@ -205,12 +201,8 @@
 
   std::unique_ptr<Decoder> decoder_;
 
-  // When falling back from H/W decoding to S/W decoding, destructing the
-  // GpuVideoDecoder too early results in black frames being displayed.
-  // |previous_decoder_| is used to keep it alive.  It is destroyed once we've
-  // decoded at least media::limits::kMaxVideoFrames frames after fallback.
-  int decoded_frames_since_fallback_;
-  std::unique_ptr<Decoder> previous_decoder_;
+  // Whether |decoder_| has produced a frame yet. Reset on fallback.
+  bool decoder_produced_a_frame_;
 
   std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream_;
 
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc
index 3207c34..c36b0e1 100644
--- a/media/filters/gpu_video_decoder.cc
+++ b/media/filters/gpu_video_decoder.cc
@@ -679,9 +679,6 @@
     frame->metadata()->SetBoolean(VideoFrameMetadata::WANTS_PROMOTION_HINT,
                                   true);
   }
-#if defined(OS_WIN)
-  frame->metadata()->SetBoolean(VideoFrameMetadata::DECODER_OWNS_FRAME, true);
-#endif
 
   if (requires_texture_copy_)
     frame->metadata()->SetBoolean(VideoFrameMetadata::COPY_REQUIRED, true);
diff --git a/media/filters/video_frame_stream_unittest.cc b/media/filters/video_frame_stream_unittest.cc
index c82dadd2..0f6752d 100644
--- a/media/filters/video_frame_stream_unittest.cc
+++ b/media/filters/video_frame_stream_unittest.cc
@@ -977,7 +977,6 @@
   EXPECT_EQ(0, video_frame_stream_->get_fallback_buffers_size_for_testing());
   EXPECT_EQ(demuxer_reads_satisfied,
             video_frame_stream_->get_pending_buffers_size_for_testing());
-  EXPECT_EQ(video_frame_stream_->get_previous_decoder_for_testing(), decoder1_);
   EXPECT_TRUE(pending_read_);
 
   // Give the decoder one more buffer, enough to release a frame.
@@ -1083,15 +1082,9 @@
 TEST_P(VideoFrameStreamTest, FallbackDecoderSelectedOnFailureToReinitialize) {
   Initialize();
   decoder1_->SimulateFailureToInit();
-  // Holding decode, because large decoder delays might cause us to get rid of
-  // |previous_decoder_| before we are in a pending state again.
-  decoder2_->HoldDecode();
   ReadUntilDecoderReinitialized(decoder1_);
-  ASSERT_TRUE(video_frame_stream_->get_previous_decoder_for_testing());
-  decoder2_->SatisfyDecode();
-  base::RunLoop().RunUntilIdle();
   ReadAllFrames();
-  ASSERT_FALSE(video_frame_stream_->get_previous_decoder_for_testing());
+  ASSERT_GT(decoder2_->total_bytes_decoded(), 0);
 }
 
 TEST_P(VideoFrameStreamTest,
diff --git a/media/renderers/video_overlay_factory.cc b/media/renderers/video_overlay_factory.cc
index 6bb1f0f..a5a935b56 100644
--- a/media/renderers/video_overlay_factory.cc
+++ b/media/renderers/video_overlay_factory.cc
@@ -107,9 +107,6 @@
       base::TimeDelta());  // timestamp
   CHECK(frame);
   frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY, true);
-  // TODO(halliwell): this is to block idle suspend behaviour on Chromecast.
-  // Find a better way to do this.
-  frame->metadata()->SetBoolean(VideoFrameMetadata::DECODER_OWNS_FRAME, true);
   return frame;
 }
 
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 2fd124c..22b9400 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -3448,6 +3448,9 @@
 # Sheriff failures 2017-04-19
 crbug.com/713094 [ Win ] virtual/sharedarraybuffer/fast/css/fontfaceset-check-platform-fonts.html [ Failure Pass ]
 
+# Sheriff failures 2017-04-24
+crbug.com/714862 [ Android ] tables/mozilla/collapsing_borders/bug41262-3.html [ Failure ]
+
 # Tests to be run only under virtual/experimental-canvas-features/
 crbug.com/682753 fast/canvas-experimental [ Skip ]
 
diff --git a/third_party/WebKit/LayoutTests/fragmentation/forced-break-inside-float.html b/third_party/WebKit/LayoutTests/fragmentation/forced-break-inside-float.html
new file mode 100644
index 0000000..9b1f20e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fragmentation/forced-break-inside-float.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<style>
+    .part { height:30px; background:blue; }
+</style>
+<p>There should be a blue square below.</p>
+<div style="columns:3; column-fill:auto; column-gap:0; width:30px; height:80px; line-height:20px;">
+    <div style="position:relative;">
+        <div style="float:left; width:100%;">
+            <div id="elm1" class="part"></div>
+            <div id="elm2" class="part" style="break-before:column; break-after:column;"></div>
+            <div id="elm3" class="part"></div>
+        </div>
+    </div>
+</div>
+
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script>
+test(() => {
+    assert_equals(document.getElementById("elm1").offsetTop, 0);
+    assert_equals(document.getElementById("elm2").offsetTop, 80);
+    assert_equals(document.getElementById("elm3").offsetTop, 160);
+}, "Forced breaks should be allowed inside floats");
+</script>
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 0774e23..d4bd0cf3 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -4552,7 +4552,8 @@
     AddListenerType(listener_type);
 }
 
-void Document::AddListenerTypeIfNeeded(const AtomicString& event_type) {
+void Document::AddListenerTypeIfNeeded(const AtomicString& event_type,
+                                       EventTarget& event_target) {
   if (event_type == EventTypeNames::DOMSubtreeModified) {
     UseCounter::Count(*this, UseCounter::kDOMSubtreeModifiedEvent);
     AddMutationEventListenerTypeIfEnabled(DOMSUBTREEMODIFIED_LISTENER);
@@ -4589,6 +4590,15 @@
     AddListenerType(TRANSITIONEND_LISTENER);
   } else if (event_type == EventTypeNames::scroll) {
     AddListenerType(SCROLL_LISTENER);
+  } else if (event_type == EventTypeNames::load) {
+    if (Node* node = event_target.ToNode()) {
+      if (isHTMLStyleElement(*node)) {
+        AddListenerType(LOAD_LISTENER_AT_CAPTURE_PHASE_OR_AT_STYLE_ELEMENT);
+        return;
+      }
+    }
+    if (event_target.HasCapturingEventListeners(event_type))
+      AddListenerType(LOAD_LISTENER_AT_CAPTURE_PHASE_OR_AT_STYLE_ELEMENT);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index 9da3667..14066c98 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -794,14 +794,15 @@
     ANIMATIONSTART_LISTENER = 1 << 7,
     ANIMATIONITERATION_LISTENER = 1 << 8,
     TRANSITIONEND_LISTENER = 1 << 9,
-    SCROLL_LISTENER = 1 << 10
-    // 5 bits remaining
+    SCROLL_LISTENER = 1 << 10,
+    LOAD_LISTENER_AT_CAPTURE_PHASE_OR_AT_STYLE_ELEMENT = 1 << 11
+    // 4 bits remaining
   };
 
   bool HasListenerType(ListenerType listener_type) const {
     return (listener_types_ & listener_type);
   }
-  void AddListenerTypeIfNeeded(const AtomicString& event_type);
+  void AddListenerTypeIfNeeded(const AtomicString& event_type, EventTarget&);
 
   bool HasMutationObserversOfType(MutationObserver::MutationType type) const {
     return mutation_observer_types_ & type;
diff --git a/third_party/WebKit/Source/core/dom/Node.cpp b/third_party/WebKit/Source/core/dom/Node.cpp
index d70c2af2..14d5b5e 100644
--- a/third_party/WebKit/Source/core/dom/Node.cpp
+++ b/third_party/WebKit/Source/core/dom/Node.cpp
@@ -1867,7 +1867,7 @@
         event_target_data->event_listener_map;
     if (!listener_map.IsEmpty()) {
       for (const auto& type : listener_map.EventTypes())
-        GetDocument().AddListenerTypeIfNeeded(type);
+        GetDocument().AddListenerTypeIfNeeded(type, *this);
     }
   }
 
@@ -1894,7 +1894,7 @@
 void Node::AddedEventListener(const AtomicString& event_type,
                               RegisteredEventListener& registered_listener) {
   EventTarget::AddedEventListener(event_type, registered_listener);
-  GetDocument().AddListenerTypeIfNeeded(event_type);
+  GetDocument().AddListenerTypeIfNeeded(event_type, *this);
   if (Page* page = GetDocument().GetPage())
     page->GetEventHandlerRegistry().DidAddEventHandler(
         *this, event_type, registered_listener.Options());
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
index cdb98a3..0a20380 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
@@ -491,17 +491,16 @@
     const Vector<String>& words) {
   for (auto& node_markers : markers_) {
     const Node& node = *node_markers.key;
-    if (!node.IsTextNode())  // MarkerRemoverPredicate requires a Text node.
+    if (!node.IsTextNode())
       continue;
     MarkerLists* markers = node_markers.value;
-    for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
+    for (DocumentMarker::MarkerType type :
+         DocumentMarker::MisspellingMarkers()) {
       MarkerList* list = ListForType(markers, type);
       if (!list)
         continue;
-      bool removed_markers = DocumentMarkerListEditor::RemoveMarkersUnderWords(
+      DocumentMarkerListEditor::RemoveMarkersUnderWords(
           list, ToText(node).data(), words);
-      if (removed_markers && type == DocumentMarker::kTextMatch)
-        InvalidatePaintForTickmarks(node);
     }
   }
 }
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerControllerTest.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerControllerTest.cpp
index 16646d47..931f6e7 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerControllerTest.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerControllerTest.cpp
@@ -324,4 +324,28 @@
   EXPECT_EQ(0u, MarkerController().Markers().size());
 }
 
+TEST_F(DocumentMarkerControllerTest, RemoveSpellingMarkersUnderWords) {
+  SetBodyInnerHTML("<div contenteditable>foo</div>");
+  GetDocument().UpdateStyleAndLayout();
+  Element* div = GetDocument().QuerySelector("div");
+  Node* text = div->firstChild();
+
+  // Add a spelling marker and a text match marker to "foo".
+  const EphemeralRange marker_range(Position(text, 0), Position(text, 3));
+  MarkerController().AddMarker(marker_range.StartPosition(),
+                               marker_range.EndPosition(),
+                               DocumentMarker::kSpelling, "");
+  MarkerController().AddTextMatchMarker(marker_range,
+                                        DocumentMarker::MatchStatus::kInactive);
+
+  MarkerController().RemoveSpellingMarkersUnderWords({"foo"});
+
+  // RemoveSpellingMarkersUnderWords does not remove text match marker.
+  ASSERT_EQ(1u, MarkerController().Markers().size());
+  const DocumentMarker& marker = *MarkerController().Markers()[0];
+  EXPECT_EQ(0u, marker.StartOffset());
+  EXPECT_EQ(3u, marker.EndOffset());
+  EXPECT_EQ(DocumentMarker::kTextMatch, marker.GetType());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
index 7fdceb5..8a160dc4 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
@@ -1442,7 +1442,7 @@
         *this, event_type, registered_listener.Options());
 
   if (Document* document = this->document())
-    document->AddListenerTypeIfNeeded(event_type);
+    document->AddListenerTypeIfNeeded(event_type, *this);
 
   for (auto& it : event_listener_observers_) {
     it->DidAddEventListener(this, event_type);
diff --git a/third_party/WebKit/Source/core/html/HTMLStyleElement.cpp b/third_party/WebKit/Source/core/html/HTMLStyleElement.cpp
index 349287a..c2ca811 100644
--- a/third_party/WebKit/Source/core/html/HTMLStyleElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLStyleElement.cpp
@@ -107,9 +107,13 @@
 
 void HTMLStyleElement::DispatchPendingEvent(
     std::unique_ptr<IncrementLoadEventDelayCount> count) {
-  DispatchEvent(Event::Create(loaded_sheet_ ? EventTypeNames::load
-                                            : EventTypeNames::error));
-
+  if (loaded_sheet_) {
+    if (GetDocument().HasListenerType(
+            Document::LOAD_LISTENER_AT_CAPTURE_PHASE_OR_AT_STYLE_ELEMENT))
+      DispatchEvent(Event::Create(EventTypeNames::load));
+  } else {
+    DispatchEvent(Event::Create(EventTypeNames::error));
+  }
   // Checks Document's load event synchronously here for performance.
   // This is safe because dispatchPendingEvent() is called asynchronously.
   count->ClearAndCheckLoadEvent();
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index 92f2ff0..6f2ceee 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -2272,7 +2272,7 @@
       // If this is a flow thread for a multicol container, and we have a break
       // value for paged, we need to keep looking.
     }
-    if (curr->IsFloatingOrOutOfFlowPositioned())
+    if (curr->IsOutOfFlowPositioned())
       return false;
     curr = curr->ContainingBlock();
   }
diff --git a/tools/perf/benchmarks/tab_switching.py b/tools/perf/benchmarks/tab_switching.py
index a451fec..b1cf856 100644
--- a/tools/perf/benchmarks/tab_switching.py
+++ b/tools/perf/benchmarks/tab_switching.py
@@ -2,11 +2,14 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import os
+
 from core import perf_benchmark
 
 from measurements import tab_switching
-import page_sets
+from page_sets.system_health import multi_tab_stories
 from telemetry import benchmark
+from telemetry import story
 
 
 @benchmark.Owner(emails=['vovoy@chromium.org'],
@@ -24,9 +27,19 @@
   """
   test = tab_switching.TabSwitching
 
+  @classmethod
+  def AddBenchmarkCommandLineArgs(cls, parser):
+    parser.add_option('--tabset-repeat', type='int', default=1,
+                      help='repeat tab page set')
+
   def CreateStorySet(self, options):
-    return page_sets.SystemHealthStorySet(platform='desktop',
-                                          case='multitab:misc')
+    story_set = story.StorySet(
+        archive_data_file='../page_sets/data/system_health_desktop.json',
+        base_dir=os.path.dirname(os.path.abspath(__file__)),
+        cloud_storage_bucket=story.PARTNER_BUCKET)
+    story_set.AddStory(multi_tab_stories.MultiTabTypical24Story(
+        story_set, False, options.tabset_repeat))
+    return story_set
 
   @classmethod
   def Name(cls):
diff --git a/tools/perf/page_sets/system_health/browsing_stories.py b/tools/perf/page_sets/system_health/browsing_stories.py
index c37a5a9..d47f2ea 100644
--- a/tools/perf/page_sets/system_health/browsing_stories.py
+++ b/tools/perf/page_sets/system_health/browsing_stories.py
@@ -565,7 +565,7 @@
   SUPPORTED_PLATFORMS = platforms.MOBILE_ONLY
   TAGS = [story_tags.EMERGING_MARKET]
 
-  ITEMS_TO_VISIT = 4
+  ITEMS_TO_VISIT = 3  # 4 links causes renderer OOM crbug.com/714650.
   ITEM_SELECTOR = '.hui-premium__title'
   COMPLETE_STATE_WAIT_TIMEOUT = 150
 
diff --git a/tools/perf/page_sets/system_health/multi_tab_stories.py b/tools/perf/page_sets/system_health/multi_tab_stories.py
index a9ab0c7..fef8486 100644
--- a/tools/perf/page_sets/system_health/multi_tab_stories.py
+++ b/tools/perf/page_sets/system_health/multi_tab_stories.py
@@ -15,18 +15,23 @@
 class MultiTabStory(system_health_story.SystemHealthStory):
   ABSTRACT_STORY = True
 
+  def __init__(self, story_set, take_memory_measurement, tabset_repeat=1):
+    super(MultiTabStory, self).__init__(story_set, take_memory_measurement)
+    self._tabset_repeat = tabset_repeat
+
   def RunNavigateSteps(self, action_runner):
     tabs = action_runner.tab.browser.tabs
 
     # No need to create the first tab as there is already one
     # when the browser is ready,
-    if self.URL_LIST:
-      action_runner.Navigate(self.URL_LIST[0])
-    for url in self.URL_LIST[1:]:
+    url_list = self.URL_LIST * self._tabset_repeat
+    if url_list:
+      action_runner.Navigate(url_list[0])
+    for url in url_list[1:]:
       new_tab = tabs.New()
       new_tab.action_runner.Navigate(url)
 
-    for i, url in enumerate(self.URL_LIST):
+    for i, url in enumerate(url_list):
       try:
         tabs[i].action_runner.WaitForNetworkQuiescence()
       except py_utils.TimeoutException:
diff --git a/ui/message_center/views/message_bubble_base.cc b/ui/message_center/views/message_bubble_base.cc
index 43915d1..4089234 100644
--- a/ui/message_center/views/message_bubble_base.cc
+++ b/ui/message_center/views/message_bubble_base.cc
@@ -21,9 +21,6 @@
 
 namespace message_center {
 
-const SkColor MessageBubbleBase::kBackgroundColor =
-    SkColorSetRGB(0xfe, 0xfe, 0xfe);
-
 MessageBubbleBase::MessageBubbleBase(MessageCenter* message_center,
                                      MessageCenterTray* tray)
     : message_center_(message_center),
diff --git a/ui/message_center/views/message_bubble_base.h b/ui/message_center/views/message_bubble_base.h
index 65bdf5a..0c9718a 100644
--- a/ui/message_center/views/message_bubble_base.h
+++ b/ui/message_center/views/message_bubble_base.h
@@ -57,8 +57,6 @@
 
   views::TrayBubbleView* bubble_view() const { return bubble_view_; }
 
-  static const SkColor kBackgroundColor;
-
  protected:
   views::TrayBubbleView::InitParams GetDefaultInitParams(
       views::TrayBubbleView::AnchorAlignment anchor_alignment);
diff --git a/ui/views/background.cc b/ui/views/background.cc
index 190a5a6..e036490a 100644
--- a/ui/views/background.cc
+++ b/ui/views/background.cc
@@ -6,11 +6,14 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/scoped_observer.h"
 #include "build/build_config.h"
 #include "ui/gfx/canvas.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/views/painter.h"
 #include "ui/views/view.h"
+#include "ui/views/view_observer.h"
 
 #if defined(OS_WIN)
 #include "skia/ext/skia_utils_win.h"
@@ -36,6 +39,32 @@
   DISALLOW_COPY_AND_ASSIGN(SolidBackground);
 };
 
+// ThemedSolidBackground is a solid background that stays in sync with a view's
+// native theme.
+class ThemedSolidBackground : public SolidBackground, public ViewObserver {
+ public:
+  explicit ThemedSolidBackground(View* view, ui::NativeTheme::ColorId color_id)
+      : SolidBackground(gfx::kPlaceholderColor),
+        observer_(this),
+        color_id_(color_id) {
+    observer_.Add(view);
+    OnViewNativeThemeChanged(view);
+  }
+  ~ThemedSolidBackground() override {}
+
+  // ViewObserver:
+  void OnViewNativeThemeChanged(View* view) override {
+    SetNativeControlColor(view->GetNativeTheme()->GetSystemColor(color_id_));
+    view->SchedulePaint();
+  }
+
+ private:
+  ScopedObserver<View, ViewObserver> observer_;
+  ui::NativeTheme::ColorId color_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThemedSolidBackground);
+};
+
 class BackgroundPainter : public Background {
  public:
   explicit BackgroundPainter(std::unique_ptr<Painter> painter)
@@ -73,6 +102,13 @@
 }
 
 // static
+Background* Background::CreateThemedSolidBackground(
+    View* view,
+    ui::NativeTheme::ColorId color_id) {
+  return new ThemedSolidBackground(view, color_id);
+}
+
+// static
 Background* Background::CreateStandardPanelBackground() {
   // TODO(beng): Should be in NativeTheme.
   return CreateSolidBackground(SK_ColorWHITE);
diff --git a/ui/views/background.h b/ui/views/background.h
index 6648689d..81bb675 100644
--- a/ui/views/background.h
+++ b/ui/views/background.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/native_theme/native_theme.h"
 #include "ui/views/views_export.h"
 
 #if defined(OS_WIN)
@@ -47,6 +48,12 @@
   // Creates a background that fills the canvas in the specified color.
   static Background* CreateSolidBackground(SkColor color);
 
+  // Creates a background that fills the canvas in the color specified by the
+  // view's NativeTheme and the given color identifier.
+  static Background* CreateThemedSolidBackground(
+      View* view,
+      ui::NativeTheme::ColorId color_id);
+
   // Creates a background that fills the canvas in the specified color.
   static Background* CreateSolidBackground(int r, int g, int b) {
     return CreateSolidBackground(SkColorSetRGB(r, g, b));
diff --git a/ui/views/bubble/tray_bubble_view.cc b/ui/views/bubble/tray_bubble_view.cc
index 3d7a93e..e1cd285 100644
--- a/ui/views/bubble/tray_bubble_view.cc
+++ b/ui/views/bubble/tray_bubble_view.cc
@@ -175,8 +175,7 @@
       max_width(max_width),
       max_height(0),
       can_activate(false),
-      close_on_deactivate(true),
-      bg_color(gfx::kPlaceholderColor) {}
+      close_on_deactivate(true) {}
 
 TrayBubbleView::InitParams::InitParams(const InitParams& other) = default;
 
@@ -196,12 +195,14 @@
       layout_(new BottomAlignedBoxLayout(this)),
       delegate_(delegate),
       preferred_width_(init_params.min_width),
-      bubble_border_(new BubbleBorder(arrow(),
-                                      BubbleBorder::NO_ASSETS,
-                                      init_params.bg_color)),
+      bubble_border_(new BubbleBorder(
+          arrow(),
+          BubbleBorder::NO_ASSETS,
+          init_params.bg_color.value_or(gfx::kPlaceholderColor))),
       owned_bubble_border_(bubble_border_),
       is_gesture_dragging_(false),
       mouse_actively_entered_(false) {
+  bubble_border_->set_use_theme_background_color(!init_params.bg_color);
   bubble_border_->set_alignment(BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
   bubble_border_->set_paint_arrow(BubbleBorder::PAINT_NONE);
   set_can_activate(params_.can_activate);
diff --git a/ui/views/bubble/tray_bubble_view.h b/ui/views/bubble/tray_bubble_view.h
index 8de9c30..12b9bad 100644
--- a/ui/views/bubble/tray_bubble_view.h
+++ b/ui/views/bubble/tray_bubble_view.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "ui/views/bubble/bubble_dialog_delegate.h"
 #include "ui/views/mouse_watcher.h"
 #include "ui/views/views_export.h"
@@ -83,7 +84,8 @@
     int max_height;
     bool can_activate;
     bool close_on_deactivate;
-    SkColor bg_color;
+    // If not provided, the bg color will be derived from the NativeTheme.
+    base::Optional<SkColor> bg_color;
   };
 
   // Constructs and returns a TrayBubbleView. |init_params| may be modified.
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 27eada4..b39da49 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -2136,6 +2136,8 @@
       child->PropagateNativeThemeChanged(theme);
   }
   OnNativeThemeChanged(theme);
+  for (ViewObserver& observer : observers_)
+    observer.OnViewNativeThemeChanged(this);
 }
 
 // Size and disposition --------------------------------------------------------
diff --git a/ui/views/view_observer.h b/ui/views/view_observer.h
index c4f56791..f4285a9 100644
--- a/ui/views/view_observer.h
+++ b/ui/views/view_observer.h
@@ -35,6 +35,9 @@
   // View::ReorderChildView() is called on |observed_view|.
   virtual void OnChildViewReordered(View* observed_view, View* child) {}
 
+  // Called when the active NativeTheme has changed for |observed_view|.
+  virtual void OnViewNativeThemeChanged(View* observed_view) {}
+
   // Called from ~View.
   virtual void OnViewIsDeleting(View* observed_view) {}