diff --git a/base/BUILD.gn b/base/BUILD.gn
index 21c303d..59b3207 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2560,6 +2560,7 @@
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumInstrumentationTestRunner.java",
       "test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java",
       "test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java",
+      "test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java",
       "test/android/javatests/src/org/chromium/base/test/BaseTestResult.java",
       "test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java",
       "test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java",
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
new file mode 100644
index 0000000..fcd60869
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
@@ -0,0 +1,25 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.test;
+
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnitRunner;
+
+import org.chromium.base.multidex.ChromiumMultiDexInstaller;
+
+/**
+ * A custom AndroidJUnitRunner that supports multidex installer.
+ *
+ * This class is the equivalent of BaseChromiumInstrumentationTestRunner in JUnit3. Please
+ * beware that is this not a class runner. It is declared in test apk AndroidManifest.xml
+ * <instrumentation>
+ */
+public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner {
+    @Override
+    public void onCreate(Bundle arguments) {
+        ChromiumMultiDexInstaller.install(getTargetContext());
+        super.onCreate(arguments);
+    }
+}
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen_view.h b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen_view.h
index 3259790..72b475e1 100644
--- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen_view.h
+++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen_view.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_ENROLLMENT_AUTO_ENROLLMENT_CHECK_SCREEN_VIEW_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_ENROLLMENT_AUTO_ENROLLMENT_CHECK_SCREEN_VIEW_H_
 
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+
 namespace chromeos {
 
 // Interface between auto-enrollment check screen and its representation.
@@ -21,6 +23,9 @@
     virtual void OnViewDestroyed(AutoEnrollmentCheckScreenView* view) = 0;
   };
 
+  constexpr static OobeScreen kScreenId =
+      OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK;
+
   virtual ~AutoEnrollmentCheckScreenView() {}
 
   virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h b/chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h
index 9b43849..cebbbc34 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 class GoogleServiceAuthError;
 
@@ -37,6 +38,8 @@
                                            const std::string& location) = 0;
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_OOBE_ENROLLMENT;
+
   virtual ~EnrollmentScreenView() {}
 
   // Initializes the view with parameters.
diff --git a/chrome/browser/chromeos/login/screens/app_launch_splash_screen_view.h b/chrome/browser/chromeos/login/screens/app_launch_splash_screen_view.h
index d7dcfe8..9f8b104 100644
--- a/chrome/browser/chromeos/login/screens/app_launch_splash_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/app_launch_splash_screen_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_APP_LAUNCH_SPLASH_SCREEN_VIEW_H_
 
 #include "base/strings/string16.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -41,6 +42,8 @@
     virtual ~Delegate() {}
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_APP_LAUNCH_SPLASH;
+
   virtual ~AppLaunchSplashScreenView() {}
 
   // Sets screen this view belongs to.
diff --git a/chrome/browser/chromeos/login/screens/arc_kiosk_splash_screen_view.h b/chrome/browser/chromeos/login/screens/arc_kiosk_splash_screen_view.h
index 3e96825..e3fc63f 100644
--- a/chrome/browser/chromeos/login/screens/arc_kiosk_splash_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/arc_kiosk_splash_screen_view.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_KIOSK_SPLASH_SCREEN_VIEW_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ARC_KIOSK_SPLASH_SCREEN_VIEW_H_
 
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+
 namespace chromeos {
 
 // Interface for UI implemenations of the ArcKioskSplashScreen.
@@ -29,6 +31,8 @@
     DISALLOW_COPY_AND_ASSIGN(Delegate);
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_ARC_KIOSK_SPLASH;
+
   ArcKioskSplashScreenView() = default;
 
   virtual ~ArcKioskSplashScreenView() = default;
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_view.h b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_view.h
index 7b75f78..68be57c 100644
--- a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen_view.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -17,6 +18,9 @@
 // WebUI representation.
 class ArcTermsOfServiceScreenView {
  public:
+  constexpr static OobeScreen kScreenId =
+      OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE;
+
   virtual ~ArcTermsOfServiceScreenView() = default;
 
   // Adds/Removes observer for view.
diff --git a/chrome/browser/chromeos/login/screens/base_screen.h b/chrome/browser/chromeos/login/screens/base_screen.h
index 8b69691..633021a 100644
--- a/chrome/browser/chromeos/login/screens/base_screen.h
+++ b/chrome/browser/chromeos/login/screens/base_screen.h
@@ -131,7 +131,7 @@
   FRIEND_TEST_ALL_PREFIXES(HandsOffNetworkScreenTest, RequiresNoInput);
   FRIEND_TEST_ALL_PREFIXES(HandsOffNetworkScreenTest, ContinueClickedOnlyOnce);
 
-  friend class BaseScreenHandler;
+  friend class BaseWebUIHandler;
   friend class NetworkScreenTest;
   friend class ScreenEditor;
   friend class ScreenManager;
diff --git a/chrome/browser/chromeos/login/screens/controller_pairing_screen_view.h b/chrome/browser/chromeos/login/screens/controller_pairing_screen_view.h
index 739d4a6..df482a6b 100644
--- a/chrome/browser/chromeos/login/screens/controller_pairing_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/controller_pairing_screen_view.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/macros.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace base {
 class DictionaryValue;
@@ -69,6 +70,9 @@
     virtual void OnUserActed(const std::string& action) = 0;
   };
 
+  constexpr static OobeScreen kScreenId =
+      OobeScreen::SCREEN_OOBE_CONTROLLER_PAIRING;
+
   ControllerPairingScreenView();
   virtual ~ControllerPairingScreenView();
 
diff --git a/chrome/browser/chromeos/login/screens/device_disabled_screen_view.h b/chrome/browser/chromeos/login/screens/device_disabled_screen_view.h
index bd063d7..5a1260d 100644
--- a/chrome/browser/chromeos/login/screens/device_disabled_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/device_disabled_screen_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_DEVICE_DISABLED_SCREEN_VIEW_H_
 
 #include <string>
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -28,6 +29,8 @@
     virtual const std::string& GetMessage() const = 0;
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_DEVICE_DISABLED;
+
   virtual ~DeviceDisabledScreenView() {}
 
   virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/screens/enable_debugging_screen_view.h b/chrome/browser/chromeos/login/screens/enable_debugging_screen_view.h
index 664fbae..821c33ee 100644
--- a/chrome/browser/chromeos/login/screens/enable_debugging_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/enable_debugging_screen_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_ENABLE_DEBUGGING_SCREEN_VIEW_H_
 
 #include <string>
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -26,6 +27,9 @@
     virtual void OnViewDestroyed(EnableDebuggingScreenView* view) = 0;
   };
 
+  constexpr static OobeScreen kScreenId =
+      OobeScreen::SCREEN_OOBE_ENABLE_DEBUGGING;
+
   virtual ~EnableDebuggingScreenView() {}
 
   virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/screens/eula_view.h b/chrome/browser/chromeos/login/screens/eula_view.h
index 08c0da0f..383f5de8 100644
--- a/chrome/browser/chromeos/login/screens/eula_view.h
+++ b/chrome/browser/chromeos/login/screens/eula_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_EULA_VIEW_H_
 
 #include <string>
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -16,6 +17,8 @@
 // dtor.
 class EulaView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_OOBE_EULA;
+
   virtual ~EulaView() {}
 
   virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/screens/gaia_view.h b/chrome/browser/chromeos/login/screens/gaia_view.h
index 04af20e..28532c2 100644
--- a/chrome/browser/chromeos/login/screens/gaia_view.h
+++ b/chrome/browser/chromeos/login/screens/gaia_view.h
@@ -10,11 +10,14 @@
 #include "base/bind.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
 class GaiaView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_GAIA_SIGNIN;
+
   GaiaView() = default;
   virtual ~GaiaView() = default;
 
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_view.h b/chrome/browser/chromeos/login/screens/hid_detection_view.h
index 34a19c8..03fa090 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_view.h
+++ b/chrome/browser/chromeos/login/screens/hid_detection_view.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -18,6 +19,8 @@
 // dtor.
 class HIDDetectionView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_OOBE_HID_DETECTION;
+
   virtual ~HIDDetectionView() {}
 
   virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/screens/host_pairing_screen_view.h b/chrome/browser/chromeos/login/screens/host_pairing_screen_view.h
index 64ded65..2958bba3 100644
--- a/chrome/browser/chromeos/login/screens/host_pairing_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/host_pairing_screen_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_HOST_PAIRING_SCREEN_VIEW_H_
 
 #include "base/macros.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace base {
 class DictionaryValue;
@@ -47,6 +48,8 @@
     virtual void OnViewDestroyed(HostPairingScreenView* view) = 0;
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_OOBE_HOST_PAIRING;
+
   HostPairingScreenView();
   virtual ~HostPairingScreenView();
 
diff --git a/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen_view.h b/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen_view.h
index c45487d..58a07b2 100644
--- a/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_KIOSK_AUTOLAUNCH_SCREEN_VIEW_H_
 
 #include <string>
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -26,6 +27,8 @@
     virtual void OnViewDestroyed(KioskAutolaunchScreenView* view) = 0;
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_KIOSK_AUTOLAUNCH;
+
   virtual ~KioskAutolaunchScreenView() {}
 
   virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/screens/kiosk_enable_screen_view.h b/chrome/browser/chromeos/login/screens/kiosk_enable_screen_view.h
index f4304dd..2dbaad3 100644
--- a/chrome/browser/chromeos/login/screens/kiosk_enable_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/kiosk_enable_screen_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_KIOSK_ENABLE_SCREEN_VIEW_H_
 
 #include <string>
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -26,6 +27,8 @@
     virtual void OnViewDestroyed(KioskEnableScreenView* view) = 0;
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_KIOSK_ENABLE;
+
   virtual ~KioskEnableScreenView() {}
 
   virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/screens/network_error_view.h b/chrome/browser/chromeos/login/screens/network_error_view.h
index b58e3e3..d81175b 100644
--- a/chrome/browser/chromeos/login/screens/network_error_view.h
+++ b/chrome/browser/chromeos/login/screens/network_error_view.h
@@ -16,6 +16,8 @@
 // representation. Owned by ErrorScreen.
 class NetworkErrorView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_ERROR_MESSAGE;
+
   virtual ~NetworkErrorView() {}
 
   // Shows the contents of the screen.
diff --git a/chrome/browser/chromeos/login/screens/network_view.h b/chrome/browser/chromeos/login/screens/network_view.h
index a27c969b5..6742989 100644
--- a/chrome/browser/chromeos/login/screens/network_view.h
+++ b/chrome/browser/chromeos/login/screens/network_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_NETWORK_VIEW_H_
 
 #include "base/strings/string16.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -15,6 +16,8 @@
 // representation, either views based or WebUI. Owned by NetworkScreen.
 class NetworkView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_OOBE_NETWORK;
+
   virtual ~NetworkView() {}
 
   // Shows the contents of the screen.
diff --git a/chrome/browser/chromeos/login/screens/reset_view.h b/chrome/browser/chromeos/login/screens/reset_view.h
index 7570c04..22c3dcc 100644
--- a/chrome/browser/chromeos/login/screens/reset_view.h
+++ b/chrome/browser/chromeos/login/screens/reset_view.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_RESET_VIEW_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_RESET_VIEW_H_
 
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+
 namespace chromeos {
 
 class ResetScreen;
@@ -13,6 +15,8 @@
 // representation, either views based or WebUI.
 class ResetView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_OOBE_RESET;
+
   virtual ~ResetView() {}
 
   virtual void Bind(ResetScreen* screen) = 0;
diff --git a/chrome/browser/chromeos/login/screens/terms_of_service_screen_view.h b/chrome/browser/chromeos/login/screens/terms_of_service_screen_view.h
index dcd3ee4..fce5dc97 100644
--- a/chrome/browser/chromeos/login/screens/terms_of_service_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/terms_of_service_screen_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_TERMS_OF_SERVICE_SCREEN_VIEW_H_
 
 #include <string>
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -27,6 +28,8 @@
     virtual void OnViewDestroyed(TermsOfServiceScreenView* view) = 0;
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_TERMS_OF_SERVICE;
+
   virtual ~TermsOfServiceScreenView() {}
 
   // Sets screen this view belongs to.
diff --git a/chrome/browser/chromeos/login/screens/update_view.h b/chrome/browser/chromeos/login/screens/update_view.h
index 17de164..3d273f6 100644
--- a/chrome/browser/chromeos/login/screens/update_view.h
+++ b/chrome/browser/chromeos/login/screens/update_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_UPDATE_VIEW_H_
 
 #include "base/strings/string16.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -15,6 +16,8 @@
 // representation. Owned by UpdateScreen.
 class UpdateView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_OOBE_UPDATE;
+
   virtual ~UpdateView() {}
 
   // Shows the contents of the screen.
diff --git a/chrome/browser/chromeos/login/screens/user_image_view.h b/chrome/browser/chromeos/login/screens/user_image_view.h
index d14ce6b..3464a10 100644
--- a/chrome/browser/chromeos/login/screens/user_image_view.h
+++ b/chrome/browser/chromeos/login/screens/user_image_view.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_USER_IMAGE_VIEW_H_
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_USER_IMAGE_VIEW_H_
 
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+
 namespace chromeos {
 
 class UserImageScreen;
@@ -13,6 +15,8 @@
 // representation, either views based or WebUI.
 class UserImageView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_USER_IMAGE_PICKER;
+
   virtual ~UserImageView() {}
 
   virtual void Bind(UserImageScreen* screen) = 0;
diff --git a/chrome/browser/chromeos/login/screens/wrong_hwid_screen_view.h b/chrome/browser/chromeos/login/screens/wrong_hwid_screen_view.h
index 9576e66c..38c8881 100644
--- a/chrome/browser/chromeos/login/screens/wrong_hwid_screen_view.h
+++ b/chrome/browser/chromeos/login/screens/wrong_hwid_screen_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_WRONG_HWID_SCREEN_VIEW_H_
 
 #include <string>
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 
 namespace chromeos {
 
@@ -26,6 +27,8 @@
     virtual void OnViewDestroyed(WrongHWIDScreenView* view) = 0;
   };
 
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_WRONG_HWID;
+
   virtual ~WrongHWIDScreenView() {}
 
   virtual void Show() = 0;
diff --git a/chrome/browser/chromeos/login/ui/views/user_board_view.h b/chrome/browser/chromeos/login/ui/views/user_board_view.h
index 5577da9..cdf5054e 100644
--- a/chrome/browser/chromeos/login/ui/views/user_board_view.h
+++ b/chrome/browser/chromeos/login/ui/views/user_board_view.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
 #include "components/proximity_auth/screenlock_bridge.h"
 
 class AccountId;
@@ -26,6 +27,8 @@
 // or Views one.
 class UserBoardView {
  public:
+  constexpr static OobeScreen kScreenId = OobeScreen::SCREEN_USER_SELECTION;
+
   virtual ~UserBoardView() {}
 
   virtual void Bind(UserSelectionScreen* screen) = 0;
diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
index 50dea992..2fc55b9f 100644
--- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc
+++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
@@ -367,18 +367,35 @@
     const base::Version& cdm_version,
     const base::FilePath& cdm_install_dir,
     std::unique_ptr<base::DictionaryValue> manifest) {
+  // On some platforms (e.g. Mac) we use symlinks for paths. Since we are
+  // comparing paths below, convert paths to absolute paths to avoid unexpected
+  // failure. base::MakeAbsoluteFilePath() requires IO so it can only be done
+  // in this function.
+  const base::FilePath absolute_cdm_install_dir =
+      base::MakeAbsoluteFilePath(cdm_install_dir);
+  if (absolute_cdm_install_dir.empty()) {
+    PLOG(WARNING) << "Failed to get absolute CDM install path.";
+    return;
+  }
+
   const base::FilePath adapter_version_path =
-      GetPlatformDirectory(cdm_install_dir).AppendASCII(kCdmAdapterVersionName);
+      GetPlatformDirectory(absolute_cdm_install_dir)
+          .AppendASCII(kCdmAdapterVersionName);
   const base::FilePath adapter_install_path =
-      GetPlatformDirectory(cdm_install_dir)
+      GetPlatformDirectory(absolute_cdm_install_dir)
           .AppendASCII(kWidevineCdmAdapterFileName);
 
-  VLOG(1) << "UpdateCdmAdapter: version" << cdm_version.GetString()
-          << " adapter_install_path=" << adapter_install_path.AsUTF8Unsafe()
-          << " adapter_version_path=" << adapter_version_path.AsUTF8Unsafe();
+  VLOG(1) << "UpdateCdmAdapter: version" << cdm_version.GetString();
+  VLOG(1) << " - adapter_install_path=" << adapter_install_path.AsUTF8Unsafe();
+  VLOG(1) << " - adapter_version_path=" << adapter_version_path.AsUTF8Unsafe();
 
   base::FilePath adapter_source_path;
   PathService::Get(chrome::FILE_WIDEVINE_CDM_ADAPTER, &adapter_source_path);
+  adapter_source_path = base::MakeAbsoluteFilePath(adapter_source_path);
+  if (adapter_source_path.empty()) {
+    PLOG(WARNING) << "Failed to get absolute adapter source path.";
+    return;
+  }
 
   const std::string chrome_version = version_info::GetVersionNumber();
   DCHECK(!chrome_version.empty());
@@ -414,8 +431,8 @@
 
   BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
-      base::Bind(&RegisterWidevineCdmWithChrome, cdm_version, cdm_install_dir,
-                 base::Passed(&manifest)));
+      base::Bind(&RegisterWidevineCdmWithChrome, cdm_version,
+                 absolute_cdm_install_dir, base::Passed(&manifest)));
 }
 
 #endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
diff --git a/chrome/browser/resources/options/options.js b/chrome/browser/resources/options/options.js
index 821d7ea..cdfb732 100644
--- a/chrome/browser/resources/options/options.js
+++ b/chrome/browser/resources/options/options.js
@@ -290,6 +290,9 @@
   window.setTimeout(function() {
     document.documentElement.classList.remove('loading');
     chrome.send('onFinishedLoadingOptions');
+    chrome.send(
+        'metricsHandler:recordTime',
+        ['Settings.TimeUntilInteractive', window.performance.now()]);
   }, 0);
 }
 
diff --git a/chrome/browser/resources/settings/settings.html b/chrome/browser/resources/settings/settings.html
index 191d3144..dcd77adc 100644
--- a/chrome/browser/resources/settings/settings.html
+++ b/chrome/browser/resources/settings/settings.html
@@ -9,7 +9,6 @@
   <link rel="import" href="chrome://resources/html/polymer.html">
   <link rel="import" href="settings_ui/settings_ui.html">
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
-  <link rel="import" href="chrome://resources/html/i18n_template.html">
   <!-- Do not add any <link rel="..."> elements below here. Imports outside of
        <head> are disallowed by HTML5 and cause undefined behavior that
        confuses Polymer's lifecycle methods: crbug.com/638074. -->
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index c05b827..e3c3ca1a 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -162,6 +162,11 @@
 
   /** @override */
   attached: function() {
+    setTimeout(function() {
+      chrome.send(
+          'metricsHandler:recordTime',
+          ['Settings.TimeUntilInteractive', window.performance.now()]);
+    });
     // Preload bold Roboto so it doesn't load and flicker the first time used.
     document.fonts.load('bold 12px Roboto');
     settings.setGlobalScrollTarget(this.$.headerPanel.scroller);
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 08540e8d..540431c1 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -239,6 +239,8 @@
     "webui/chromeos/login/auto_enrollment_check_screen_handler.h",
     "webui/chromeos/login/base_screen_handler.cc",
     "webui/chromeos/login/base_screen_handler.h",
+    "webui/chromeos/login/base_webui_handler.cc",
+    "webui/chromeos/login/base_webui_handler.h",
     "webui/chromeos/login/controller_pairing_screen_handler.cc",
     "webui/chromeos/login/controller_pairing_screen_handler.h",
     "webui/chromeos/login/core_oobe_handler.cc",
diff --git a/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
index 5dcdd4fc..865ceac 100644
--- a/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.cc
@@ -39,7 +39,8 @@
 AppLaunchSplashScreenHandler::AppLaunchSplashScreenHandler(
     const scoped_refptr<NetworkStateInformer>& network_state_informer,
     ErrorScreen* error_screen)
-    : network_state_informer_(network_state_informer),
+    : BaseScreenHandler(kScreenId),
+      network_state_informer_(network_state_informer),
       error_screen_(error_screen) {
   set_call_js_prefix(kJsScreenPath);
   network_state_informer_->AddObserver(this);
@@ -90,7 +91,7 @@
   data.Set("appInfo", app_info);
 
   SetLaunchText(l10n_util::GetStringUTF8(GetProgressMessageFromState(state_)));
-  ShowScreenWithData(OobeScreen::SCREEN_APP_LAUNCH_SPLASH, &data);
+  ShowScreenWithData(kScreenId, &data);
 }
 
 void AppLaunchSplashScreenHandler::RegisterMessages() {
@@ -176,7 +177,7 @@
   }
 
   if (GetCurrentScreen() != OobeScreen::SCREEN_ERROR_MESSAGE)
-    error_screen_->SetParentScreen(OobeScreen::SCREEN_APP_LAUNCH_SPLASH);
+    error_screen_->SetParentScreen(kScreenId);
   error_screen_->Show();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.cc
index 6533113..6b1ae2fd 100644
--- a/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/arc_kiosk_splash_screen_handler.cc
@@ -24,7 +24,8 @@
 
 namespace chromeos {
 
-ArcKioskSplashScreenHandler::ArcKioskSplashScreenHandler() {
+ArcKioskSplashScreenHandler::ArcKioskSplashScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -61,7 +62,7 @@
       base::MakeUnique<base::DictionaryValue>();
   PopulateAppInfo(app_info.get());
   data.Set("appInfo", std::move(app_info));
-  ShowScreenWithData(OobeScreen::SCREEN_ARC_KIOSK_SPLASH, &data);
+  ShowScreenWithData(kScreenId, &data);
 }
 
 void ArcKioskSplashScreenHandler::RegisterMessages() {
diff --git a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
index 9622daf..2acd08b 100644
--- a/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/arc_terms_of_service_screen_handler.cc
@@ -26,7 +26,8 @@
 
 namespace chromeos {
 
-ArcTermsOfServiceScreenHandler::ArcTermsOfServiceScreenHandler() {
+ArcTermsOfServiceScreenHandler::ArcTermsOfServiceScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -159,7 +160,7 @@
 
   system::TimezoneSettings::GetInstance()->AddObserver(this);
 
-  ShowScreen(OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE);
+  ShowScreen(kScreenId);
 
   UpdateTimeZone();
   pref_handler_.reset(new arc::ArcOptInPreferenceHandler(
diff --git a/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc
index 4c03bd98fd..0c3ce2c 100644
--- a/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/auto_enrollment_check_screen_handler.cc
@@ -16,7 +16,8 @@
 
 namespace chromeos {
 
-AutoEnrollmentCheckScreenHandler::AutoEnrollmentCheckScreenHandler() {
+AutoEnrollmentCheckScreenHandler::AutoEnrollmentCheckScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -30,7 +31,7 @@
     show_on_init_ = true;
     return;
   }
-  ShowScreen(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
+  ShowScreen(kScreenId);
 }
 
 void AutoEnrollmentCheckScreenHandler::SetDelegate(Delegate* delegate) {
diff --git a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc
index 56759473..8588ac9b 100644
--- a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.cc
@@ -4,136 +4,11 @@
 
 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/values.h"
-#include "chrome/browser/chromeos/login/screens/base_screen.h"
-#include "chrome/browser/chromeos/login/ui/login_display_host.h"
-#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
-#include "components/login/localized_values_builder.h"
-#include "content/public/browser/web_ui.h"
-
 namespace chromeos {
 
-namespace {
-const char kMethodContextChanged[] = "contextChanged";
-}  // namespace
+BaseScreenHandler::BaseScreenHandler(OobeScreen oobe_screen)
+    : oobe_screen_(oobe_screen) {}
 
-JSCallsContainer::JSCallsContainer() = default;
-
-JSCallsContainer::~JSCallsContainer() = default;
-
-BaseScreenHandler::BaseScreenHandler() = default;
-
-BaseScreenHandler::BaseScreenHandler(JSCallsContainer* js_calls_container)
-    : js_calls_container_(js_calls_container) {}
-
-BaseScreenHandler::~BaseScreenHandler() {
-  if (base_screen_)
-    base_screen_->set_model_view_channel(nullptr);
-}
-
-void BaseScreenHandler::InitializeBase() {
-  page_is_ready_ = true;
-  Initialize();
-  if (!pending_context_changes_.empty()) {
-    CommitContextChanges(pending_context_changes_);
-    pending_context_changes_.Clear();
-  }
-}
-
-void BaseScreenHandler::GetLocalizedStrings(base::DictionaryValue* dict) {
-  auto builder = base::MakeUnique<::login::LocalizedValuesBuilder>(dict);
-  DeclareLocalizedValues(builder.get());
-  GetAdditionalParameters(dict);
-}
-
-void BaseScreenHandler::RegisterMessages() {
-  AddPrefixedCallback("userActed",
-                      &BaseScreenHandler::HandleUserAction);
-  AddPrefixedCallback("contextChanged",
-                      &BaseScreenHandler::HandleContextChanged);
-  DeclareJSCallbacks();
-}
-
-void BaseScreenHandler::CommitContextChanges(
-    const base::DictionaryValue& diff) {
-  if (!page_is_ready())
-    pending_context_changes_.MergeDictionary(&diff);
-  else
-    CallJS(kMethodContextChanged, diff);
-}
-
-void BaseScreenHandler::GetAdditionalParameters(base::DictionaryValue* dict) {
-}
-
-void BaseScreenHandler::CallJS(const std::string& method) {
-  web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method));
-}
-
-void BaseScreenHandler::ShowScreen(OobeScreen screen) {
-  ShowScreenWithData(screen, nullptr);
-}
-
-void BaseScreenHandler::ShowScreenWithData(OobeScreen screen,
-                                           const base::DictionaryValue* data) {
-  if (!web_ui())
-    return;
-  base::DictionaryValue screen_params;
-  screen_params.SetString("id", GetOobeScreenName(screen));
-  if (data)
-    screen_params.SetWithoutPathExpansion("data", data->DeepCopy());
-  web_ui()->CallJavascriptFunctionUnsafe("cr.ui.Oobe.showScreen",
-                                         screen_params);
-}
-
-OobeUI* BaseScreenHandler::GetOobeUI() const {
-  return static_cast<OobeUI*>(web_ui()->GetController());
-}
-
-OobeScreen BaseScreenHandler::GetCurrentScreen() const {
-  OobeUI* oobe_ui = GetOobeUI();
-  if (!oobe_ui)
-    return OobeScreen::SCREEN_UNKNOWN;
-  return oobe_ui->current_screen();
-}
-
-gfx::NativeWindow BaseScreenHandler::GetNativeWindow() {
-  return LoginDisplayHost::default_host()->GetNativeWindow();
-}
-
-void BaseScreenHandler::SetBaseScreen(BaseScreen* base_screen) {
-  if (base_screen_ == base_screen)
-    return;
-  if (base_screen_)
-    base_screen_->set_model_view_channel(nullptr);
-  base_screen_ = base_screen;
-  if (base_screen_)
-    base_screen_->set_model_view_channel(this);
-}
-
-std::string BaseScreenHandler::FullMethodPath(const std::string& method) const {
-  DCHECK(!method.empty());
-  return js_screen_path_prefix_ + method;
-}
-
-void BaseScreenHandler::HandleUserAction(const std::string& action_id) {
-  if (base_screen_)
-    base_screen_->OnUserAction(action_id);
-}
-
-void BaseScreenHandler::HandleContextChanged(
-    const base::DictionaryValue* diff) {
-  if (diff && base_screen_)
-    base_screen_->OnContextChanged(*diff);
-}
-
-void BaseScreenHandler::ExecuteDeferredJSCalls() {
-  DCHECK(!js_calls_container_->is_initialized());
-  js_calls_container_->mark_initialized();
-  for (const auto& deferred_js_call : js_calls_container_->deferred_js_calls())
-    deferred_js_call.Run();
-  js_calls_container_->deferred_js_calls().clear();
-}
+BaseScreenHandler::~BaseScreenHandler() {}
 
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
index 1fae31a..05916e7 100644
--- a/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/base_screen_handler.h
@@ -5,261 +5,24 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_SCREEN_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_SCREEN_HANDLER_H_
 
-#include <string>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
 #include "base/macros.h"
 #include "chrome/browser/chromeos/login/oobe_screen.h"
-#include "chrome/browser/chromeos/login/screens/model_view_channel.h"
-#include "components/login/base_screen_handler_utils.h"
-#include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_message_handler.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace base {
-class DictionaryValue;
-class ListValue;
-}
-
-namespace login {
-class LocalizedValuesBuilder;
-}
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
 
 namespace chromeos {
 
-class BaseScreen;
-class OobeUI;
-
-// A helper class to store deferred Javascript calls, shared by subclasses of
-// BaseScreenHandler.
-class JSCallsContainer {
+// Base class for the OOBE/Login WebUI handlers which provide methods specific
+// to a particular OobeScreen.
+class BaseScreenHandler : public BaseWebUIHandler {
  public:
-  JSCallsContainer();
-  ~JSCallsContainer();
-
-  // Used to decide whether the JS call should be deferred.
-  bool is_initialized() { return is_initialized_; }
-
-  // Used to mark the instance as intialized.
-  void mark_initialized() { is_initialized_ = true; }
-
-  // Used to add deferred calls to.
-  std::vector<base::Closure>& deferred_js_calls() { return deferred_js_calls_; }
-
- private:
-  // Whether the instance is initialized.
-  //
-  // The instance becomes initialized after the corresponding message is
-  // received from Javascript side.
-  bool is_initialized_ = false;
-
-  // Javascript calls that have been deferred while the instance was not
-  // initialized yet.
-  std::vector<base::Closure> deferred_js_calls_;
-};
-
-// Base class for the OOBE/Login WebUI handlers.
-class BaseScreenHandler : public content::WebUIMessageHandler,
-                          public ModelViewChannel {
- public:
-  BaseScreenHandler();
-  explicit BaseScreenHandler(JSCallsContainer* js_calls_container);
+  explicit BaseScreenHandler(OobeScreen oobe_screen);
   ~BaseScreenHandler() override;
 
-  // Gets localized strings to be used on the page.
-  void GetLocalizedStrings(
-      base::DictionaryValue* localized_strings);
-
-  // WebUIMessageHandler implementation:
-  void RegisterMessages() override;
-
-  // ModelViewChannel implementation:
-  void CommitContextChanges(const base::DictionaryValue& diff) override;
-
-  // This method is called when page is ready. It propagates to inherited class
-  // via virtual Initialize() method (see below).
-  void InitializeBase();
-
-  void set_async_assets_load_id(const std::string& async_assets_load_id) {
-    async_assets_load_id_ = async_assets_load_id;
-  }
-  const std::string& async_assets_load_id() const {
-    return async_assets_load_id_;
-  }
-
-  // Set the prefix used when running CallJs with a method. For example,
-  //    set_call_js_prefix("Oobe")
-  //    CallJs("lock") -> Invokes JS global named "Oobe.lock"
-  void set_call_js_prefix(const std::string& prefix) {
-    js_screen_path_prefix_ = prefix + ".";
-  }
-
- protected:
-  // All subclasses should implement this method to provide localized values.
-  virtual void DeclareLocalizedValues(
-      ::login::LocalizedValuesBuilder* builder) = 0;
-
-  // All subclasses should implement this method to register callbacks for JS
-  // messages.
-  //
-  // TODO (ygorshenin, crbug.com/433797): make this method purely vrtual when
-  // all screens will be switched to use ScreenContext.
-  virtual void DeclareJSCallbacks() {}
-
-  // Subclasses can override these methods to pass additional parameters
-  // to loadTimeData. Generally, it is a bad approach, and it should be replaced
-  // with Context at some point.
-  virtual void GetAdditionalParameters(base::DictionaryValue* parameters);
-
-  // Shortcut for calling JS methods on WebUI side.
-  void CallJS(const std::string& method);
-
-  template<typename A1>
-  void CallJS(const std::string& method, const A1& arg1) {
-    web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method),
-                                           ::login::MakeValue(arg1));
-  }
-
-  template<typename A1, typename A2>
-  void CallJS(const std::string& method, const A1& arg1, const A2& arg2) {
-    web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method),
-                                           ::login::MakeValue(arg1),
-                                           ::login::MakeValue(arg2));
-  }
-
-  template<typename A1, typename A2, typename A3>
-  void CallJS(const std::string& method,
-              const A1& arg1,
-              const A2& arg2,
-              const A3& arg3) {
-    web_ui()->CallJavascriptFunctionUnsafe(
-        FullMethodPath(method), ::login::MakeValue(arg1),
-        ::login::MakeValue(arg2), ::login::MakeValue(arg3));
-  }
-
-  template<typename A1, typename A2, typename A3, typename A4>
-  void CallJS(const std::string& method,
-              const A1& arg1,
-              const A2& arg2,
-              const A3& arg3,
-              const A4& arg4) {
-    web_ui()->CallJavascriptFunctionUnsafe(
-        FullMethodPath(method), ::login::MakeValue(arg1),
-        ::login::MakeValue(arg2), ::login::MakeValue(arg3),
-        ::login::MakeValue(arg4));
-  }
-
-  template <typename... Args>
-  void CallJSOrDefer(const std::string& function_name, const Args&... args) {
-    DCHECK(js_calls_container_);
-    if (js_calls_container_->is_initialized()) {
-      CallJS(function_name, args...);
-    } else {
-      // Note that std::conditional is used here in order to obtain a sequence
-      // of base::Value types with the length equal to sizeof...(Args); the C++
-      // template parameter pack expansion rules require that the name of the
-      // parameter pack appears in the pattern, even though the elements of the
-      // Args pack are not actually in this code.
-      js_calls_container_->deferred_js_calls().push_back(base::Bind(
-          &BaseScreenHandler::ExecuteDeferredJSCall<
-              typename std::conditional<true, base::Value, Args>::type...>,
-          base::Unretained(this), function_name,
-          base::Passed(::login::MakeValue(args).CreateDeepCopy())...));
-    }
-  }
-
-  // Executes Javascript calls that were deferred while the instance was not
-  // initialized yet.
-  void ExecuteDeferredJSCalls();
-
-  // Shortcut methods for adding WebUI callbacks.
-  template<typename T>
-  void AddRawCallback(const std::string& name,
-                      void (T::*method)(const base::ListValue* args)) {
-    web_ui()->RegisterMessageCallback(
-        name,
-        base::Bind(method, base::Unretained(static_cast<T*>(this))));
-  }
-
-  template<typename T, typename... Args>
-  void AddCallback(const std::string& name, void (T::*method)(Args...)) {
-    base::Callback<void(Args...)> callback =
-        base::Bind(method, base::Unretained(static_cast<T*>(this)));
-    web_ui()->RegisterMessageCallback(
-        name, base::Bind(&::login::CallbackWrapper<Args...>, callback));
-  }
-
-  template <typename Method>
-  void AddPrefixedCallback(const std::string& unprefixed_name,
-                           const Method& method) {
-    AddCallback(FullMethodPath(unprefixed_name), method);
-  }
-
-  // Called when the page is ready and handler can do initialization.
-  virtual void Initialize() = 0;
-
-  // Show selected WebUI |screen|.
-  void ShowScreen(OobeScreen screen);
-  // Show selected WebUI |screen|. Pass screen initialization using the |data|
-  // parameter.
-  void ShowScreenWithData(OobeScreen screen, const base::DictionaryValue* data);
-
-  // Returns the OobeUI instance.
-  OobeUI* GetOobeUI() const;
-
-  // Returns current visible OOBE screen.
-  OobeScreen GetCurrentScreen() const;
-
-  // Whether page is ready.
-  bool page_is_ready() const { return page_is_ready_; }
-
-  // Returns the window which shows us.
-  virtual gfx::NativeWindow GetNativeWindow();
-
-  void SetBaseScreen(BaseScreen* base_screen);
+  OobeScreen oobe_screen() const { return oobe_screen_; }
 
  private:
-  // Calls Javascript method.
-  //
-  // Note that the Args template parameter pack should consist of types
-  // convertible to base::Value.
-  template <typename... Args>
-  void ExecuteDeferredJSCall(const std::string& function_name,
-                             std::unique_ptr<Args>... args) {
-    CallJS(function_name, *args...);
-  }
-
-  // Returns full name of JS method based on screen and method
-  // names.
-  std::string FullMethodPath(const std::string& method) const;
-
-  // Handles user action.
-  void HandleUserAction(const std::string& action_id);
-
-  // Handles situation when screen context is changed.
-  void HandleContextChanged(const base::DictionaryValue* diff);
-
-  // Keeps whether page is ready.
-  bool page_is_ready_ = false;
-
-  BaseScreen* base_screen_ = nullptr;
-
-  // Full name of the corresponding JS screen object. Can be empty, if
-  // there are no corresponding screen object or several different
-  // objects.
-  std::string js_screen_path_prefix_;
-
-  // The string id used in the async asset load in JS. If it is set to a
-  // non empty value, the Initialize will be deferred until the underlying load
-  // is finished.
-  std::string async_assets_load_id_;
-
-  // Pending changes to context which will be sent when the page will be ready.
-  base::DictionaryValue pending_context_changes_;
-
-  JSCallsContainer* js_calls_container_ = nullptr;  // non-owning pointers.
+  // OobeScreen that this handler corresponds to.
+  OobeScreen oobe_screen_ = OobeScreen::SCREEN_UNKNOWN;
 
   DISALLOW_COPY_AND_ASSIGN(BaseScreenHandler);
 };
diff --git a/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc b/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc
new file mode 100644
index 0000000..3bae474c
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/base_webui_handler.cc
@@ -0,0 +1,135 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/login/screens/base_screen.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
+#include "components/login/localized_values_builder.h"
+#include "content/public/browser/web_ui.h"
+
+namespace chromeos {
+
+namespace {
+const char kMethodContextChanged[] = "contextChanged";
+}  // namespace
+
+JSCallsContainer::JSCallsContainer() = default;
+
+JSCallsContainer::~JSCallsContainer() = default;
+
+BaseWebUIHandler::BaseWebUIHandler() = default;
+
+BaseWebUIHandler::BaseWebUIHandler(JSCallsContainer* js_calls_container)
+    : js_calls_container_(js_calls_container) {}
+
+BaseWebUIHandler::~BaseWebUIHandler() {
+  if (base_screen_)
+    base_screen_->set_model_view_channel(nullptr);
+}
+
+void BaseWebUIHandler::InitializeBase() {
+  page_is_ready_ = true;
+  Initialize();
+  if (!pending_context_changes_.empty()) {
+    CommitContextChanges(pending_context_changes_);
+    pending_context_changes_.Clear();
+  }
+}
+
+void BaseWebUIHandler::GetLocalizedStrings(base::DictionaryValue* dict) {
+  auto builder = base::MakeUnique<::login::LocalizedValuesBuilder>(dict);
+  DeclareLocalizedValues(builder.get());
+  GetAdditionalParameters(dict);
+}
+
+void BaseWebUIHandler::RegisterMessages() {
+  AddPrefixedCallback("userActed", &BaseScreenHandler::HandleUserAction);
+  AddPrefixedCallback("contextChanged",
+                      &BaseScreenHandler::HandleContextChanged);
+  DeclareJSCallbacks();
+}
+
+void BaseWebUIHandler::CommitContextChanges(const base::DictionaryValue& diff) {
+  if (!page_is_ready())
+    pending_context_changes_.MergeDictionary(&diff);
+  else
+    CallJS(kMethodContextChanged, diff);
+}
+
+void BaseWebUIHandler::GetAdditionalParameters(base::DictionaryValue* dict) {}
+
+void BaseWebUIHandler::CallJS(const std::string& method) {
+  web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method));
+}
+
+void BaseWebUIHandler::ShowScreen(OobeScreen screen) {
+  ShowScreenWithData(screen, nullptr);
+}
+
+void BaseWebUIHandler::ShowScreenWithData(OobeScreen screen,
+                                          const base::DictionaryValue* data) {
+  if (!web_ui())
+    return;
+  base::DictionaryValue screen_params;
+  screen_params.SetString("id", GetOobeScreenName(screen));
+  if (data)
+    screen_params.SetWithoutPathExpansion("data", data->DeepCopy());
+  web_ui()->CallJavascriptFunctionUnsafe("cr.ui.Oobe.showScreen",
+                                         screen_params);
+}
+
+OobeUI* BaseWebUIHandler::GetOobeUI() const {
+  return static_cast<OobeUI*>(web_ui()->GetController());
+}
+
+OobeScreen BaseWebUIHandler::GetCurrentScreen() const {
+  OobeUI* oobe_ui = GetOobeUI();
+  if (!oobe_ui)
+    return OobeScreen::SCREEN_UNKNOWN;
+  return oobe_ui->current_screen();
+}
+
+gfx::NativeWindow BaseWebUIHandler::GetNativeWindow() {
+  return LoginDisplayHost::default_host()->GetNativeWindow();
+}
+
+void BaseWebUIHandler::SetBaseScreen(BaseScreen* base_screen) {
+  if (base_screen_ == base_screen)
+    return;
+  if (base_screen_)
+    base_screen_->set_model_view_channel(nullptr);
+  base_screen_ = base_screen;
+  if (base_screen_)
+    base_screen_->set_model_view_channel(this);
+}
+
+std::string BaseWebUIHandler::FullMethodPath(const std::string& method) const {
+  DCHECK(!method.empty());
+  return js_screen_path_prefix_ + method;
+}
+
+void BaseWebUIHandler::HandleUserAction(const std::string& action_id) {
+  if (base_screen_)
+    base_screen_->OnUserAction(action_id);
+}
+
+void BaseWebUIHandler::HandleContextChanged(const base::DictionaryValue* diff) {
+  if (diff && base_screen_)
+    base_screen_->OnContextChanged(*diff);
+}
+
+void BaseWebUIHandler::ExecuteDeferredJSCalls() {
+  DCHECK(!js_calls_container_->is_initialized());
+  js_calls_container_->mark_initialized();
+  for (const auto& deferred_js_call : js_calls_container_->deferred_js_calls())
+    deferred_js_call.Run();
+  js_calls_container_->deferred_js_calls().clear();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h b/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h
new file mode 100644
index 0000000..aae5b26
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/base_webui_handler.h
@@ -0,0 +1,274 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/model_view_channel.h"
+#include "components/login/base_screen_handler_utils.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_message_handler.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace login {
+class LocalizedValuesBuilder;
+}
+
+namespace chromeos {
+
+class BaseScreen;
+class OobeUI;
+
+// A helper class to store deferred Javascript calls, shared by subclasses of
+// BaseWebUIHandler.
+class JSCallsContainer {
+ public:
+  JSCallsContainer();
+  ~JSCallsContainer();
+
+  // Used to decide whether the JS call should be deferred.
+  bool is_initialized() { return is_initialized_; }
+
+  // Used to mark the instance as intialized.
+  void mark_initialized() { is_initialized_ = true; }
+
+  // Used to add deferred calls to.
+  std::vector<base::Closure>& deferred_js_calls() { return deferred_js_calls_; }
+
+ private:
+  // Whether the instance is initialized.
+  //
+  // The instance becomes initialized after the corresponding message is
+  // received from Javascript side.
+  bool is_initialized_ = false;
+
+  // Javascript calls that have been deferred while the instance was not
+  // initialized yet.
+  std::vector<base::Closure> deferred_js_calls_;
+};
+
+// Base class for all oobe/login WebUI handlers. These handlers are the binding
+// layer that allow the C++ and JavaScript code to communicate.
+//
+// If the deriving type is associated with a specific OobeScreen, it should
+// derive from BaseScreenHandler instead of BaseWebUIHandler.
+//
+// TODO(jdufault): Move all OobeScreen related concepts out of BaseWebUIHandler
+// and into BaseScreenHandler.
+class BaseWebUIHandler : public content::WebUIMessageHandler,
+                         public ModelViewChannel {
+ public:
+  BaseWebUIHandler();
+  explicit BaseWebUIHandler(JSCallsContainer* js_calls_container);
+  ~BaseWebUIHandler() override;
+
+  // Gets localized strings to be used on the page.
+  void GetLocalizedStrings(base::DictionaryValue* localized_strings);
+
+  // WebUIMessageHandler implementation:
+  void RegisterMessages() override;
+
+  // ModelViewChannel implementation:
+  void CommitContextChanges(const base::DictionaryValue& diff) override;
+
+  // This method is called when page is ready. It propagates to inherited class
+  // via virtual Initialize() method (see below).
+  void InitializeBase();
+
+  // Set the prefix used when running CallJs with a method. For example,
+  //    set_call_js_prefix("Oobe")
+  //    CallJs("lock") -> Invokes JS global named "Oobe.lock"
+  void set_call_js_prefix(const std::string& prefix) {
+    js_screen_path_prefix_ = prefix + ".";
+  }
+
+  void set_async_assets_load_id(const std::string& async_assets_load_id) {
+    async_assets_load_id_ = async_assets_load_id;
+  }
+  const std::string& async_assets_load_id() const {
+    return async_assets_load_id_;
+  }
+
+ protected:
+  // All subclasses should implement this method to provide localized values.
+  virtual void DeclareLocalizedValues(
+      ::login::LocalizedValuesBuilder* builder) = 0;
+
+  // All subclasses should implement this method to register callbacks for JS
+  // messages.
+  //
+  // TODO (ygorshenin, crbug.com/433797): make this method purely vrtual when
+  // all screens will be switched to use ScreenContext.
+  virtual void DeclareJSCallbacks() {}
+
+  // Subclasses can override these methods to pass additional parameters
+  // to loadTimeData. Generally, it is a bad approach, and it should be replaced
+  // with Context at some point.
+  virtual void GetAdditionalParameters(base::DictionaryValue* parameters);
+
+  // Shortcut for calling JS methods on WebUI side.
+  void CallJS(const std::string& method);
+
+  template <typename A1>
+  void CallJS(const std::string& method, const A1& arg1) {
+    web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method),
+                                           ::login::MakeValue(arg1));
+  }
+
+  template <typename A1, typename A2>
+  void CallJS(const std::string& method, const A1& arg1, const A2& arg2) {
+    web_ui()->CallJavascriptFunctionUnsafe(FullMethodPath(method),
+                                           ::login::MakeValue(arg1),
+                                           ::login::MakeValue(arg2));
+  }
+
+  template <typename A1, typename A2, typename A3>
+  void CallJS(const std::string& method,
+              const A1& arg1,
+              const A2& arg2,
+              const A3& arg3) {
+    web_ui()->CallJavascriptFunctionUnsafe(
+        FullMethodPath(method), ::login::MakeValue(arg1),
+        ::login::MakeValue(arg2), ::login::MakeValue(arg3));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4>
+  void CallJS(const std::string& method,
+              const A1& arg1,
+              const A2& arg2,
+              const A3& arg3,
+              const A4& arg4) {
+    web_ui()->CallJavascriptFunctionUnsafe(
+        FullMethodPath(method), ::login::MakeValue(arg1),
+        ::login::MakeValue(arg2), ::login::MakeValue(arg3),
+        ::login::MakeValue(arg4));
+  }
+
+  template <typename... Args>
+  void CallJSOrDefer(const std::string& function_name, const Args&... args) {
+    DCHECK(js_calls_container_);
+    if (js_calls_container_->is_initialized()) {
+      CallJS(function_name, args...);
+    } else {
+      // Note that std::conditional is used here in order to obtain a sequence
+      // of base::Value types with the length equal to sizeof...(Args); the C++
+      // template parameter pack expansion rules require that the name of the
+      // parameter pack appears in the pattern, even though the elements of the
+      // Args pack are not actually in this code.
+      js_calls_container_->deferred_js_calls().push_back(base::Bind(
+          &BaseWebUIHandler::ExecuteDeferredJSCall<
+              typename std::conditional<true, base::Value, Args>::type...>,
+          base::Unretained(this), function_name,
+          base::Passed(::login::MakeValue(args).CreateDeepCopy())...));
+    }
+  }
+
+  // Executes Javascript calls that were deferred while the instance was not
+  // initialized yet.
+  void ExecuteDeferredJSCalls();
+
+  // Shortcut methods for adding WebUI callbacks.
+  template <typename T>
+  void AddRawCallback(const std::string& name,
+                      void (T::*method)(const base::ListValue* args)) {
+    web_ui()->RegisterMessageCallback(
+        name, base::Bind(method, base::Unretained(static_cast<T*>(this))));
+  }
+
+  template <typename T, typename... Args>
+  void AddCallback(const std::string& name, void (T::*method)(Args...)) {
+    base::Callback<void(Args...)> callback =
+        base::Bind(method, base::Unretained(static_cast<T*>(this)));
+    web_ui()->RegisterMessageCallback(
+        name, base::Bind(&::login::CallbackWrapper<Args...>, callback));
+  }
+
+  template <typename Method>
+  void AddPrefixedCallback(const std::string& unprefixed_name,
+                           const Method& method) {
+    AddCallback(FullMethodPath(unprefixed_name), method);
+  }
+
+  // Called when the page is ready and handler can do initialization.
+  virtual void Initialize() = 0;
+
+  // Show selected WebUI |screen|.
+  void ShowScreen(OobeScreen screen);
+  // Show selected WebUI |screen|. Pass screen initialization using the |data|
+  // parameter.
+  void ShowScreenWithData(OobeScreen screen, const base::DictionaryValue* data);
+
+  // Returns the OobeUI instance.
+  OobeUI* GetOobeUI() const;
+
+  // Returns current visible OOBE screen.
+  OobeScreen GetCurrentScreen() const;
+
+  // Whether page is ready.
+  bool page_is_ready() const { return page_is_ready_; }
+
+  // Returns the window which shows us.
+  virtual gfx::NativeWindow GetNativeWindow();
+
+  void SetBaseScreen(BaseScreen* base_screen);
+
+ private:
+  // Calls Javascript method.
+  //
+  // Note that the Args template parameter pack should consist of types
+  // convertible to base::Value.
+  template <typename... Args>
+  void ExecuteDeferredJSCall(const std::string& function_name,
+                             std::unique_ptr<Args>... args) {
+    CallJS(function_name, *args...);
+  }
+
+  // Returns full name of JS method based on screen and method
+  // names.
+  std::string FullMethodPath(const std::string& method) const;
+
+  // Handles user action.
+  void HandleUserAction(const std::string& action_id);
+
+  // Handles situation when screen context is changed.
+  void HandleContextChanged(const base::DictionaryValue* diff);
+
+  // Keeps whether page is ready.
+  bool page_is_ready_ = false;
+
+  BaseScreen* base_screen_ = nullptr;
+
+  // Full name of the corresponding JS screen object. Can be empty, if
+  // there are no corresponding screen object or several different
+  // objects.
+  std::string js_screen_path_prefix_;
+
+  // The string id used in the async asset load in JS. If it is set to a
+  // non empty value, the Initialize will be deferred until the underlying load
+  // is finished.
+  std::string async_assets_load_id_;
+
+  // Pending changes to context which will be sent when the page will be ready.
+  base::DictionaryValue pending_context_changes_;
+
+  JSCallsContainer* js_calls_container_ = nullptr;  // non-owning pointers.
+
+  DISALLOW_COPY_AND_ASSIGN(BaseWebUIHandler);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_BASE_WEBUI_HANDLER_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.cc
index e77bfc9..a2c7493 100644
--- a/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/controller_pairing_screen_handler.cc
@@ -30,7 +30,8 @@
 
 }  // namespace
 
-ControllerPairingScreenHandler::ControllerPairingScreenHandler() {
+ControllerPairingScreenHandler::ControllerPairingScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -150,7 +151,7 @@
     show_on_init_ = true;
     return;
   }
-  ShowScreen(OobeScreen::SCREEN_OOBE_CONTROLLER_PAIRING);
+  ShowScreen(kScreenId);
 }
 
 void ControllerPairingScreenHandler::Hide() {
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
index 15d635e1..db15811 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.cc
@@ -57,7 +57,7 @@
 // OOBE UI is not visible by default.
 CoreOobeHandler::CoreOobeHandler(OobeUI* oobe_ui,
                                  JSCallsContainer* js_calls_container)
-    : BaseScreenHandler(js_calls_container),
+    : BaseWebUIHandler(js_calls_container),
       oobe_ui_(oobe_ui),
       version_info_updater_(this) {
   DCHECK(js_calls_container);
diff --git a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
index 33a11e9..2e53a077 100644
--- a/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h
@@ -16,7 +16,7 @@
 #include "chrome/browser/chromeos/login/demo_mode/demo_mode_detector.h"
 #include "chrome/browser/chromeos/login/screens/core_oobe_view.h"
 #include "chrome/browser/chromeos/login/version_info_updater.h"
-#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
 #include "ui/events/event_source.h"
 #include "ui/keyboard/scoped_keyboard_disabler.h"
 
@@ -34,7 +34,7 @@
 class OobeUI;
 
 // The core handler for Javascript messages related to the "oobe" view.
-class CoreOobeHandler : public BaseScreenHandler,
+class CoreOobeHandler : public BaseWebUIHandler,
                         public VersionInfoUpdater::Delegate,
                         public CoreOobeView,
                         public ui::EventSource {
diff --git a/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.cc
index cc8bc9e..6211c4fd4 100644
--- a/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/device_disabled_screen_handler.cc
@@ -17,7 +17,8 @@
 
 namespace chromeos {
 
-DeviceDisabledScreenHandler::DeviceDisabledScreenHandler() {
+DeviceDisabledScreenHandler::DeviceDisabledScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -36,7 +37,7 @@
     CallJS("setEnrollmentDomain", delegate_->GetEnrollmentDomain());
     CallJS("setMessage", delegate_->GetMessage());
   }
-  ShowScreen(OobeScreen::SCREEN_DEVICE_DISABLED);
+  ShowScreen(kScreenId);
 }
 
 void DeviceDisabledScreenHandler::Hide() {
diff --git a/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
index 3d7ed28..e1234fe 100644
--- a/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.cc
@@ -35,7 +35,7 @@
 namespace chromeos {
 
 EnableDebuggingScreenHandler::EnableDebuggingScreenHandler()
-    : weak_ptr_factory_(this) {
+    : BaseScreenHandler(kScreenId), weak_ptr_factory_(this) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -45,7 +45,7 @@
 }
 
 void EnableDebuggingScreenHandler::ShowWithParams() {
-  ShowScreen(OobeScreen::SCREEN_OOBE_ENABLE_DEBUGGING);
+  ShowScreen(kScreenId);
 
   UpdateUIState(UI_STATE_WAIT);
 
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
index 00ae028..f516642 100644
--- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -123,13 +123,13 @@
 EnrollmentScreenHandler::EnrollmentScreenHandler(
     const scoped_refptr<NetworkStateInformer>& network_state_informer,
     ErrorScreen* error_screen)
-    : network_state_informer_(network_state_informer),
+    : BaseScreenHandler(kScreenId),
+      network_state_informer_(network_state_informer),
       error_screen_(error_screen),
       histogram_helper_(new ErrorScreensHistogramHelper("Enrollment")),
       weak_ptr_factory_(this) {
   set_call_js_prefix(kJsScreenPath);
-  set_async_assets_load_id(
-      GetOobeScreenName(OobeScreen::SCREEN_OOBE_ENROLLMENT));
+  set_async_assets_load_id(GetOobeScreenName(kScreenId));
   DCHECK(network_state_informer_.get());
   DCHECK(error_screen_);
   network_state_informer_->AddObserver(this);
@@ -414,13 +414,12 @@
 }
 
 bool EnrollmentScreenHandler::IsOnEnrollmentScreen() const {
-  return (GetCurrentScreen() == OobeScreen::SCREEN_OOBE_ENROLLMENT);
+  return (GetCurrentScreen() == kScreenId);
 }
 
 bool EnrollmentScreenHandler::IsEnrollmentScreenHiddenByError() const {
   return (GetCurrentScreen() == OobeScreen::SCREEN_ERROR_MESSAGE &&
-          error_screen_->GetParentScreen() ==
-              OobeScreen::SCREEN_OOBE_ENROLLMENT);
+          error_screen_->GetParentScreen() == kScreenId);
 }
 
 void EnrollmentScreenHandler::UpdateState(NetworkError::ErrorReason reason) {
@@ -500,7 +499,7 @@
   if (GetCurrentScreen() != OobeScreen::SCREEN_ERROR_MESSAGE) {
     const std::string network_type = network_state_informer_->network_type();
     error_screen_->SetUIState(NetworkError::UI_STATE_SIGNIN);
-    error_screen_->SetParentScreen(OobeScreen::SCREEN_OOBE_ENROLLMENT);
+    error_screen_->SetParentScreen(kScreenId);
     error_screen_->SetHideCallback(base::Bind(&EnrollmentScreenHandler::DoShow,
                                               weak_ptr_factory_.GetWeakPtr()));
     error_screen_->Show();
diff --git a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
index 5f0d589..e93b4ddc 100644
--- a/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/error_screen_handler.cc
@@ -21,7 +21,8 @@
 
 namespace chromeos {
 
-ErrorScreenHandler::ErrorScreenHandler() : weak_ptr_factory_(this) {
+ErrorScreenHandler::ErrorScreenHandler()
+    : BaseScreenHandler(kScreenId), weak_ptr_factory_(this) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -35,7 +36,7 @@
     show_on_init_ = true;
     return;
   }
-  BaseScreenHandler::ShowScreen(OobeScreen::SCREEN_ERROR_MESSAGE);
+  BaseScreenHandler::ShowScreen(kScreenId);
   if (screen_)
     screen_->OnShow();
   showing_ = true;
diff --git a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
index 134245c..a223d46 100644
--- a/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/eula_screen_handler.cc
@@ -83,7 +83,7 @@
 namespace chromeos {
 
 EulaScreenHandler::EulaScreenHandler(CoreOobeView* core_oobe_view)
-    : core_oobe_view_(core_oobe_view) {
+    : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -97,7 +97,7 @@
     show_on_init_ = true;
     return;
   }
-  ShowScreen(OobeScreen::SCREEN_OOBE_EULA);
+  ShowScreen(kScreenId);
 }
 
 void EulaScreenHandler::Hide() {
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 13c9224..d847e257 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -228,7 +228,8 @@
 GaiaScreenHandler::GaiaScreenHandler(
     CoreOobeView* core_oobe_view,
     const scoped_refptr<NetworkStateInformer>& network_state_informer)
-    : network_state_informer_(network_state_informer),
+    : BaseScreenHandler(kScreenId),
+      network_state_informer_(network_state_informer),
       core_oobe_view_(core_oobe_view),
       weak_factory_(this) {
   DCHECK(network_state_informer_.get());
@@ -461,7 +462,7 @@
   if (offline_login_is_active() ||
       IsOnline(captive_portal_status_) == IsOnline(previous_status) ||
       disable_restrictive_proxy_check_for_test_ ||
-      GetCurrentScreen() != OobeScreen::SCREEN_GAIA_SIGNIN)
+      GetCurrentScreen() != kScreenId)
     return;
 
   LoadAuthExtension(true /* force */, false /* offline */);
diff --git a/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
index 1aaa7ec..26b7f92 100644
--- a/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.cc
@@ -30,7 +30,7 @@
 
 HIDDetectionScreenHandler::HIDDetectionScreenHandler(
     CoreOobeView* core_oobe_view)
-    : core_oobe_view_(core_oobe_view) {
+    : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -52,7 +52,7 @@
   local_state->SetInteger(prefs::kTimesHIDDialogShown,
                           num_of_times_dialog_was_shown + 1);
 
-  ShowScreen(OobeScreen::SCREEN_OOBE_HID_DETECTION);
+  ShowScreen(kScreenId);
 }
 
 void HIDDetectionScreenHandler::Hide() {
diff --git a/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.cc
index 0dc1b55..1f378848 100644
--- a/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/host_pairing_screen_handler.cc
@@ -26,7 +26,8 @@
 
 }  // namespace
 
-HostPairingScreenHandler::HostPairingScreenHandler() {
+HostPairingScreenHandler::HostPairingScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -95,7 +96,7 @@
     show_on_init_ = true;
     return;
   }
-  ShowScreen(OobeScreen::SCREEN_OOBE_HOST_PAIRING);
+  ShowScreen(kScreenId);
 }
 
 void HostPairingScreenHandler::Hide() {
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
index 50d8b97c..d2d341d 100644
--- a/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/kiosk_autolaunch_screen_handler.cc
@@ -32,7 +32,8 @@
 
 namespace chromeos {
 
-KioskAutolaunchScreenHandler::KioskAutolaunchScreenHandler() {
+KioskAutolaunchScreenHandler::KioskAutolaunchScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
   KioskAppManager::Get()->AddObserver(this);
 }
@@ -50,7 +51,7 @@
     return;
   }
   UpdateKioskApp();
-  ShowScreen(OobeScreen::SCREEN_KIOSK_AUTOLAUNCH);
+  ShowScreen(kScreenId);
 }
 
 void KioskAutolaunchScreenHandler::SetDelegate(Delegate* delegate) {
diff --git a/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
index f2c86d7..16d9eb7c 100644
--- a/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.cc
@@ -24,7 +24,8 @@
 
 namespace chromeos {
 
-KioskEnableScreenHandler::KioskEnableScreenHandler() : weak_ptr_factory_(this) {
+KioskEnableScreenHandler::KioskEnableScreenHandler()
+    : BaseScreenHandler(kScreenId), weak_ptr_factory_(this) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -54,7 +55,7 @@
     return;
   }
 
-  ShowScreen(OobeScreen::SCREEN_KIOSK_ENABLE);
+  ShowScreen(kScreenId);
 
   content::NotificationService::current()->Notify(
       chrome::NOTIFICATION_KIOSK_ENABLE_WARNING_VISIBLE,
diff --git a/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h b/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h
index 4c0c64a..9e0841d 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/network_dropdown_handler.h
@@ -10,12 +10,12 @@
 
 #include "base/macros.h"
 #include "base/observer_list.h"
-#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/network_dropdown.h"
 
 namespace chromeos {
 
-class NetworkDropdownHandler : public BaseScreenHandler,
+class NetworkDropdownHandler : public BaseWebUIHandler,
                                public NetworkDropdown::View {
  public:
   class Observer {
diff --git a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
index 00427f8..e20720e8 100644
--- a/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/network_screen_handler.cc
@@ -55,7 +55,7 @@
 // NetworkScreenHandler, public: -----------------------------------------------
 
 NetworkScreenHandler::NetworkScreenHandler(CoreOobeView* core_oobe_view)
-    : core_oobe_view_(core_oobe_view) {
+    : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {
   set_call_js_prefix(kJsScreenPath);
   DCHECK(core_oobe_view_);
 }
@@ -97,7 +97,7 @@
   network_screen_params.SetBoolean("isDeveloperMode",
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kSystemDevMode));
-  ShowScreenWithData(OobeScreen::SCREEN_OOBE_NETWORK, &network_screen_params);
+  ShowScreenWithData(kScreenId, &network_screen_params);
   core_oobe_view_->InitDemoModeDetection();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index 281702d..84e4b4f 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -218,46 +218,27 @@
   auto core_handler =
       base::MakeUnique<CoreOobeHandler>(this, js_calls_container.get());
   core_handler_ = core_handler.get();
-  AddScreenHandler(std::move(core_handler));
+  AddWebUIHandler(std::move(core_handler));
   core_handler_->SetDelegate(this);
 
   auto network_dropdown_handler = base::MakeUnique<NetworkDropdownHandler>();
   network_dropdown_handler_ = network_dropdown_handler.get();
-  AddScreenHandler(std::move(network_dropdown_handler));
+  AddWebUIHandler(std::move(network_dropdown_handler));
 
-  auto update_screen_handler = base::MakeUnique<UpdateScreenHandler>();
-  update_view_ = update_screen_handler.get();
-  AddScreenHandler(std::move(update_screen_handler));
+  AddScreenHandler(base::MakeUnique<UpdateScreenHandler>());
 
-  if (display_type_ == kOobeDisplay) {
-    auto network_screen_handler =
-        base::MakeUnique<NetworkScreenHandler>(core_handler_);
-    network_view_ = network_screen_handler.get();
-    AddScreenHandler(std::move(network_screen_handler));
-  }
+  if (display_type_ == kOobeDisplay)
+    AddScreenHandler(base::MakeUnique<NetworkScreenHandler>(core_handler_));
 
-  auto debugging_screen_handler =
-      base::MakeUnique<EnableDebuggingScreenHandler>();
-  debugging_screen_view_ = debugging_screen_handler.get();
-  AddScreenHandler(std::move(debugging_screen_handler));
+  AddScreenHandler(base::MakeUnique<EnableDebuggingScreenHandler>());
 
-  auto eula_screen_handler = base::MakeUnique<EulaScreenHandler>(core_handler_);
-  eula_view_ = eula_screen_handler.get();
-  AddScreenHandler(std::move(eula_screen_handler));
+  AddScreenHandler(base::MakeUnique<EulaScreenHandler>(core_handler_));
 
-  auto reset_screen_handler = base::MakeUnique<ResetScreenHandler>();
-  reset_view_ = reset_screen_handler.get();
-  AddScreenHandler(std::move(reset_screen_handler));
+  AddScreenHandler(base::MakeUnique<ResetScreenHandler>());
 
-  auto autolaunch_screen_handler =
-      base::MakeUnique<KioskAutolaunchScreenHandler>();
-  autolaunch_screen_view_ = autolaunch_screen_handler.get();
-  AddScreenHandler(std::move(autolaunch_screen_handler));
+  AddScreenHandler(base::MakeUnique<KioskAutolaunchScreenHandler>());
 
-  auto kiosk_enable_screen_handler =
-      base::MakeUnique<KioskEnableScreenHandler>();
-  kiosk_enable_screen_view_ = kiosk_enable_screen_handler.get();
-  AddScreenHandler(std::move(kiosk_enable_screen_handler));
+  AddScreenHandler(base::MakeUnique<KioskEnableScreenHandler>());
 
   auto supervised_user_creation_screen_handler =
       base::MakeUnique<SupervisedUserCreationScreenHandler>();
@@ -265,89 +246,51 @@
       supervised_user_creation_screen_handler.get();
   AddScreenHandler(std::move(supervised_user_creation_screen_handler));
 
-  auto wrong_hwid_screen_handler = base::MakeUnique<WrongHWIDScreenHandler>();
-  wrong_hwid_screen_view_ = wrong_hwid_screen_handler.get();
-  AddScreenHandler(std::move(wrong_hwid_screen_handler));
+  AddScreenHandler(base::MakeUnique<WrongHWIDScreenHandler>());
 
-  auto auto_enrollment_check_screen_handler =
-      base::MakeUnique<AutoEnrollmentCheckScreenHandler>();
-  auto_enrollment_check_screen_view_ =
-      auto_enrollment_check_screen_handler.get();
-  AddScreenHandler(std::move(auto_enrollment_check_screen_handler));
+  AddScreenHandler(base::MakeUnique<AutoEnrollmentCheckScreenHandler>());
 
-  auto hid_detection_screen_handler =
-      base::MakeUnique<HIDDetectionScreenHandler>(core_handler_);
-  hid_detection_view_ = hid_detection_screen_handler.get();
-  AddScreenHandler(std::move(hid_detection_screen_handler));
+  AddScreenHandler(base::MakeUnique<HIDDetectionScreenHandler>(core_handler_));
 
-  auto error_screen_handler = base::MakeUnique<ErrorScreenHandler>();
-  error_screen_handler_ = error_screen_handler.get();
-  AddScreenHandler(std::move(error_screen_handler));
-  network_dropdown_handler_->AddObserver(error_screen_handler_);
+  AddScreenHandler(base::MakeUnique<ErrorScreenHandler>());
+  network_dropdown_handler_->AddObserver(GetView<ErrorScreenHandler>());
 
-  error_screen_.reset(new ErrorScreen(nullptr, error_screen_handler_));
+  error_screen_.reset(new ErrorScreen(nullptr, GetView<ErrorScreenHandler>()));
   ErrorScreen* error_screen = error_screen_.get();
 
-  auto enrollment_screen_handler = base::MakeUnique<EnrollmentScreenHandler>(
-      network_state_informer_, error_screen);
-  enrollment_screen_view_ = enrollment_screen_handler.get();
-  AddScreenHandler(std::move(enrollment_screen_handler));
+  AddScreenHandler(base::MakeUnique<EnrollmentScreenHandler>(
+      network_state_informer_, error_screen));
 
-  auto terms_of_service_screen_handler =
-      base::MakeUnique<TermsOfServiceScreenHandler>(core_handler_);
-  terms_of_service_screen_view_ = terms_of_service_screen_handler.get();
-  AddScreenHandler(std::move(terms_of_service_screen_handler));
+  AddScreenHandler(
+      base::MakeUnique<TermsOfServiceScreenHandler>(core_handler_));
 
-  auto arc_terms_of_service_screen_handler =
-      base::MakeUnique<ArcTermsOfServiceScreenHandler>();
-  arc_terms_of_service_screen_view_ = arc_terms_of_service_screen_handler.get();
-  AddScreenHandler(std::move(arc_terms_of_service_screen_handler));
+  AddScreenHandler(base::MakeUnique<ArcTermsOfServiceScreenHandler>());
 
-  auto user_image_screen_handler = base::MakeUnique<UserImageScreenHandler>();
-  user_image_view_ = user_image_screen_handler.get();
-  AddScreenHandler(std::move(user_image_screen_handler));
+  AddScreenHandler(base::MakeUnique<UserImageScreenHandler>());
 
-  auto user_board_screen_handler = base::MakeUnique<UserBoardScreenHandler>();
-  user_board_screen_handler_ = user_board_screen_handler.get();
-  AddScreenHandler(std::move(user_board_screen_handler));
+  AddScreenHandler(base::MakeUnique<UserBoardScreenHandler>());
 
-  auto gaia_screen_handler = base::MakeUnique<GaiaScreenHandler>(
-      core_handler_, network_state_informer_);
-  gaia_screen_handler_ = gaia_screen_handler.get();
-  AddScreenHandler(std::move(gaia_screen_handler));
+  AddScreenHandler(base::MakeUnique<GaiaScreenHandler>(
+      core_handler_, network_state_informer_));
 
   auto signin_screen_handler = base::MakeUnique<SigninScreenHandler>(
       network_state_informer_, error_screen, core_handler_,
-      gaia_screen_handler_, js_calls_container.get());
+      GetView<GaiaScreenHandler>(), js_calls_container.get());
   signin_screen_handler_ = signin_screen_handler.get();
-  AddScreenHandler(std::move(signin_screen_handler));
+  AddWebUIHandler(std::move(signin_screen_handler));
 
-  auto app_launch_splash_screen_handler =
-      base::MakeUnique<AppLaunchSplashScreenHandler>(network_state_informer_,
-                                                     error_screen);
-  app_launch_splash_screen_view_ = app_launch_splash_screen_handler.get();
-  AddScreenHandler(std::move(app_launch_splash_screen_handler));
+  AddScreenHandler(base::MakeUnique<AppLaunchSplashScreenHandler>(
+      network_state_informer_, error_screen));
 
-  auto arc_kiosk_splash_screen_handler =
-      base::MakeUnique<ArcKioskSplashScreenHandler>();
-  arc_kiosk_splash_screen_view_ = arc_kiosk_splash_screen_handler.get();
-  AddScreenHandler(std::move(arc_kiosk_splash_screen_handler));
+  AddScreenHandler(base::MakeUnique<ArcKioskSplashScreenHandler>());
 
   if (display_type_ == kOobeDisplay) {
-    auto controller_pairing_handler =
-        base::MakeUnique<ControllerPairingScreenHandler>();
-    controller_pairing_screen_view_ = controller_pairing_handler.get();
-    AddScreenHandler(std::move(controller_pairing_handler));
+    AddScreenHandler(base::MakeUnique<ControllerPairingScreenHandler>());
 
-    auto host_pairing_handler = base::MakeUnique<HostPairingScreenHandler>();
-    host_pairing_screen_view_ = host_pairing_handler.get();
-    AddScreenHandler(std::move(host_pairing_handler));
+    AddScreenHandler(base::MakeUnique<HostPairingScreenHandler>());
   }
 
-  auto device_disabled_screen_handler =
-      base::MakeUnique<DeviceDisabledScreenHandler>();
-  device_disabled_screen_view_ = device_disabled_screen_handler.get();
-  AddScreenHandler(std::move(device_disabled_screen_handler));
+  AddScreenHandler(base::MakeUnique<DeviceDisabledScreenHandler>());
 
   // Initialize KioskAppMenuHandler. Note that it is NOT a screen handler.
   auto kiosk_app_menu_handler =
@@ -386,7 +329,7 @@
 
 OobeUI::~OobeUI() {
   core_handler_->SetDelegate(nullptr);
-  network_dropdown_handler_->RemoveObserver(error_screen_handler_);
+  network_dropdown_handler_->RemoveObserver(GetView<ErrorScreenHandler>());
   if (ash_util::IsRunningInMash()) {
     // TODO: Ash needs to expose screen dimming api. See
     // http://crbug.com/646034.
@@ -399,71 +342,71 @@
 }
 
 NetworkView* OobeUI::GetNetworkView() {
-  return network_view_;
+  return GetView<NetworkScreenHandler>();
 }
 
 EulaView* OobeUI::GetEulaView() {
-  return eula_view_;
+  return GetView<EulaScreenHandler>();
 }
 
 UpdateView* OobeUI::GetUpdateView() {
-  return update_view_;
+  return GetView<UpdateScreenHandler>();
 }
 
 EnableDebuggingScreenView* OobeUI::GetEnableDebuggingScreenView() {
-  return debugging_screen_view_;
+  return GetView<EnableDebuggingScreenHandler>();
 }
 
 EnrollmentScreenView* OobeUI::GetEnrollmentScreenView() {
-  return enrollment_screen_view_;
+  return GetView<EnrollmentScreenHandler>();
 }
 
 ResetView* OobeUI::GetResetView() {
-  return reset_view_;
+  return GetView<ResetScreenHandler>();
 }
 
 KioskAutolaunchScreenView* OobeUI::GetKioskAutolaunchScreenView() {
-  return autolaunch_screen_view_;
+  return GetView<KioskAutolaunchScreenHandler>();
 }
 
 KioskEnableScreenView* OobeUI::GetKioskEnableScreenView() {
-  return kiosk_enable_screen_view_;
+  return GetView<KioskEnableScreenHandler>();
 }
 
 TermsOfServiceScreenView* OobeUI::GetTermsOfServiceScreenView() {
-  return terms_of_service_screen_view_;
+  return GetView<TermsOfServiceScreenHandler>();
 }
 
 ArcTermsOfServiceScreenView* OobeUI::GetArcTermsOfServiceScreenView() {
-  return arc_terms_of_service_screen_view_;
+  return GetView<ArcTermsOfServiceScreenHandler>();
 }
 
 WrongHWIDScreenView* OobeUI::GetWrongHWIDScreenView() {
-  return wrong_hwid_screen_view_;
+  return GetView<WrongHWIDScreenHandler>();
 }
 
 AutoEnrollmentCheckScreenView* OobeUI::GetAutoEnrollmentCheckScreenView() {
-  return auto_enrollment_check_screen_view_;
+  return GetView<AutoEnrollmentCheckScreenHandler>();
 }
 
 HIDDetectionView* OobeUI::GetHIDDetectionView() {
-  return hid_detection_view_;
+  return GetView<HIDDetectionScreenHandler>();
 }
 
 ControllerPairingScreenView* OobeUI::GetControllerPairingScreenView() {
-  return controller_pairing_screen_view_;
+  return GetView<ControllerPairingScreenHandler>();
 }
 
 HostPairingScreenView* OobeUI::GetHostPairingScreenView() {
-  return host_pairing_screen_view_;
+  return GetView<HostPairingScreenHandler>();
 }
 
 DeviceDisabledScreenView* OobeUI::GetDeviceDisabledScreenView() {
-  return device_disabled_screen_view_;
+  return GetView<DeviceDisabledScreenHandler>();
 }
 
 UserImageView* OobeUI::GetUserImageView() {
-  return user_image_view_;
+  return GetView<UserImageScreenHandler>();
 }
 
 ErrorScreen* OobeUI::GetErrorScreen() {
@@ -476,11 +419,11 @@
 }
 
 GaiaView* OobeUI::GetGaiaScreenView() {
-  return gaia_screen_handler_;
+  return GetView<GaiaScreenHandler>();
 }
 
 UserBoardView* OobeUI::GetUserBoardView() {
-  return user_board_screen_handler_;
+  return GetView<UserBoardScreenHandler>();
 }
 
 void OobeUI::OnShutdownPolicyChanged(bool reboot_on_shutdown) {
@@ -488,18 +431,16 @@
 }
 
 AppLaunchSplashScreenView* OobeUI::GetAppLaunchSplashScreenView() {
-  return app_launch_splash_screen_view_;
+  return GetView<AppLaunchSplashScreenHandler>();
 }
 
 ArcKioskSplashScreenView* OobeUI::GetArcKioskSplashScreenView() {
-  return arc_kiosk_splash_screen_view_;
+  return GetView<ArcKioskSplashScreenHandler>();
 }
 
 void OobeUI::GetLocalizedStrings(base::DictionaryValue* localized_strings) {
-  // Note, handlers_[0] is a GenericHandler used by the WebUI.
-  for (size_t i = 0; i < handlers_.size(); ++i) {
-    handlers_[i]->GetLocalizedStrings(localized_strings);
-  }
+  for (BaseWebUIHandler* handler : webui_handlers_)
+    handler->GetLocalizedStrings(localized_strings);
   const std::string& app_locale = g_browser_process->GetApplicationLocale();
   webui::SetLoadTimeDataDefaults(app_locale, localized_strings);
   kiosk_app_menu_handler_->GetLocalizedStrings(localized_strings);
@@ -531,8 +472,14 @@
   localized_strings->SetString("newOobeUI", oobe_ui_md_mode_ ? "on" : "off");
 }
 
+void OobeUI::AddWebUIHandler(std::unique_ptr<BaseWebUIHandler> handler) {
+  webui_handlers_.push_back(handler.get());
+  web_ui()->AddMessageHandler(std::move(handler));
+}
+
 void OobeUI::AddScreenHandler(std::unique_ptr<BaseScreenHandler> handler) {
-  handlers_.push_back(handler.get());
+  webui_handlers_.push_back(handler.get());
+  screen_handlers_.push_back(handler.get());
   web_ui()->AddMessageHandler(std::move(handler));
 }
 
@@ -543,9 +490,9 @@
   ready_callbacks_.clear();
 
   // Notify 'initialize' for synchronously loaded screens.
-  for (size_t i = 0; i < handlers_.size(); ++i) {
-    if (handlers_[i]->async_assets_load_id().empty())
-      handlers_[i]->InitializeBase();
+  for (BaseWebUIHandler* handler : webui_handlers_) {
+    if (handler->async_assets_load_id().empty())
+      handler->InitializeBase();
   }
 
   // Instantiate the ShutdownPolicyHandler.
@@ -559,9 +506,9 @@
 void OobeUI::OnScreenAssetsLoaded(const std::string& async_assets_load_id) {
   DCHECK(!async_assets_load_id.empty());
 
-  for (size_t i = 0; i < handlers_.size(); ++i) {
-    if (handlers_[i]->async_assets_load_id() == async_assets_load_id)
-      handlers_[i]->InitializeBase();
+  for (BaseWebUIHandler* handler : webui_handlers_) {
+    if (handler->async_assets_load_id() == async_assets_load_id)
+      handler->InitializeBase();
   }
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
index 73748fe..b7d9be0 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -16,6 +16,7 @@
 #include "base/observer_list.h"
 #include "chrome/browser/chromeos/login/oobe_screen.h"
 #include "chrome/browser/chromeos/settings/shutdown_policy_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/core_oobe_handler.h"
 #include "content/public/browser/web_ui_controller.h"
 
@@ -40,9 +41,7 @@
 class EnrollmentScreenView;
 class EulaView;
 class ErrorScreen;
-class ErrorScreenHandler;
 class GaiaView;
-class GaiaScreenHandler;
 class HIDDetectionView;
 class HostPairingScreenView;
 class KioskAppMenuHandler;
@@ -58,7 +57,6 @@
 class SupervisedUserCreationScreenHandler;
 class ResetView;
 class TermsOfServiceScreenView;
-class UserBoardScreenHandler;
 class UserBoardView;
 class UserImageView;
 class UpdateView;
@@ -169,6 +167,21 @@
   void UpdateLocalizedStringsIfNeeded();
 
  private:
+  // Lookup a view by its statically registered OobeScreen.
+  template <typename TView>
+  TView* GetView() {
+    OobeScreen expected_screen = TView::kScreenId;
+    for (BaseScreenHandler* handler : screen_handlers_) {
+      if (expected_screen == handler->oobe_screen())
+        return static_cast<TView*>(handler);
+    }
+
+    NOTREACHED() << "Unable to find handler for screen "
+                 << GetOobeScreenName(expected_screen);
+    return nullptr;
+  }
+
+  void AddWebUIHandler(std::unique_ptr<BaseWebUIHandler> handler);
   void AddScreenHandler(std::unique_ptr<BaseScreenHandler> handler);
 
   // CoreOobeHandler::Delegate implementation:
@@ -188,47 +201,14 @@
   // network dropdown.
   NetworkDropdownHandler* network_dropdown_handler_ = nullptr;
 
-  // Screens views. Note, OobeUI owns them via |handlers_|, not directly here.
-  UpdateView* update_view_ = nullptr;
-  NetworkView* network_view_ = nullptr;
-  EnableDebuggingScreenView* debugging_screen_view_ = nullptr;
-  EulaView* eula_view_ = nullptr;
-  EnrollmentScreenView* enrollment_screen_view_ = nullptr;
-  ResetView* reset_view_ = nullptr;
-  HIDDetectionView* hid_detection_view_ = nullptr;
-  KioskAutolaunchScreenView* autolaunch_screen_view_ = nullptr;
-  KioskEnableScreenView* kiosk_enable_screen_view_ = nullptr;
-  WrongHWIDScreenView* wrong_hwid_screen_view_ = nullptr;
-  AutoEnrollmentCheckScreenView* auto_enrollment_check_screen_view_ = nullptr;
   SupervisedUserCreationScreenHandler* supervised_user_creation_screen_view_ =
       nullptr;
-  AppLaunchSplashScreenView* app_launch_splash_screen_view_ = nullptr;
-  ArcKioskSplashScreenView* arc_kiosk_splash_screen_view_ = nullptr;
-  ControllerPairingScreenView* controller_pairing_screen_view_ = nullptr;
-  HostPairingScreenView* host_pairing_screen_view_ = nullptr;
-  DeviceDisabledScreenView* device_disabled_screen_view_ = nullptr;
-
-  // Reference to ErrorScreenHandler that handles error screen
-  // requests and forward calls from native code to JS side.
-  ErrorScreenHandler* error_screen_handler_ = nullptr;
-
-  // Reference to GaiaScreenHandler that handles gaia screen requests and
-  // forwards calls from native code to JS side.
-  GaiaScreenHandler* gaia_screen_handler_ = nullptr;
-
-  // Reference to UserBoardScreenHandler, that allows to pick user on device
-  // and attempt authentication.
-  UserBoardScreenHandler* user_board_screen_handler_ = nullptr;
-
   // Reference to SigninScreenHandler that handles sign-in screen requests and
   // forwards calls from native code to JS side.
   SigninScreenHandler* signin_screen_handler_ = nullptr;
 
-  TermsOfServiceScreenView* terms_of_service_screen_view_ = nullptr;
-  ArcTermsOfServiceScreenView* arc_terms_of_service_screen_view_ = nullptr;
-  UserImageView* user_image_view_ = nullptr;
-
-  std::vector<BaseScreenHandler*> handlers_;  // Non-owning pointers.
+  std::vector<BaseWebUIHandler*> webui_handlers_;    // Non-owning pointers.
+  std::vector<BaseScreenHandler*> screen_handlers_;  // Non-owning pointers.
 
   KioskAppMenuHandler* kiosk_app_menu_handler_ =
       nullptr;  // Non-owning pointers.
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
index 3906059..38a49d3 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
@@ -27,7 +27,7 @@
 
 namespace chromeos {
 
-ResetScreenHandler::ResetScreenHandler() {
+ResetScreenHandler::ResetScreenHandler() : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -41,7 +41,7 @@
     show_on_init_ = true;
     return;
   }
-  ShowScreen(OobeScreen::SCREEN_OOBE_RESET);
+  ShowScreen(kScreenId);
 }
 
 void ResetScreenHandler::Hide() {
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 06367218..c03f474 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -293,7 +293,7 @@
     CoreOobeView* core_oobe_view,
     GaiaScreenHandler* gaia_screen_handler,
     JSCallsContainer* js_calls_container)
-    : BaseScreenHandler(js_calls_container),
+    : BaseWebUIHandler(js_calls_container),
       network_state_informer_(network_state_informer),
       error_screen_(error_screen),
       core_oobe_view_(core_oobe_view),
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 861ac63..d64b327 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -21,7 +21,7 @@
 #include "chrome/browser/chromeos/login/signin_specifics.h"
 #include "chrome/browser/chromeos/login/ui/login_display.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
-#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_webui_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "chromeos/dbus/power_manager_client.h"
@@ -218,7 +218,7 @@
 // A class that handles the WebUI hooks in sign-in screen in OobeUI and
 // LoginDisplay.
 class SigninScreenHandler
-    : public BaseScreenHandler,
+    : public BaseWebUIHandler,
       public LoginDisplayWebUIHandler,
       public content::NotificationObserver,
       public NetworkStateInformer::NetworkStateInformerObserver,
diff --git a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
index 8f6221dd..0ac53b7 100644
--- a/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/supervised_user_creation_screen_handler.cc
@@ -36,7 +36,8 @@
 
 namespace chromeos {
 
-SupervisedUserCreationScreenHandler::SupervisedUserCreationScreenHandler() {
+SupervisedUserCreationScreenHandler::SupervisedUserCreationScreenHandler()
+    : BaseScreenHandler(OobeScreen::SCREEN_CREATE_SUPERVISED_USER_FLOW) {
   set_call_js_prefix(kJsScreenPath);
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
   media::SoundsManager* manager = media::SoundsManager::Get();
diff --git a/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
index 351a685a..e927f9b 100644
--- a/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.cc
@@ -38,7 +38,7 @@
 
 TermsOfServiceScreenHandler::TermsOfServiceScreenHandler(
     CoreOobeView* core_oobe_view)
-    : core_oobe_view_(core_oobe_view) {
+    : BaseScreenHandler(kScreenId), core_oobe_view_(core_oobe_view) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -169,7 +169,7 @@
   // Update the UI to show an error message or the Terms of Service.
   UpdateTermsOfServiceInUI();
 
-  ShowScreen(OobeScreen::SCREEN_TERMS_OF_SERVICE);
+  ShowScreen(kScreenId);
 }
 
 void TermsOfServiceScreenHandler::UpdateDomainInUI() {
diff --git a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
index 24be021c..a1ca651 100644
--- a/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/update_screen_handler.cc
@@ -21,7 +21,7 @@
 
 namespace chromeos {
 
-UpdateScreenHandler::UpdateScreenHandler() {
+UpdateScreenHandler::UpdateScreenHandler() : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -70,7 +70,7 @@
     show_on_init_ = true;
     return;
   }
-  ShowScreen(OobeScreen::SCREEN_OOBE_UPDATE);
+  ShowScreen(kScreenId);
 }
 
 void UpdateScreenHandler::Hide() {
diff --git a/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc
index aa9ce9be..1596d5a9 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.cc
@@ -9,7 +9,8 @@
 
 namespace chromeos {
 
-UserBoardScreenHandler::UserBoardScreenHandler() : weak_factory_(this) {}
+UserBoardScreenHandler::UserBoardScreenHandler()
+    : BaseScreenHandler(kScreenId), weak_factory_(this) {}
 
 UserBoardScreenHandler::~UserBoardScreenHandler() {
 }
@@ -95,12 +96,12 @@
 
 void UserBoardScreenHandler::Bind(UserSelectionScreen* screen) {
   screen_ = screen;
-  BaseScreenHandler::SetBaseScreen(screen_);
+  BaseWebUIHandler::SetBaseScreen(screen_);
 }
 
 void UserBoardScreenHandler::Unbind() {
   screen_ = nullptr;
-  BaseScreenHandler::SetBaseScreen(nullptr);
+  BaseWebUIHandler::SetBaseScreen(nullptr);
 }
 
 base::WeakPtr<UserBoardView> UserBoardScreenHandler::GetWeakPtr() {
diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
index c1d4841..d313a59 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
@@ -38,7 +38,8 @@
 
 namespace chromeos {
 
-UserImageScreenHandler::UserImageScreenHandler() {
+UserImageScreenHandler::UserImageScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
   media::SoundsManager* manager = media::SoundsManager::Get();
@@ -76,7 +77,7 @@
     return;
   }
   screen_show_time_ = base::Time::Now();
-  ShowScreen(OobeScreen::SCREEN_USER_IMAGE_PICKER);
+  ShowScreen(kScreenId);
 
   // When shown, query camera presence.
   if (screen_ && is_ready_)
diff --git a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
index e38c74c4..87b1920 100644
--- a/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.cc
@@ -16,7 +16,8 @@
 
 namespace chromeos {
 
-WrongHWIDScreenHandler::WrongHWIDScreenHandler() {
+WrongHWIDScreenHandler::WrongHWIDScreenHandler()
+    : BaseScreenHandler(kScreenId) {
   set_call_js_prefix(kJsScreenPath);
 }
 
@@ -30,7 +31,7 @@
     show_on_init_ = true;
     return;
   }
-  ShowScreen(OobeScreen::SCREEN_WRONG_HWID);
+  ShowScreen(kScreenId);
 }
 
 void WrongHWIDScreenHandler::Hide() {
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java
index dfa50a29..47b53b6 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java
@@ -16,6 +16,7 @@
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_shell_apk.ContentShellActivityTestRule.RerunWithUpdatedContainerView;
 import org.chromium.content_shell_apk.ContentShellTestBase;
 
 /**
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewScrollingTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewScrollingTest.java
index 4f77f08..3485bed 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewScrollingTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewScrollingTest.java
@@ -17,6 +17,7 @@
 import org.chromium.content.browser.ContentViewCore.InternalAccessDelegate;
 import org.chromium.content.browser.test.util.Criteria;
 import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content_shell_apk.ContentShellActivityTestRule.RerunWithUpdatedContainerView;
 import org.chromium.content_shell_apk.ContentShellTestBase;
 
 /**
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java
index 380b99f..51529251 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/SelectPopupTest.java
@@ -17,6 +17,7 @@
 import org.chromium.content.browser.test.util.DOMUtils;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper;
+import org.chromium.content_shell_apk.ContentShellActivityTestRule.RerunWithUpdatedContainerView;
 import org.chromium.content_shell_apk.ContentShellTestBase;
 
 import java.util.concurrent.TimeUnit;
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
index d699e7e..d77d300 100644
--- a/content/renderer/media_recorder/video_track_recorder.cc
+++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -30,6 +30,10 @@
 #include "third_party/libyuv/include/libyuv.h"
 #include "ui/gfx/geometry/size.h"
 
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
 #if BUILDFLAG(RTC_USE_H264)
 #include "third_party/openh264/src/codec/api/svc/codec_api.h"
 #include "third_party/openh264/src/codec/api/svc/codec_app_def.h"
@@ -117,6 +121,12 @@
   return;
 #endif
 
+#if defined(OS_WIN)
+  // See https://crbug.com/698441.
+  if (base::win::GetVersion() < base::win::VERSION_WIN10)
+    return;
+#endif
+
   content::RenderThreadImpl* const render_thread_impl =
       content::RenderThreadImpl::current();
   if (!render_thread_impl) {
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 278ab5f..7831a5c 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -144,13 +144,17 @@
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//content/shell/android:content_shell_java",
+    "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
+    "//third_party/junit:junit",
     "//ui/android:ui_java",
   ]
   java_files = [
     "javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java",
     "javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java",
     "javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java",
+    "javatests/src/org/chromium/content_shell_apk/ContentShellTestCommon.java",
+    "javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java",
     "javatests/src/org/chromium/content_shell_apk/ContentShellUrlTest.java",
   ]
 }
diff --git a/content/shell/android/javatests/AndroidManifest.xml b/content/shell/android/javatests/AndroidManifest.xml
index 1f0780d0..1a4f916 100644
--- a/content/shell/android/javatests/AndroidManifest.xml
+++ b/content/shell/android/javatests/AndroidManifest.xml
@@ -16,6 +16,11 @@
         <activity android:name="org.chromium.test.broker.OnDeviceInstrumentationBroker"
             android:exported="true"/>
     </application>
+
+    <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner"
+        android:targetPackage="org.chromium.content_shell_apk"
+        chromium-junit4="true"
+        android:label="JUnit4-based tests for org.chromium.content_shell_apk" />
     <instrumentation android:name="org.chromium.base.test.BaseChromiumInstrumentationTestRunner"
         android:targetPackage="org.chromium.content_shell_apk"
         android:label="Tests for org.chromium.content_shell_apk"/>
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
new file mode 100644
index 0000000..df1fcdf9b
--- /dev/null
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellActivityTestRule.java
@@ -0,0 +1,184 @@
+// 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.
+
+package org.chromium.content_shell_apk;
+
+import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+
+import org.junit.Assert;
+
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.content.browser.ContentView;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.NavigationController;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_shell.Shell;
+import org.chromium.content_shell_apk.ContentShellTestCommon.TestCommonCallback;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * ActivityTestRule for ContentShellActivity.
+ *
+ * Test can use this ActivityTestRule to launch or get ContentShellActivity.
+ */
+public class ContentShellActivityTestRule extends ActivityTestRule<ContentShellActivity>
+        implements TestCommonCallback<ContentShellActivity> {
+    private final ContentShellTestCommon mDelegate;
+    private final boolean mLaunchActivity;
+
+    protected static final long WAIT_PAGE_LOADING_TIMEOUT_SECONDS = scaleTimeout(15);
+
+    public ContentShellActivityTestRule() {
+        this(false, false);
+    }
+
+    public ContentShellActivityTestRule(boolean initialTouchMode, boolean launchActivity) {
+        super(ContentShellActivity.class, initialTouchMode, launchActivity);
+        mLaunchActivity = launchActivity;
+        mDelegate = new ContentShellTestCommon(this);
+    }
+
+    @Override
+    public ContentShellActivity getActivityForTestCommon() {
+        return getActivity();
+    }
+
+    @Override
+    public Instrumentation getInstrumentationForTestCommon() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Override
+    public ContentShellActivity launchActivityWithIntentForTestCommon(Intent t) {
+        return launchActivity(t);
+    }
+
+    @Override
+    public void runOnUiThreadForTestCommon(Runnable runnable) throws Throwable {
+        runOnUiThread(runnable);
+    }
+
+    @Override
+    protected void beforeActivityLaunched() {
+        mDelegate.assertScreenIsOn();
+    }
+
+    /**
+     * Starts the ContentShell activity and loads the given URL.
+     * The URL can be null, in which case will default to ContentShellActivity.DEFAULT_SHELL_URL.
+     */
+    public ContentShellActivity launchContentShellWithUrl(String url) {
+        Assert.assertFalse(mLaunchActivity);
+        return mDelegate.launchContentShellWithUrl(url);
+    }
+
+    /**
+     * Starts the content shell activity with the provided test url.
+     * The url is synchronously loaded.
+     * @param url Test url to load.
+     */
+    public void launchContentShellWithUrlSync(String url) {
+        Assert.assertFalse(mLaunchActivity);
+        mDelegate.launchContentShellWithUrlSync(url);
+    }
+
+    /**
+     * Returns the current ContentViewCore or null if there is no ContentView.
+     */
+    public ContentViewCore getContentViewCore() {
+        return mDelegate.getContentViewCore();
+    }
+
+    /**
+     * Returns the WebContents of this Shell.
+     */
+    public WebContents getWebContents() {
+        return mDelegate.getWebContents();
+    }
+
+    /**
+     * Waits for the Active shell to finish loading.  This times out after
+     * WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT milliseconds and it shouldn't be used for long
+     * loading pages. Instead it should be used more for test initialization. The proper way
+     * to wait is to use a TestCallbackHelperContainer after the initial load is completed.
+     */
+    public void waitForActiveShellToBeDoneLoading() {
+        mDelegate.waitForActiveShellToBeDoneLoading();
+    }
+
+    /**
+     * Creates a new {@link Shell} and waits for it to finish loading.
+     * @param url The URL to create the new {@link Shell} with.
+     * @return A new instance of a {@link Shell}.
+     * @throws ExecutionException
+     */
+    public Shell loadNewShell(String url) throws ExecutionException {
+        return mDelegate.loadNewShell(url);
+    }
+
+    /**
+     * Loads a URL in the specified content view.
+     *
+     * @param navigationController The navigation controller to load the URL in.
+     * @param callbackHelperContainer The callback helper container used to monitor progress.
+     * @param params The URL params to use.
+     */
+    public void loadUrl(NavigationController navigationController,
+            TestCallbackHelperContainer callbackHelperContainer, LoadUrlParams params)
+            throws Throwable {
+        mDelegate.loadUrl(navigationController, callbackHelperContainer, params);
+    }
+
+    /**
+     * Handles performing an action on the UI thread that will return when the specified callback
+     * is incremented.
+     *
+     * @param callbackHelper The callback helper that will be blocked on.
+     * @param action The action to be performed on the UI thread.
+     */
+    public void handleBlockingCallbackAction(CallbackHelper callbackHelper, Runnable action)
+            throws Throwable {
+        mDelegate.handleBlockingCallbackAction(callbackHelper, action);
+    }
+
+    // TODO(aelias): This method needs to be removed once http://crbug.com/179511 is fixed.
+    // Meanwhile, we have to wait if the page has the <meta viewport> tag.
+    /**
+     * Waits till the ContentViewCore receives the expected page scale factor
+     * from the compositor and asserts that this happens.
+     */
+    public void assertWaitForPageScaleFactorMatch(float expectedScale) {
+        mDelegate.assertWaitForPageScaleFactorMatch(expectedScale);
+    }
+
+    /**
+     * Replaces the {@link ContentViewCore#mContainerView} with a newly created
+     * {@link ContentView}.
+     */
+    public void replaceContainerView() throws Throwable {
+        mDelegate.replaceContainerView();
+    }
+
+    /**
+     * Annotation for tests that should be executed a second time after replacing
+     * the ContentViewCore's container view.
+     * <p>Please note that activity launch is only invoked once before both runs,
+     * and that any state changes produced by the first run are visible to the second run.
+     */
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface RerunWithUpdatedContainerView {}
+}
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java
index 21495fc..ab3f798 100644
--- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -8,26 +8,34 @@
 import android.content.Context;
 import android.os.Build;
 import android.os.PowerManager;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 
 /**
  * Test that verifies preconditions for tests to run.
  */
-public class ContentShellPreconditionsTest extends ContentShellTestBase {
+@RunWith(BaseJUnit4ClassRunner.class)
+public class ContentShellPreconditionsTest {
+    @Test
     @TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
     @SuppressWarnings("deprecation")
     @MediumTest
     @Feature({"TestInfrastructure"})
     public void testScreenIsOn() throws Exception {
-        PowerManager pm = (PowerManager) getInstrumentation().getContext().getSystemService(
+        PowerManager pm = (PowerManager) InstrumentationRegistry.getContext().getSystemService(
                 Context.POWER_SERVICE);
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
-            assertTrue("Many tests will fail if the screen is not on.", pm.isInteractive());
+            Assert.assertTrue("Many tests will fail if the screen is not on.", pm.isInteractive());
         } else {
-            assertTrue("Many tests will fail if the screen is not on.", pm.isScreenOn());
+            Assert.assertTrue("Many tests will fail if the screen is not on.", pm.isScreenOn());
         }
     }
 }
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java
index f782574b..e5f81048 100644
--- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellShellManagementTest.java
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// 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.
 
@@ -6,6 +6,12 @@
 
 import android.support.test.filters.SmallTest;
 
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.base.test.util.UrlUtils;
@@ -16,31 +22,36 @@
 /**
  * Test suite to verify the behavior of the shell management logic.
  */
-public class ContentShellShellManagementTest extends ContentShellTestBase {
+@RunWith(BaseJUnit4ClassRunner.class)
+public class ContentShellShellManagementTest {
+    @Rule
+    public ContentShellActivityTestRule mActivityTestRule = new ContentShellActivityTestRule();
 
     private static final String TEST_PAGE_1 = UrlUtils.encodeHtmlDataUri(
             "<html><body style='background: red;'></body></html>");
     private static final String TEST_PAGE_2 = UrlUtils.encodeHtmlDataUri(
             "<html><body style='background: green;'></body></html>");
 
+    @Test
     @SmallTest
     @Feature({"Main"})
     @RetryOnFailure
     public void testMultipleShellsLaunched() throws InterruptedException, ExecutionException {
-        final ContentShellActivity activity = launchContentShellWithUrl(TEST_PAGE_1);
-        assertEquals(TEST_PAGE_1, activity.getActiveShell().getContentViewCore()
-                .getWebContents().getUrl());
+        final ContentShellActivity activity =
+                mActivityTestRule.launchContentShellWithUrl(TEST_PAGE_1);
+        Assert.assertEquals(TEST_PAGE_1,
+                activity.getActiveShell().getContentViewCore().getWebContents().getUrl());
 
         Shell previousActiveShell = activity.getActiveShell();
-        assertFalse(previousActiveShell.isDestroyed());
+        Assert.assertFalse(previousActiveShell.isDestroyed());
 
-        loadNewShell(TEST_PAGE_2);
-        assertEquals(TEST_PAGE_2, activity.getActiveShell().getContentViewCore()
-                .getWebContents().getUrl());
+        mActivityTestRule.loadNewShell(TEST_PAGE_2);
+        Assert.assertEquals(TEST_PAGE_2,
+                activity.getActiveShell().getContentViewCore().getWebContents().getUrl());
 
-        assertNotSame(previousActiveShell, activity.getActiveShell());
-        assertTrue(previousActiveShell.isDestroyed());
-        assertFalse(previousActiveShell.getContentViewCore().isAlive());
+        Assert.assertNotSame(previousActiveShell, activity.getActiveShell());
+        Assert.assertTrue(previousActiveShell.isDestroyed());
+        Assert.assertFalse(previousActiveShell.getContentViewCore().isAlive());
     }
 
 }
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java
index 3fa2a4d..8d1d7386 100644
--- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestBase.java
@@ -4,90 +4,80 @@
 
 package org.chromium.content_shell_apk;
 
-import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
+import android.app.Instrumentation;
 import android.content.Intent;
-import android.net.Uri;
-import android.os.Build;
-import android.os.PowerManager;
-import android.text.TextUtils;
-import android.view.ViewGroup;
 
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseActivityInstrumentationTestCase;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.UrlUtils;
 import org.chromium.content.browser.ContentView;
 import org.chromium.content.browser.ContentViewCore;
-import org.chromium.content.browser.test.util.Criteria;
-import org.chromium.content.browser.test.util.CriteriaHelper;
 import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
 import org.chromium.content.common.ContentSwitches;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_shell.Shell;
+import org.chromium.content_shell_apk.ContentShellActivityTestRule.RerunWithUpdatedContainerView;
+import org.chromium.content_shell_apk.ContentShellTestCommon.TestCommonCallback;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
 import java.lang.reflect.AnnotatedElement;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Base test class for all ContentShell based tests.
  */
 @CommandLineFlags.Add(ContentSwitches.ENABLE_TEST_INTENTS)
-public class ContentShellTestBase
-        extends BaseActivityInstrumentationTestCase<ContentShellActivity> {
-    /** The maximum time the waitForActiveShellToBeDoneLoading method will wait. */
-    private static final long WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT = scaleTimeout(10000);
+public class ContentShellTestBase extends BaseActivityInstrumentationTestCase<ContentShellActivity>
+        implements TestCommonCallback<ContentShellActivity> {
+    protected static final long WAIT_PAGE_LOADING_TIMEOUT_SECONDS =
+            ContentShellTestCommon.WAIT_PAGE_LOADING_TIMEOUT_SECONDS;
 
-    protected static final long WAIT_PAGE_LOADING_TIMEOUT_SECONDS = scaleTimeout(15);
+    private ContentShellTestCommon mDelegate;
 
     public ContentShellTestBase() {
         super(ContentShellActivity.class);
+        mDelegate = new ContentShellTestCommon(this);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public ContentShellActivity getActivityForTestCommon() {
+        return getActivity();
     }
 
     @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        assertScreenIsOn();
+    @SuppressWarnings("deprecation")
+    public Instrumentation getInstrumentationForTestCommon() {
+        return getInstrumentation();
     }
 
-    @TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
     @SuppressWarnings("deprecation")
-    private void assertScreenIsOn() {
-        PowerManager pm = (PowerManager) getInstrumentation().getContext().getSystemService(
-                Context.POWER_SERVICE);
+    @Override
+    public ContentShellActivity launchActivityWithIntentForTestCommon(Intent intent) {
+        setActivityIntent(intent);
+        return getActivity();
+    }
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
-            assertTrue("Many tests will fail if the screen is not on.", pm.isInteractive());
-        } else {
-            assertTrue("Many tests will fail if the screen is not on.", pm.isScreenOn());
-        }
+    @SuppressWarnings("deprecation")
+    @Override
+    public void runOnUiThreadForTestCommon(Runnable runnable) throws Throwable {
+        runTestOnUiThread(runnable);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDelegate.assertScreenIsOn();
     }
 
     /**
      * Starts the ContentShell activity and loads the given URL.
      * The URL can be null, in which case will default to ContentShellActivity.DEFAULT_SHELL_URL.
      */
-    protected ContentShellActivity launchContentShellWithUrl(String url) {
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        if (url != null) intent.setData(Uri.parse(url));
-        intent.setComponent(new ComponentName(getInstrumentation().getTargetContext(),
-                ContentShellActivity.class));
-        setActivityIntent(intent);
-        return getActivity();
+    public ContentShellActivity launchContentShellWithUrl(String url) {
+        return mDelegate.launchContentShellWithUrl(url);
     }
 
     // TODO(cjhopman): These functions are inconsistent with launchContentShell***. Should be
@@ -98,26 +88,22 @@
      * The url is synchronously loaded.
      * @param url Test url to load.
      */
-    protected void startActivityWithTestUrl(String url) {
-        launchContentShellWithUrl(UrlUtils.getIsolatedTestFileUrl(url));
-        assertNotNull(getActivity());
-        waitForActiveShellToBeDoneLoading();
-        assertEquals(UrlUtils.getIsolatedTestFileUrl(url),
-                getContentViewCore().getWebContents().getUrl());
+    public void startActivityWithTestUrl(String url) {
+        mDelegate.launchContentShellWithUrlSync(url);
     }
 
     /**
      * Returns the current ContentViewCore or null if there is no ContentView.
      */
-    protected ContentViewCore getContentViewCore() {
-        return getActivity().getActiveShell().getContentViewCore();
+    public ContentViewCore getContentViewCore() {
+        return mDelegate.getContentViewCore();
     }
 
     /**
      * Returns the WebContents of this Shell.
      */
-    protected WebContents getWebContents() {
-        return getActivity().getActiveShell().getWebContents();
+    public WebContents getWebContents() {
+        return mDelegate.getWebContents();
     }
 
     /**
@@ -126,33 +112,8 @@
      * loading pages. Instead it should be used more for test initialization. The proper way
      * to wait is to use a TestCallbackHelperContainer after the initial load is completed.
      */
-    protected void waitForActiveShellToBeDoneLoading() {
-        final ContentShellActivity activity = getActivity();
-
-        // Wait for the Content Shell to be initialized.
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                Shell shell = activity.getActiveShell();
-                // There are two cases here that need to be accounted for.
-                // The first is that we've just created a Shell and it isn't
-                // loading because it has no URL set yet.  The second is that
-                // we've set a URL and it actually is loading.
-                if (shell == null) {
-                    updateFailureReason("Shell is null.");
-                    return false;
-                }
-                if (shell.isLoading()) {
-                    updateFailureReason("Shell is still loading.");
-                    return false;
-                }
-                if (TextUtils.isEmpty(shell.getContentViewCore().getWebContents().getUrl())) {
-                    updateFailureReason("Shell's URL is empty or null.");
-                    return false;
-                }
-                return true;
-            }
-        }, WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+    public void waitForActiveShellToBeDoneLoading() {
+        mDelegate.waitForActiveShellToBeDoneLoading();
     }
 
     /**
@@ -161,22 +122,10 @@
      * @return A new instance of a {@link Shell}.
      * @throws ExecutionException
      */
-    protected Shell loadNewShell(final String url) throws ExecutionException {
-        Shell shell = ThreadUtils.runOnUiThreadBlocking(new Callable<Shell>() {
-            @Override
-            public Shell call() {
-                getActivity().getShellManager().launchShell(url);
-                return getActivity().getActiveShell();
-            }
-        });
-
-        assertNotNull("Unable to create shell.", shell);
-        assertEquals("Active shell unexpected.", shell, getActivity().getActiveShell());
-
-        waitForActiveShellToBeDoneLoading();
-
-        return shell;
+    public Shell loadNewShell(String url) throws ExecutionException {
+        return mDelegate.loadNewShell(url);
     }
+
     /**
      * Loads a URL in the specified content view.
      *
@@ -184,18 +133,10 @@
      * @param callbackHelperContainer The callback helper container used to monitor progress.
      * @param params The URL params to use.
      */
-    protected void loadUrl(
-            final NavigationController navigationController,
-            TestCallbackHelperContainer callbackHelperContainer,
-            final LoadUrlParams params) throws Throwable {
-        handleBlockingCallbackAction(
-                callbackHelperContainer.getOnPageFinishedHelper(),
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        navigationController.loadUrl(params);
-                    }
-                });
+    public void loadUrl(NavigationController navigationController,
+            TestCallbackHelperContainer callbackHelperContainer, LoadUrlParams params)
+            throws Throwable {
+        mDelegate.loadUrl(navigationController, callbackHelperContainer, params);
     }
 
     /**
@@ -205,12 +146,9 @@
      * @param callbackHelper The callback helper that will be blocked on.
      * @param action The action to be performed on the UI thread.
      */
-    protected void handleBlockingCallbackAction(
-            CallbackHelper callbackHelper, Runnable action) throws Throwable {
-        int currentCallCount = callbackHelper.getCallCount();
-        runTestOnUiThread(action);
-        callbackHelper.waitForCallback(
-                currentCallCount, 1, WAIT_PAGE_LOADING_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    public void handleBlockingCallbackAction(CallbackHelper callbackHelper, Runnable action)
+            throws Throwable {
+        mDelegate.handleBlockingCallbackAction(callbackHelper, action);
     }
 
     // TODO(aelias): This method needs to be removed once http://crbug.com/179511 is fixed.
@@ -219,14 +157,8 @@
      * Waits till the ContentViewCore receives the expected page scale factor
      * from the compositor and asserts that this happens.
      */
-    protected void assertWaitForPageScaleFactorMatch(float expectedScale) {
-        CriteriaHelper.pollInstrumentationThread(
-                Criteria.equals(expectedScale, new Callable<Float>() {
-                    @Override
-                    public Float call() {
-                        return getContentViewCore().getScale();
-                    }
-                }));
+    public void assertWaitForPageScaleFactorMatch(float expectedScale) {
+        mDelegate.assertWaitForPageScaleFactorMatch(expectedScale);
     }
 
     /**
@@ -234,19 +166,11 @@
      * {@link ContentView}.
      */
     @SuppressWarnings("javadoc")
-    protected void replaceContainerView() throws Throwable {
-        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
-                @Override
-            public void run() {
-                ContentView cv = ContentView.createContentView(getActivity(), getContentViewCore());
-                ((ViewGroup) getContentViewCore().getContainerView().getParent()).addView(cv);
-                getContentViewCore().setContainerView(cv);
-                getContentViewCore().setContainerViewInternals(cv);
-                cv.requestFocus();
-            }
-        });
+    public void replaceContainerView() throws Throwable {
+        mDelegate.replaceContainerView();
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     protected void runTest() throws Throwable {
         super.runTest();
@@ -261,16 +185,4 @@
                     + " See ContentShellTestBase#runTest.", e);
         }
     }
-
-    /**
-     * Annotation for tests that should be executed a second time after replacing
-     * the ContentViewCore's container view (see {@link #runTest()}).
-     *
-     * <p>Please note that {@link #setUp()} is only invoked once before both runs,
-     * and that any state changes produced by the first run are visible to the second run.
-     */
-    @Target(ElementType.METHOD)
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface RerunWithUpdatedContainerView {
-    }
 }
diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestCommon.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestCommon.java
new file mode 100644
index 0000000..263723f
--- /dev/null
+++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellTestCommon.java
@@ -0,0 +1,195 @@
+// 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.
+
+package org.chromium.content_shell_apk;
+
+import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.PowerManager;
+import android.text.TextUtils;
+import android.view.ViewGroup;
+
+import org.junit.Assert;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.UrlUtils;
+import org.chromium.content.browser.ContentView;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.content.browser.test.util.Criteria;
+import org.chromium.content.browser.test.util.CriteriaHelper;
+import org.chromium.content.browser.test.util.TestCallbackHelperContainer;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.NavigationController;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.content_shell.Shell;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implementation of utility methods for ContentShellTestBase and ContentShellActivityTestRule to
+ * wrap around during instrumentation test JUnit3 to JUnit4 migration
+ *
+ * Please do not use this class' methods in places other than {@link ContentShellTestBase}
+ * and {@link ContentShellActivityTestRule}
+ */
+public final class ContentShellTestCommon {
+    /** The maximum time the waitForActiveShellToBeDoneLoading method will wait. */
+    private static final long WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT = scaleTimeout(10000);
+    static final long WAIT_PAGE_LOADING_TIMEOUT_SECONDS = scaleTimeout(15);
+
+    private final TestCommonCallback<ContentShellActivity> mCallback;
+
+    ContentShellTestCommon(TestCommonCallback<ContentShellActivity> callback) {
+        mCallback = callback;
+    }
+
+    @TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
+    @SuppressWarnings("deprecation")
+    void assertScreenIsOn() {
+        PowerManager pm = (PowerManager) mCallback.getInstrumentationForTestCommon()
+                                  .getContext()
+                                  .getSystemService(Context.POWER_SERVICE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
+            Assert.assertTrue("Many tests will fail if the screen is not on.", pm.isInteractive());
+        } else {
+            Assert.assertTrue("Many tests will fail if the screen is not on.", pm.isScreenOn());
+        }
+    }
+
+    ContentShellActivity launchContentShellWithUrl(String url) {
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        if (url != null) intent.setData(Uri.parse(url));
+        intent.setComponent(
+                new ComponentName(mCallback.getInstrumentationForTestCommon().getTargetContext(),
+                        ContentShellActivity.class));
+        return mCallback.launchActivityWithIntentForTestCommon(intent);
+    }
+
+    // TODO(yolandyan): This should use the url exactly without the getIsolatedTestFileUrl call.
+    void launchContentShellWithUrlSync(String url) {
+        String isolatedTestFileUrl = UrlUtils.getIsolatedTestFileUrl(url);
+        launchContentShellWithUrl(isolatedTestFileUrl);
+        Assert.assertNotNull(mCallback.getActivityForTestCommon());
+        waitForActiveShellToBeDoneLoading();
+        Assert.assertEquals(isolatedTestFileUrl, getContentViewCore().getWebContents().getUrl());
+    }
+
+    void waitForActiveShellToBeDoneLoading() {
+        // Wait for the Content Shell to be initialized.
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                Shell shell = mCallback.getActivityForTestCommon().getActiveShell();
+                // There are two cases here that need to be accounted for.
+                // The first is that we've just created a Shell and it isn't
+                // loading because it has no URL set yet.  The second is that
+                // we've set a URL and it actually is loading.
+                if (shell == null) {
+                    updateFailureReason("Shell is null.");
+                    return false;
+                }
+                if (shell.isLoading()) {
+                    updateFailureReason("Shell is still loading.");
+                    return false;
+                }
+                if (TextUtils.isEmpty(shell.getContentViewCore().getWebContents().getUrl())) {
+                    updateFailureReason("Shell's URL is empty or null.");
+                    return false;
+                }
+                return true;
+            }
+        }, WAIT_FOR_ACTIVE_SHELL_LOADING_TIMEOUT, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+    }
+
+    ContentViewCore getContentViewCore() {
+        return mCallback.getActivityForTestCommon().getActiveShell().getContentViewCore();
+    }
+
+    WebContents getWebContents() {
+        return mCallback.getActivityForTestCommon().getActiveShell().getWebContents();
+    }
+
+    void loadUrl(final NavigationController navigationController,
+            TestCallbackHelperContainer callbackHelperContainer, final LoadUrlParams params)
+            throws Throwable {
+        handleBlockingCallbackAction(
+                callbackHelperContainer.getOnPageFinishedHelper(), new Runnable() {
+                    @Override
+                    public void run() {
+                        navigationController.loadUrl(params);
+                    }
+                });
+    }
+
+    Shell loadNewShell(final String url) throws ExecutionException {
+        Shell shell = ThreadUtils.runOnUiThreadBlocking(new Callable<Shell>() {
+            @Override
+            public Shell call() {
+                mCallback.getActivityForTestCommon().getShellManager().launchShell(url);
+                return mCallback.getActivityForTestCommon().getActiveShell();
+            }
+        });
+        Assert.assertNotNull("Unable to create shell.", shell);
+        Assert.assertEquals("Active shell unexpected.", shell,
+                mCallback.getActivityForTestCommon().getActiveShell());
+        waitForActiveShellToBeDoneLoading();
+        return shell;
+    }
+
+    void handleBlockingCallbackAction(CallbackHelper callbackHelper, Runnable uiThreadAction)
+            throws Throwable {
+        int currentCallCount = callbackHelper.getCallCount();
+        mCallback.runOnUiThreadForTestCommon(uiThreadAction);
+        callbackHelper.waitForCallback(
+                currentCallCount, 1, WAIT_PAGE_LOADING_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    void assertWaitForPageScaleFactorMatch(float expectedScale) {
+        CriteriaHelper.pollInstrumentationThread(
+                Criteria.equals(expectedScale, new Callable<Float>() {
+                    @Override
+                    public Float call() {
+                        return getContentViewCore().getScale();
+                    }
+                }));
+    }
+
+    void replaceContainerView() throws Throwable {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                ContentView cv = ContentView.createContentView(
+                        mCallback.getActivityForTestCommon(), getContentViewCore());
+                ((ViewGroup) getContentViewCore().getContainerView().getParent()).addView(cv);
+                getContentViewCore().setContainerView(cv);
+                getContentViewCore().setContainerViewInternals(cv);
+                cv.requestFocus();
+            }
+        });
+    }
+
+    /**
+     * Interface used by TestRule and TestBase class to implement methods for TestCommonCallback
+     * class to use.
+     */
+    public static interface TestCommonCallback<T extends Activity> {
+        Instrumentation getInstrumentationForTestCommon();
+        T launchActivityWithIntentForTestCommon(Intent t);
+        T getActivityForTestCommon();
+        void runOnUiThreadForTestCommon(Runnable runnable) throws Throwable;
+    }
+}
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/nested-and-unbreakable-crash.html b/third_party/WebKit/LayoutTests/fast/multicol/nested-and-unbreakable-crash.html
new file mode 100644
index 0000000..424e469
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/nested-and-unbreakable-crash.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<p>PASS if no crash or assertion failure.</p>
+<div style="columns:7; column-fill:auto; height:20px;">
+    <div style="columns:2;">
+        <div style="columns:1; overflow-y:scroll; column-fill:auto; height:1px; margin-top:1em;">
+            <div style="height:50px;"></div>
+            <div style="margin-top:1px;"></div>
+        </div>
+    </div>
+</div>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+test(() => { }, "No crash or assertion failure.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/nested-writing-mode-root-crash.html b/third_party/WebKit/LayoutTests/fast/multicol/nested-writing-mode-root-crash.html
new file mode 100644
index 0000000..becdc782
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/multicol/nested-writing-mode-root-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<p>PASS if no crash or assertion failure.</p>
+<div style="columns:2;">
+    <div style="columns:2; writing-mode:vertical-rl;"><br></div>
+</div>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+    test(() => { }, "No crash or assertion failure.");
+</script>
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
index 488e4ac..d3bba0a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.cpp
@@ -612,9 +612,16 @@
 }
 
 FragmentationContext*
-LayoutMultiColumnFlowThread::enclosingFragmentationContext() const {
-  if (LayoutMultiColumnFlowThread* enclosingFlowThread =
-          this->enclosingFlowThread())
+LayoutMultiColumnFlowThread::enclosingFragmentationContext(
+    AncestorSearchConstraint constraint) const {
+  // If this multicol container is strictly unbreakable (due to having
+  // scrollbars, for instance), it's also strictly unbreakable in any outer
+  // fragmentation context. As such, what kind of fragmentation that goes on
+  // inside this multicol container is completely opaque to the ancestors.
+  if (constraint == IsolateUnbreakableContainers &&
+      multiColumnBlockFlow()->getPaginationBreakability() == ForbidBreaks)
+    return nullptr;
+  if (auto* enclosingFlowThread = this->enclosingFlowThread())
     return enclosingFlowThread;
   return view()->fragmentationContext();
 }
diff --git a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.h b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.h
index 8264eb4..91fb0d7 100644
--- a/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.h
+++ b/third_party/WebKit/Source/core/layout/LayoutMultiColumnFlowThread.h
@@ -260,10 +260,25 @@
   bool removeSpannerPlaceholderIfNoLongerValid(
       LayoutBox* spannerObjectInFlowThread);
 
+  // Search mode when looking for an enclosing fragmentation context.
+  enum AncestorSearchConstraint {
+    // No constraints. Sometimes we just want to find all enclosing
+    // fragmentation contexts, e.g. to calculate the accumulated visual
+    // translation.
+    AnyAncestor,
+
+    // Consider fragmentation contexts that are strictly unbreakable (seen from
+    // the outside) to be isolated from the rest, so that such fragmentation
+    // contexts don't participate in fragmentation of enclosing fragmentation
+    // contexts, apart from taking up space and otherwise being completely
+    // unbreakable. This is typically what we want to do during layout.
+    IsolateUnbreakableContainers,
+  };
   LayoutMultiColumnFlowThread* enclosingFlowThread() const;
-  FragmentationContext* enclosingFragmentationContext() const;
+  FragmentationContext* enclosingFragmentationContext(
+      AncestorSearchConstraint = IsolateUnbreakableContainers) const;
   LayoutUnit blockOffsetInEnclosingFragmentationContext() const {
-    ASSERT(enclosingFragmentationContext());
+    DCHECK(enclosingFragmentationContext(AnyAncestor));
     return m_blockOffsetInEnclosingFragmentationContext;
   }
 
diff --git a/third_party/WebKit/Source/platform/geometry/FloatQuad.cpp b/third_party/WebKit/Source/platform/geometry/FloatQuad.cpp
index 7fd3481..0a1c2b9 100644
--- a/third_party/WebKit/Source/platform/geometry/FloatQuad.cpp
+++ b/third_party/WebKit/Source/platform/geometry/FloatQuad.cpp
@@ -82,7 +82,7 @@
 static inline float saturateInf(float value) {
   if (UNLIKELY(std::isinf(value))) {
     return std::signbit(value) ? std::numeric_limits<int>::min()
-                               : std::numeric_limits<int>::min();
+                               : std::numeric_limits<int>::max();
   }
   return value;
 }
diff --git a/third_party/WebKit/Source/platform/geometry/FloatQuadTest.cpp b/third_party/WebKit/Source/platform/geometry/FloatQuadTest.cpp
index 9f918c8..d9cd465 100644
--- a/third_party/WebKit/Source/platform/geometry/FloatQuadTest.cpp
+++ b/third_party/WebKit/Source/platform/geometry/FloatQuadTest.cpp
@@ -4,6 +4,7 @@
 
 #include "platform/geometry/FloatQuad.h"
 
+#include <limits>
 #include "testing/gtest/include/gtest/gtest.h"
 #include "wtf/text/WTFString.h"
 
@@ -15,4 +16,24 @@
   EXPECT_EQ("2,3; 5,7; 11,13; 17,19", quad.toString());
 }
 
+TEST(FloatQuadTest, BoundingBox) {
+  FloatQuad quad(FloatPoint(2, 3), FloatPoint(5, 7), FloatPoint(11, 13),
+                 FloatPoint(17, 19));
+  FloatRect rect = quad.boundingBox();
+  EXPECT_EQ(rect.x(), 2);
+  EXPECT_EQ(rect.y(), 3);
+  EXPECT_EQ(rect.width(), 17 - 2);
+  EXPECT_EQ(rect.height(), 19 - 3);
+}
+
+TEST(FloatQuadTest, BoundingBoxSaturateInf) {
+  double inf = std::numeric_limits<double>::infinity();
+  FloatQuad quad(FloatPoint(-inf, 3), FloatPoint(5, inf), FloatPoint(11, 13),
+                 FloatPoint(17, 19));
+  FloatRect rect = quad.boundingBox();
+  EXPECT_EQ(rect.x(), std::numeric_limits<int>::min());
+  EXPECT_EQ(rect.y(), 3.0f);
+  EXPECT_EQ(rect.width(), 17.0f - std::numeric_limits<int>::min());
+  EXPECT_EQ(rect.height(), std::numeric_limits<int>::max() - 3.0f);
+}
 }  // namespace blink
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ab8c54ad..f834465 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -63918,6 +63918,15 @@
   </summary>
 </histogram>
 
+<histogram name="Settings.TimeUntilInteractive" units="ms">
+  <owner>dbeam@chromium.org</owner>
+  <summary>
+    The time until the settings Web UI is loaded, rendered, and interactive for
+    users (as in they can change a setting). Automatically logged each time the
+    settings page is opened (if not closed before interactive).
+  </summary>
+</histogram>
+
 <histogram name="Settings.TrackedPreferenceChanged" enum="TrackedPreference">
   <owner>gab@chromium.org</owner>
   <summary>