diff --git a/DEPS b/DEPS
index 6a0c0ad..cc154a5 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'eee4d6e4e8cc5c4c79f065abcc3ce609f71238f9',
+  'skia_revision': '0e022297fee80add8d2939145f65d3ee56827d03',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 369ff26..30fc6c4 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -40,6 +40,9 @@
 const char kAshEnableMagnifierKeyScroller[] =
     "ash-enable-magnifier-key-scroller";
 
+// Enables the NightLight feature.
+const char kAshEnableNightLight[] = "ash-enable-night-light";
+
 // Enables the palette on every display, instead of only the internal one.
 const char kAshEnablePaletteOnAllDisplays[] =
     "ash-enable-palette-on-all-displays";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 4fc51f54..5aecb7b5 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -23,6 +23,7 @@
 ASH_EXPORT extern const char kAshDisableSmoothScreenRotation[];
 ASH_EXPORT extern const char kAshDisableTouchExplorationMode[];
 ASH_EXPORT extern const char kAshEnableMagnifierKeyScroller[];
+ASH_EXPORT extern const char kAshEnableNightLight[];
 ASH_EXPORT extern const char kAshEnablePaletteOnAllDisplays[];
 ASH_EXPORT extern const char kAshEnableScaleSettingsTray[];
 ASH_EXPORT extern const char kAshEnableTouchView[];
diff --git a/ash/shell.cc b/ash/shell.cc
index 47d5dcf6..661b87f 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -374,6 +374,11 @@
       resolution_notification_controller_->DoesNotificationTimeout());
 }
 
+NightLightController* Shell::night_light_controller() {
+  DCHECK(NightLightController::IsFeatureEnabled());
+  return night_light_controller_.get();
+}
+
 ShelfModel* Shell::shelf_model() {
   return shelf_controller_->model();
 }
@@ -563,8 +568,6 @@
       media_controller_(base::MakeUnique<MediaController>()),
       new_window_controller_(base::MakeUnique<NewWindowController>()),
       session_controller_(base::MakeUnique<SessionController>()),
-      night_light_controller_(
-          base::MakeUnique<NightLightController>(session_controller_.get())),
       shelf_controller_(base::MakeUnique<ShelfController>()),
       shell_delegate_(std::move(shell_delegate)),
       shutdown_controller_(base::MakeUnique<ShutdownController>()),
@@ -792,6 +795,11 @@
 void Shell::Init(const ShellInitParams& init_params) {
   const Config config = shell_port_->GetAshConfig();
 
+  if (NightLightController::IsFeatureEnabled()) {
+    night_light_controller_ =
+        base::MakeUnique<NightLightController>(session_controller_.get());
+  }
+
   blocking_pool_ = init_params.blocking_pool;
 
   wallpaper_delegate_ = shell_delegate_->CreateWallpaperDelegate();
diff --git a/ash/shell.h b/ash/shell.h
index 897c532..e5a000f8 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -341,9 +341,7 @@
   NewWindowController* new_window_controller() {
     return new_window_controller_.get();
   }
-  NightLightController* night_light_controller() {
-    return night_light_controller_.get();
-  }
+  NightLightController* night_light_controller();
   SessionController* session_controller() { return session_controller_.get(); }
   ShelfController* shelf_controller() { return shelf_controller_.get(); }
   ShelfModel* shelf_model();
diff --git a/ash/system/network/network_icon.cc b/ash/system/network/network_icon.cc
index 1174c006..ec6cb5c 100644
--- a/ash/system/network/network_icon.cc
+++ b/ash/system/network/network_icon.cc
@@ -878,13 +878,15 @@
   }
 }
 
-int GetCellularUninitializedMsg() {
+int GetMobileUninitializedMsg() {
   static base::Time s_uninitialized_state_time;
   static int s_uninitialized_msg(0);
 
   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
   if (handler->GetTechnologyState(NetworkTypePattern::Mobile()) ==
       NetworkStateHandler::TECHNOLOGY_UNINITIALIZED) {
+    // TODO (lesliewatkins): Add a more descriptive message (e.g. "Enable
+    // Bluetooth") for Tether technology type.
     s_uninitialized_msg = IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR;
     s_uninitialized_state_time = base::Time::Now();
     return s_uninitialized_msg;
@@ -894,7 +896,7 @@
     return s_uninitialized_msg;
   }
   // There can be a delay between leaving the Initializing state and when
-  // a Cellular device shows up, so keep showing the initializing
+  // a Mobile device shows up, so keep showing the initializing
   // animation for a bit to avoid flashing the disconnect icon.
   const int kInitializingDelaySeconds = 1;
   base::TimeDelta dtime = base::Time::Now() - s_uninitialized_state_time;
@@ -950,8 +952,8 @@
     }
   }
   if (!network) {
-    // If no connecting network, check for cellular initializing.
-    int uninitialized_msg = GetCellularUninitializedMsg();
+    // If no connecting network, check for mobile initializing.
+    int uninitialized_msg = GetMobileUninitializedMsg();
     if (uninitialized_msg != 0) {
       *image = GetConnectingImage(icon_type, shill::kTypeCellular);
       if (label)
diff --git a/ash/system/network/network_icon.h b/ash/system/network/network_icon.h
index 827cacc..a7b2640 100644
--- a/ash/system/network/network_icon.h
+++ b/ash/system/network/network_icon.h
@@ -55,9 +55,9 @@
     const chromeos::NetworkState* network,
     IconType icon_type);
 
-// Updates and returns the appropriate message id if the cellular network
+// Updates and returns the appropriate message id if the mobile network
 // is uninitialized.
-ASH_EXPORT int GetCellularUninitializedMsg();
+ASH_EXPORT int GetMobileUninitializedMsg();
 
 // Gets the correct icon and label for |icon_type|. Also sets |animating|
 // based on whether or not the icon is animating (i.e. connecting).
diff --git a/ash/system/network/network_list.cc b/ash/system/network/network_list.cc
index 3f4cff25..f94b2f6 100644
--- a/ash/system/network/network_list.cc
+++ b/ash/system/network/network_list.cc
@@ -523,7 +523,7 @@
   }
 
   // Cellular initializing.
-  int cellular_message_id = network_icon::GetCellularUninitializedMsg();
+  int cellular_message_id = network_icon::GetMobileUninitializedMsg();
   if (!cellular_message_id &&
       handler->IsTechnologyEnabled(NetworkTypePattern::Mobile()) &&
       !handler->FirstNetworkByType(NetworkTypePattern::Mobile())) {
diff --git a/ash/system/night_light/night_light_controller.cc b/ash/system/night_light/night_light_controller.cc
index 90e3179f..cdbc238a 100644
--- a/ash/system/night_light/night_light_controller.cc
+++ b/ash/system/night_light/night_light_controller.cc
@@ -4,9 +4,11 @@
 
 #include "ash/system/night_light/night_light_controller.h"
 
+#include "ash/ash_switches.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
+#include "base/command_line.h"
 #include "base/time/time.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -57,6 +59,12 @@
 }
 
 // static
+bool NightLightController::IsFeatureEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      ash::switches::kAshEnableNightLight);
+}
+
+// static
 void NightLightController::RegisterPrefs(PrefRegistrySimple* registry) {
   registry->RegisterBooleanPref(prefs::kNightLightEnabled, false);
   registry->RegisterDoublePref(prefs::kNightLightTemperature,
diff --git a/ash/system/night_light/night_light_controller.h b/ash/system/night_light/night_light_controller.h
index 8066d7c..bdef68d 100644
--- a/ash/system/night_light/night_light_controller.h
+++ b/ash/system/night_light/night_light_controller.h
@@ -35,6 +35,9 @@
   explicit NightLightController(SessionController* session_controller);
   ~NightLightController() override;
 
+  // Returns true if the NightLight feature is enabled in the flags.
+  static bool IsFeatureEnabled();
+
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
   void AddObserver(Observer* observer);
diff --git a/ash/system/night_light/night_light_controller_unittest.cc b/ash/system/night_light/night_light_controller_unittest.cc
index 175a01d..8307834d 100644
--- a/ash/system/night_light/night_light_controller_unittest.cc
+++ b/ash/system/night_light/night_light_controller_unittest.cc
@@ -7,6 +7,7 @@
 #include <cmath>
 #include <limits>
 
+#include "ash/ash_switches.h"
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/config.h"
 #include "ash/public/cpp/session_types.h"
@@ -15,6 +16,7 @@
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/test_session_controller_client.h"
 #include "ash/test/test_shell_delegate.h"
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "components/prefs/testing_pref_service.h"
 #include "ui/compositor/layer.h"
@@ -76,6 +78,10 @@
 
   // ash::test::AshTestBase:
   void SetUp() override {
+    // Explicitly enable the NightLight feature for the tests.
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        ash::switches::kAshEnableNightLight);
+
     test::AshTestBase::SetUp();
     CreateTestUserSessions();
     Shell::RegisterPrefs(user1_pref_service_.registry());
diff --git a/ash/system/night_light/tray_night_light_unittest.cc b/ash/system/night_light/tray_night_light_unittest.cc
index 3a8ca56..1491771c 100644
--- a/ash/system/night_light/tray_night_light_unittest.cc
+++ b/ash/system/night_light/tray_night_light_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/night_light/tray_night_light.h"
 
+#include "ash/ash_switches.h"
 #include "ash/public/cpp/config.h"
 #include "ash/shell.h"
 #include "ash/system/night_light/night_light_controller.h"
@@ -11,6 +12,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/test_shell_delegate.h"
+#include "base/command_line.h"
 #include "base/macros.h"
 #include "components/prefs/testing_pref_service.h"
 
@@ -27,6 +29,10 @@
 
   // ash::test::AshTestBase:
   void SetUp() override {
+    // Explicitly enable the NightLight feature for the tests.
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        ash::switches::kAshEnableNightLight);
+
     test::AshTestBase::SetUp();
     GetSessionControllerClient()->Reset();
     GetSessionControllerClient()->AddUserSession(kFakeUserEmail);
diff --git a/ash/system/tiles/tiles_default_view.cc b/ash/system/tiles/tiles_default_view.cc
index dbb5fd4..6a47922 100644
--- a/ash/system/tiles/tiles_default_view.cc
+++ b/ash/system/tiles/tiles_default_view.cc
@@ -86,10 +86,12 @@
   AddChildView(help_button_);
   AddChildView(TrayPopupUtils::CreateVerticalSeparator());
 
-  night_light_button_ = new NightLightToggleButton(this);
-  night_light_button_->SetEnabled(can_show_web_ui);
-  AddChildView(night_light_button_);
-  AddChildView(TrayPopupUtils::CreateVerticalSeparator());
+  if (NightLightController::IsFeatureEnabled()) {
+    night_light_button_ = new NightLightToggleButton(this);
+    night_light_button_->SetEnabled(can_show_web_ui);
+    AddChildView(night_light_button_);
+    AddChildView(TrayPopupUtils::CreateVerticalSeparator());
+  }
 
   lock_button_ =
       new SystemMenuButton(this, TrayPopupInkDropStyle::HOST_CENTERED,
@@ -120,7 +122,8 @@
   } else if (sender == help_button_) {
     ShellPort::Get()->RecordUserMetricsAction(UMA_TRAY_HELP);
     Shell::Get()->system_tray_controller()->ShowHelp();
-  } else if (sender == night_light_button_) {
+  } else if (NightLightController::IsFeatureEnabled() &&
+             sender == night_light_button_) {
     ShellPort::Get()->RecordUserMetricsAction(UMA_TRAY_NIGHT_LIGHT);
     Shell::Get()->night_light_controller()->Toggle();
     night_light_button_->Update();
diff --git a/ash/system/tiles/tray_tiles_unittest.cc b/ash/system/tiles/tray_tiles_unittest.cc
index 7341f72..6599f48 100644
--- a/ash/system/tiles/tray_tiles_unittest.cc
+++ b/ash/system/tiles/tray_tiles_unittest.cc
@@ -4,11 +4,14 @@
 
 #include "ash/system/tiles/tray_tiles.h"
 
+#include "ash/ash_switches.h"
+#include "ash/system/night_light/night_light_controller.h"
 #include "ash/system/night_light/night_light_toggle_button.h"
 #include "ash/system/tiles/tiles_default_view.h"
 #include "ash/system/tray/system_menu_button.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_session_controller_client.h"
+#include "base/command_line.h"
 #include "components/user_manager/user_type.h"
 #include "ui/views/view.h"
 
@@ -23,6 +26,10 @@
   ~TrayTilesTest() override {}
 
   void SetUp() override {
+    // Explicitly enable the NightLight feature for the tests.
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        ash::switches::kAshEnableNightLight);
+
     test::NoSessionAshTestBase::SetUp();
     tray_tiles_.reset(new TrayTiles(GetPrimarySystemTray()));
   }
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 75c9c11..3b09824 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -265,8 +265,10 @@
   AddTrayItem(base::WrapUnique(tray_scale_));
   AddTrayItem(base::MakeUnique<TrayBrightness>(this));
   AddTrayItem(base::MakeUnique<TrayCapsLock>(this));
-  tray_night_light_ = new TrayNightLight(this);
-  AddTrayItem(base::WrapUnique(tray_night_light_));
+  if (NightLightController::IsFeatureEnabled()) {
+    tray_night_light_ = new TrayNightLight(this);
+    AddTrayItem(base::WrapUnique(tray_night_light_));
+  }
   // TODO(jamescook): Remove this when mash has support for display management
   // and we have a DisplayManager equivalent. See http://crbug.com/548429
   if (Shell::GetAshConfig() != Config::MASH)
diff --git a/base/i18n/rtl.cc b/base/i18n/rtl.cc
index 07fd964..2b6bf37 100644
--- a/base/i18n/rtl.cc
+++ b/base/i18n/rtl.cc
@@ -9,7 +9,6 @@
 
 #include <algorithm>
 
-#include "base/atomicops.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/i18n/base_i18n_switches.h"
@@ -114,8 +113,7 @@
 namespace i18n {
 
 // Represents the locale-specific ICU text direction.
-static subtle::Atomic32 g_icu_text_direction =
-    static_cast<subtle::Atomic32>(UNKNOWN_DIRECTION);
+static TextDirection g_icu_text_direction = UNKNOWN_DIRECTION;
 
 // Convert the ICU default locale to a string.
 std::string GetConfiguredLocale() {
@@ -164,10 +162,7 @@
   // presence of actual locale data). However,
   // it does not hurt to have it as a sanity check.
   DCHECK(U_SUCCESS(error_code));
-  subtle::Release_Store(
-      &g_icu_text_direction,
-      static_cast<subtle::Atomic32>(
-          GetTextDirectionForLocaleInStartUp(locale.getName())));
+  g_icu_text_direction = UNKNOWN_DIRECTION;
 }
 
 bool IsRTL() {
@@ -175,11 +170,11 @@
 }
 
 bool ICUIsRTL() {
-  // Note: There is still  a race if this is executed between the
-  // icu::Locale::setDefault and the g_icu_text_direction store
-  // that happens in SetICUDefaultLocale.
-  return static_cast<TextDirection>(
-             subtle::Acquire_Load(&g_icu_text_direction)) == RIGHT_TO_LEFT;
+  if (g_icu_text_direction == UNKNOWN_DIRECTION) {
+    const icu::Locale& locale = icu::Locale::getDefault();
+    g_icu_text_direction = GetTextDirectionForLocaleInStartUp(locale.getName());
+  }
+  return g_icu_text_direction == RIGHT_TO_LEFT;
 }
 
 TextDirection GetTextDirectionForLocaleInStartUp(const char* locale_name) {
diff --git a/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java b/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
index 179e4d6..2a4b19fb 100644
--- a/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
+++ b/build/android/incremental_install/java/org/chromium/incrementalinstall/ClassLoaderPatcher.java
@@ -221,7 +221,7 @@
     private static Object[] makeNativePathElements(File[] paths)
             throws ReflectiveOperationException {
         Object[] entries = new Object[paths.length];
-        if (Build.VERSION.CODENAME.startsWith("O")) {
+        if (Build.VERSION.SDK_INT >= 26) {
             Class<?> entryClazz = Class.forName("dalvik.system.DexPathList$NativeLibraryElement");
             for (int i = 0; i < paths.length; ++i) {
                 entries[i] = Reflect.newInstance(entryClazz, paths[i]);
@@ -253,7 +253,7 @@
                 dexFile = Reflect.invokeMethod(clazz, "loadDexFile", file, optimizedDirectory);
             }
             Object dexElement;
-            if (Build.VERSION.CODENAME.startsWith("O")) {
+            if (Build.VERSION.SDK_INT >= 26) {
                 dexElement = Reflect.newInstance(entryClazz, dexFile, file);
             } else {
                 dexElement = Reflect.newInstance(entryClazz, emptyDir, false, file, dexFile);
diff --git a/build/check_gn_headers_whitelist.txt b/build/check_gn_headers_whitelist.txt
new file mode 100644
index 0000000..d2a8282c
--- /dev/null
+++ b/build/check_gn_headers_whitelist.txt
@@ -0,0 +1,395 @@
+ash/accelerators/accelerator_controller_delegate.h
+ash/accelerators/accelerator_controller_delegate_aura.h
+ash/accelerators/accelerator_table.h
+ash/ash_export.h
+ash/ash_switches.h
+ash/frame/header_painter.h
+ash/metrics/task_switch_metrics_recorder.h
+ash/metrics/task_switch_source.h
+ash/metrics/user_metrics_action.h
+ash/metrics/user_metrics_recorder.h
+ash/public/cpp/ash_public_export.h
+ash/public/cpp/config.h
+ash/public/cpp/shelf_types.h
+ash/session/session_observer.h
+ash/shell.h
+ash/system/devicetype_utils.h
+ash/wm/system_modal_container_event_filter_delegate.h
+cc/base/ring_buffer.h
+cc/blink/web_blend_mode.h
+cc/cc_export.h
+cc/input/browser_controls_state.h
+cc/input/event_listener_properties.h
+cc/input/scrollbar.h
+cc/input/scroller_size_metrics.h
+cc/layers/performance_properties.h
+cc/layers/scrollbar_theme_painter.h
+cc/output/bsp_compare_result.h
+cc/resources/release_callback_impl.h
+cc/resources/return_callback.h
+cc/surfaces/surface_observer.h
+chrome/browser/android/android_theme_resources.h
+chrome/browser/android/resource_id.h
+chrome/browser/chromeos/certificate_provider/certificate_info.h
+chrome/browser/chromeos/certificate_provider/certificate_provider.h
+chrome/browser/chromeos/certificate_provider/certificate_provider_service.h
+chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h
+chrome/browser/chromeos/certificate_provider/certificate_requests.h
+chrome/browser/chromeos/certificate_provider/pin_dialog_manager.h
+chrome/browser/chromeos/certificate_provider/sign_requests.h
+chrome/browser/chromeos/certificate_provider/thread_safe_certificate_map.h
+chrome/browser/chromeos/login/signin/oauth2_login_manager.h
+chrome/browser/chromeos/login/signin/oauth2_login_verifier.h
+chrome/browser/chromeos/login/signin/oauth2_token_fetcher.h
+chrome/browser/chromeos/profiles/profile_helper.h
+chrome/browser/chromeos/settings/cros_settings.h
+chrome/browser/chromeos/ui/request_pin_view.h
+chrome/browser/component_updater/component_installer_errors.h
+chrome/browser/download/download_file_icon_extractor.h
+chrome/browser/extensions/api/networking_cast_private/chrome_networking_cast_private_delegate.h
+chrome/browser/extensions/api/omnibox/omnibox_api_testbase.h
+chrome/browser/extensions/api/socket/mock_tcp_client_socket.h
+chrome/browser/mac/bluetooth_utility.h
+chrome/browser/media/router/mojo/media_route_provider_util_win.h
+chrome/browser/media/webrtc/desktop_media_list_ash.h
+chrome/browser/media/webrtc/desktop_media_list_observer.h
+chrome/browser/media/webrtc/rtp_dump_type.h
+chrome/browser/media_galleries/fileapi/file_path_watcher_util.h
+chrome/browser/media_galleries/fileapi/iapps_data_provider.h
+chrome/browser/media_galleries/fileapi/itunes_data_provider.h
+chrome/browser/media_galleries/fileapi/picasa_data_provider.h
+chrome/browser/media_galleries/fileapi/safe_iapps_library_parser.h
+chrome/browser/media_galleries/media_file_system_context.h
+chrome/browser/notifications/displayed_notifications_dispatch_callback.h
+chrome/browser/permissions/permission_queue_controller.h
+chrome/browser/prefs/active_profile_pref_service.h
+chrome/browser/rlz/chrome_rlz_tracker_delegate.h
+chrome/browser/signin/easy_unlock_service_observer.h
+chrome/browser/ui/android/content_settings/subresource_filter_infobar_delegate.h
+chrome/browser/ui/app_icon_loader_delegate.h
+chrome/browser/ui/app_list/app_list_syncable_service_factory.h
+chrome/browser/ui/ash/ash_util.h
+chrome/browser/ui/ash/multi_user/multi_user_util.h
+chrome/browser/ui/network_profile_bubble.h
+chrome/browser/ui/passwords/manage_passwords_icon.h
+chrome/browser/ui/views/frame/browser_header_painter_ash.h
+chrome/browser/ui/webui/large_icon_source.h
+chrome/common/mac/app_shim_launch.h
+chrome/common/mac/app_shim_messages.h
+chrome/common/media_galleries/itunes_library.h
+chrome/common/media_galleries/picasa_types.h
+chrome/install_static/chromium_install_modes.h
+chrome/install_static/install_constants.h
+chrome/install_static/install_details.h
+chrome/install_static/install_modes.h
+chrome/install_static/install_util.h
+chrome/install_static/test/scoped_install_details.h
+chrome/installer/util/browser_distribution.h
+chrome/installer/util/google_update_constants.h
+chrome/installer/util/google_update_settings.h
+chrome/installer/util/util_constants.h
+chromeos/chromeos_export.h
+chromeos/login/login_state.h
+chromeos/login/scoped_test_public_session_login_state.h
+chromeos/settings/cros_settings_names.h
+chromeos/settings/cros_settings_provider.h
+components/browser_watcher/features.h
+components/browser_watcher/stability_paths.h
+components/cast_certificate/cast_crl_root_ca_cert_der-inc.h
+components/cdm/browser/cdm_message_filter_android.h
+components/contextual_search/browser/contextual_search_js_api_handler.h
+components/cryptauth/connection_finder.h
+components/cryptauth/connection_observer.h
+components/data_reduction_proxy/core/browser/data_use_group.h
+components/data_reduction_proxy/core/browser/data_use_group_provider.h
+components/data_use_measurement/core/url_request_classifier.h
+components/device_event_log/device_event_log_export.h
+components/dom_distiller/core/font_family_list.h
+components/dom_distiller/core/theme_list.h
+components/login/login_export.h
+components/nacl/browser/nacl_browser_delegate.h
+components/nacl/renderer/ppb_nacl_private.h
+components/omnibox/browser/autocomplete_i18n.h
+components/omnibox/browser/autocomplete_provider_client.h
+components/omnibox/browser/autocomplete_provider_listener.h
+components/password_manager/core/browser/keychain_migration_status_mac.h
+components/policy/core/browser/configuration_policy_handler_parameters.h
+components/policy/proto/policy_proto_export.h
+components/rlz/rlz_tracker_delegate.h
+components/session_manager/session_manager_types.h
+components/sessions/core/sessions_export.h
+components/sync/engine/connection_status.h
+components/sync/engine/net/network_time_update_callback.h
+components/translate/core/browser/translate_infobar_delegate.h
+components/user_manager/user.h
+components/user_manager/user_image/user_image.h
+components/user_manager/user_manager.h
+components/viz/display_compositor/display_provider.h
+components/viz/viz_export.h
+components/wallpaper/wallpaper_export.h
+components/wifi/wifi_export.h
+components/wifi/wifi_service.h
+content/browser/background_fetch/background_fetch_constants.h
+content/browser/service_worker/service_worker_response_type.h
+content/common/gpu_stream_constants.h
+content/common/mac/attributed_string_coder.h
+content/common/mac/font_descriptor.h
+content/public/browser/context_factory.h
+content/public/browser/media_observer.h
+content/renderer/external_popup_menu.h
+content/shell/android/shell_descriptors.h
+device/media_transfer_protocol/media_transfer_protocol_manager.h
+extensions/browser/api/clipboard/clipboard_api.h
+extensions/browser/api/networking_config/networking_config_service_factory.h
+extensions/browser/api/webcam_private/webcam.h
+extensions/browser/api/webcam_private/webcam_private_api.h
+extensions/browser/entry_info.h
+extensions/browser/extension_event_histogram_value.h
+extensions/browser/extension_function_histogram_value.h
+google_apis/gcm/base/encryptor.h
+google_apis/gcm/base/gcm_export.h
+gpu/GLES2/gl2chromium.h
+gpu/GLES2/gl2chromium_autogen.h
+gpu/GLES2/gl2extchromium.h
+gpu/command_buffer/client/context_support.h
+gpu/command_buffer/client/gles2_implementation_unittest_autogen.h
+gpu/command_buffer/client/gles2_interface_autogen.h
+gpu/command_buffer/client/gles2_interface_stub_autogen.h
+gpu/command_buffer/client/gles2_interface_stub_impl_autogen.h
+gpu/command_buffer/client/gpu_control_client.h
+gpu/command_buffer/client/ref_counted.h
+gpu/command_buffer/client/shared_memory_limits.h
+gpu/command_buffer/common/command_buffer_shared.h
+gpu/command_buffer/common/gles2_cmd_utils_autogen.h
+gpu/command_buffer/common/gles2_cmd_utils_implementation_autogen.h
+gpu/command_buffer/common/gpu_memory_allocation.h
+gpu/command_buffer/service/gl_stream_texture_image.h
+gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions_autogen.h
+gpu/command_buffer/service/memory_tracking.h
+gpu/command_buffer/service/progress_reporter.h
+gpu/gles2_conform_support/gtf/gtf_stubs.h
+gpu/gpu_export.h
+headless/lib/headless_macros.h
+headless/public/headless_tab_socket.h
+ipc/ipc_channel_proxy_unittest_messages.h
+ipc/ipc_message_null_macros.h
+ipc/param_traits_size_macros.h
+media/audio/audio_logging.h
+media/audio/sounds/test_data.h
+media/base/routing_token_callback.h
+media/base/video_renderer_sink.h
+media/cast/common/mod_util.h
+media/cast/net/rtcp/rtcp_session.h
+media/filters/ffmpeg_aac_bitstream_converter.h
+media/filters/ffmpeg_h264_to_annex_b_bitstream_converter.h
+media/filters/h264_to_annex_b_bitstream_converter.h
+media/formats/mp4/avc.h
+media/formats/mp4/bitstream_converter.h
+media/formats/mp4/fourccs.h
+media/formats/mp4/rcheck.h
+media/formats/mpeg/adts_stream_parser.h
+media/formats/mpeg/mpeg1_audio_stream_parser.h
+media/formats/mpeg/mpeg_audio_stream_parser_base.h
+media/gpu/media_gpu_export.h
+mojo/common/mojo_common_export.h
+mojo/edk/system/broker_messages.h
+mojo/edk/system/system_impl_export.h
+mojo/public/cpp/bindings/strong_associated_binding_set.h
+mojo/public/cpp/bindings/tests/mojo_test_blink_export.h
+mojo/public/cpp/test_support/test_support.h
+net/base/winsock_init.h
+net/cert/cert_type.h
+net/cert/cert_verify_proc_android.h
+net/cert/scoped_nss_types.h
+net/dns/notify_watcher_mac.h
+net/http/http_status_code_list.h
+net/http/transport_security_state_static.h
+net/quic/core/stream_notifier_interface.h
+ppapi/cpp/pass_ref.h
+ppapi/lib/gl/include/GLES2/gl2.h
+ppapi/lib/gl/include/GLES2/gl2ext.h
+ppapi/lib/gl/include/GLES2/gl2platform.h
+ppapi/lib/gl/include/KHR/khrplatform.h
+ppapi/nacl_irt/irt_manifest.h
+ppapi/nacl_irt/public/irt_ppapi.h
+ppapi/native_client/src/shared/ppapi_proxy/ppruntime.h
+ppapi/native_client/src/untrusted/pnacl_irt_shim/irt_shim_ppapi.h
+ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.h
+ppapi/native_client/src/untrusted/pnacl_irt_shim/shim_ppapi.h
+ppapi/proxy/content_decryptor_private_serializer.h
+ppapi/proxy/dispatch_reply_message.h
+ppapi/proxy/plugin_proxy_delegate.h
+ppapi/proxy/plugin_resource_callback.h
+ppapi/proxy/ppapi_proxy_export.h
+ppapi/proxy/resource_message_filter.h
+ppapi/proxy/video_decoder_constants.h
+ppapi/shared_impl/api_id.h
+ppapi/shared_impl/dir_contents.h
+ppapi/shared_impl/ppapi_shared_export.h
+ppapi/shared_impl/singleton_resource_id.h
+remoting/base/chromoting_event_log_writer.h
+remoting/base/logging.h
+remoting/client/display/gl_renderer_delegate.h
+remoting/client/display/gl_texture_ids.h
+remoting/codec/webrtc_video_encoder.h
+remoting/host/linux/x11_keyboard.h
+remoting/host/worker_process_ipc_delegate.h
+remoting/protocol/audio_source.h
+remoting/protocol/audio_stream.h
+remoting/protocol/cursor_shape_stub.h
+remoting/protocol/message_channel_factory.h
+remoting/protocol/test_event_matchers.h
+remoting/protocol/video_feedback_stub.h
+remoting/protocol/video_stream.h
+sandbox/linux/system_headers/capability.h
+sdch/linux/config.h
+services/service_manager/public/c/main.h
+services/ui/ws/ids.h
+skia/ext/convolver_mips_dspr2.h
+skia/ext/skia_commit_hash.h
+skia/ext/texture_handle.h
+testing/gmock_mutant.h
+third_party/WebKit/Source/bindings/modules/v8/serialization/WebCryptoSubTags.h
+third_party/WebKit/Source/core/animation/CSSInterpolationEnvironment.h
+third_party/WebKit/Source/core/animation/SVGInterpolationEnvironment.h
+third_party/WebKit/Source/core/css/CSSPropertyMetadata.h
+third_party/WebKit/Source/core/css/resolver/StyleBuilder.h
+third_party/WebKit/Source/core/css/threaded/MultiThreadedTestUtil.h
+third_party/WebKit/Source/core/css/zoomAdjustedPixelValue.h
+third_party/WebKit/Source/core/dom/ArrayBufferViewHelpers.h
+third_party/WebKit/Source/core/editing/FindOptions.h
+third_party/WebKit/Source/core/paint/FindPaintOffsetAndVisualRectNeedingUpdate.h
+third_party/WebKit/Source/core/style/ShapeValue.h
+third_party/WebKit/Source/core/style/TransformOrigin.h
+third_party/WebKit/Source/platform/ColorSuggestion.h
+third_party/WebKit/Source/platform/EncryptedMediaRequest.h
+third_party/WebKit/Source/platform/fonts/FontSelector.h
+third_party/WebKit/Source/platform/fonts/Glyph.h
+third_party/WebKit/Source/platform/graphics/cpu/arm/WebGLImageConversionNEON.h
+third_party/WebKit/Source/platform/graphics/cpu/mips/WebGLImageConversionMSA.h
+third_party/WebKit/Source/platform/graphics/paint/PaintImage.h
+third_party/WebKit/Source/platform/scheduler/base/task_queue.h
+third_party/WebKit/Source/platform/scroll/ScrollerSizeMetrics.h
+third_party/WebKit/Source/platform/text/TabSize.h
+third_party/WebKit/Source/platform/text/TextDirection.h
+third_party/WebKit/Source/platform/transforms/TransformOperation.h
+third_party/WebKit/public/platform/WebFeature.h
+third_party/WebKit/public/platform/WebFeaturePolicyFeature.h
+third_party/WebKit/public/platform/WebSourceLocation.h
+third_party/WebKit/public/platform/WebTouchInfo.h
+third_party/WebKit/public/platform/modules/media_capabilities/WebMediaCapabilitiesInfo.h
+third_party/cacheinvalidation/src/google/cacheinvalidation/impl/build_constants.h
+third_party/expat/files/lib/ascii.h
+third_party/expat/files/lib/asciitab.h
+third_party/expat/files/lib/expat_config.h
+third_party/expat/files/lib/expat_external.h
+third_party/expat/files/lib/iasciitab.h
+third_party/expat/files/lib/internal.h
+third_party/expat/files/lib/latin1tab.h
+third_party/expat/files/lib/nametab.h
+third_party/expat/files/lib/utf8tab.h
+third_party/expat/files/lib/xmlrole.h
+third_party/expat/files/lib/xmltok.h
+third_party/expat/files/lib/xmltok_impl.h
+third_party/harfbuzz-ng/src/hb-ot-cbdt-table.hh
+third_party/harfbuzz-ng/src/hb-ot-cmap-table.hh
+third_party/harfbuzz-ng/src/hb-ot-glyf-table.hh
+third_party/harfbuzz-ng/src/hb-ot-layout-jstf-table.hh
+third_party/harfbuzz-ng/src/hb-ot-os2-table.hh
+third_party/hunspell/src/hunspell/hunvisapi.h
+third_party/khronos/EGL/egl.h
+third_party/khronos/EGL/eglext.h
+third_party/khronos/EGL/eglplatform.h
+third_party/khronos/GLES2/gl2.h
+third_party/khronos/GLES2/gl2ext.h
+third_party/khronos/GLES2/gl2platform.h
+third_party/khronos/GLES3/gl3.h
+third_party/khronos/GLES3/gl3platform.h
+third_party/khronos/KHR/khrplatform.h
+third_party/leveldatabase/chromium_logger.h
+third_party/libaddressinput/chromium/addressinput_util.h
+third_party/libaddressinput/chromium/override/basictypes_override.h
+third_party/libphonenumber/phonenumber_api.h
+third_party/libudev/libudev0.h
+third_party/libudev/libudev1.h
+third_party/libvpx/source/config/linux/x64/vp8_rtcd.h
+third_party/libvpx/source/config/linux/x64/vp9_rtcd.h
+third_party/libvpx/source/config/linux/x64/vpx_config.h
+third_party/libvpx/source/config/linux/x64/vpx_dsp_rtcd.h
+third_party/libvpx/source/config/linux/x64/vpx_scale_rtcd.h
+third_party/libvpx/source/config/nacl/vp8_rtcd.h
+third_party/libvpx/source/config/nacl/vp9_rtcd.h
+third_party/libvpx/source/config/nacl/vpx_config.h
+third_party/libvpx/source/config/nacl/vpx_dsp_rtcd.h
+third_party/libvpx/source/config/nacl/vpx_scale_rtcd.h
+third_party/libvpx/source/config/vpx_version.h
+third_party/libwebp/mux/animi.h
+third_party/libwebp/mux/muxi.h
+third_party/libwebp/webp/mux.h
+third_party/libxslt/src/libxslt/xsltwin32config.h
+third_party/opus/src/src/opus_private.h
+third_party/opus/src/tests/test_opus_common.h
+third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_names.h
+third_party/protobuf/src/google/protobuf/compiler/javanano/javanano_params.h
+third_party/qcms/src/halffloat.h
+third_party/qcms/src/tests/qcms_test_util.h
+third_party/qcms/src/tests/timing.h
+third_party/snappy/linux/config.h
+third_party/speech-dispatcher/libspeechd.h
+third_party/sqlite/sqlite3.h
+third_party/tcmalloc/chromium/src/addressmap-inl.h
+third_party/tcmalloc/chromium/src/base/basictypes.h
+third_party/tcmalloc/chromium/src/base/dynamic_annotations.h
+third_party/tcmalloc/chromium/src/base/googleinit.h
+third_party/tcmalloc/chromium/src/base/linux_syscall_support.h
+third_party/tcmalloc/chromium/src/base/spinlock_linux-inl.h
+third_party/tcmalloc/chromium/src/base/stl_allocator.h
+third_party/tcmalloc/chromium/src/base/thread_annotations.h
+third_party/tcmalloc/chromium/src/base/thread_lister.h
+third_party/tcmalloc/chromium/src/gperftools/malloc_extension_c.h
+third_party/tcmalloc/chromium/src/gperftools/malloc_hook_c.h
+third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h
+third_party/tcmalloc/chromium/src/heap-profile-stats.h
+third_party/tcmalloc/chromium/src/libc_override.h
+third_party/tcmalloc/chromium/src/malloc_hook_mmap_linux.h
+third_party/tcmalloc/chromium/src/packed-cache-inl.h
+third_party/tcmalloc/chromium/src/page_heap_allocator.h
+third_party/tcmalloc/chromium/src/pagemap.h
+third_party/tcmalloc/chromium/src/stacktrace_config.h
+third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h
+third_party/tcmalloc/chromium/src/system-alloc.h
+third_party/tcmalloc/chromium/src/tcmalloc_guard.h
+third_party/wayland/include/config.h
+third_party/wayland/include/src/wayland-version.h
+third_party/woff2/src/port.h
+third_party/yasm/source/config/linux/config.h
+third_party/yasm/source/config/linux/libyasm-stdint.h
+third_party/zlib/contrib/minizip/crypt.h
+tools/battor_agent/battor_protocol_types.h
+tools/gn/ordered_set.h
+tools/ipc_fuzzer/message_lib/all_message_null_macros.h
+ui/accessibility/ax_export.h
+ui/app_list/app_list_export.h
+ui/app_list/app_list_item.h
+ui/app_list/app_list_switches.h
+ui/aura/aura_export.h
+ui/base/clipboard/clipboard_test_template.h
+ui/base/dragdrop/download_file_interface.h
+ui/compositor/compositor_constants.h
+ui/compositor/layer_owner_delegate.h
+ui/events/keycodes/keyboard_codes_posix.h
+ui/gfx/overlay_transform.h
+ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h
+ui/gfx/swap_result.h
+ui/gfx/sys_color_change_listener.h
+ui/gl/GL/glextchromium.h
+ui/gl/gl_bindings_api_autogen_egl.h
+ui/gl/gl_bindings_api_autogen_gl.h
+ui/gl/gl_bindings_api_autogen_glx.h
+ui/gl/gl_bindings_api_autogen_osmesa.h
+ui/gl/gpu_preference.h
+ui/gl/gpu_switching_observer.h
+ui/native_theme/native_theme_export.h
+ui/ozone/ozone_base_export.h
+ui/ozone/public/ozone_switches.h
+ui/shell_dialogs/shell_dialogs_export.h
diff --git a/chrome/VERSION b/chrome/VERSION
index 7324bce..65d25b80 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=61
 MINOR=0
-BUILD=3121
+BUILD=3122
 PATCH=0
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 613e668..a0714804 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -413,6 +413,8 @@
     "favicon/favicon_utils.h",
     "favicon/large_icon_service_factory.cc",
     "favicon/large_icon_service_factory.h",
+    "feature_engagement_tracker/feature_engagement_tracker_factory.cc",
+    "feature_engagement_tracker/feature_engagement_tracker_factory.h",
     "file_select_helper.cc",
     "file_select_helper.h",
     "file_select_helper_mac.mm",
@@ -1526,6 +1528,7 @@
     "//components/favicon/content",
     "//components/favicon/core",
     "//components/favicon_base",
+    "//components/feature_engagement_tracker",
     "//components/flags_ui",
     "//components/gcm_driver",
     "//components/google/core/browser",
@@ -3134,8 +3137,6 @@
       "download/download_request_infobar_delegate_android.h",
       "engagement/site_engagement_service_android.cc",
       "engagement/site_engagement_service_android.h",
-      "feature_engagement_tracker/feature_engagement_tracker_factory.cc",
-      "feature_engagement_tracker/feature_engagement_tracker_factory.h",
       "geolocation/geolocation_infobar_delegate_android.cc",
       "geolocation/geolocation_infobar_delegate_android.h",
       "history/android/android_history_provider_service.cc",
@@ -3259,7 +3260,6 @@
       "//chrome/browser/android/webapk:proto",
       "//components/cdm/browser",
       "//components/data_usage/android",
-      "//components/feature_engagement_tracker",
       "//components/payments/content/android",
       "//components/precache/content",
       "//components/resources:components_resources",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 99636416..d36739be 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -46,6 +46,8 @@
 #include "components/dom_distiller/core/dom_distiller_switches.h"
 #include "components/error_page/common/error_page_switches.h"
 #include "components/favicon/core/features.h"
+#include "components/feature_engagement_tracker/public/feature_constants.h"
+#include "components/feature_engagement_tracker/public/feature_list.h"
 #include "components/flags_ui/feature_entry.h"
 #include "components/flags_ui/feature_entry_macros.h"
 #include "components/flags_ui/flags_storage.h"
@@ -107,8 +109,6 @@
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/chrome_feature_list.h"
-#include "components/feature_engagement_tracker/public/feature_constants.h"
-#include "components/feature_engagement_tracker/public/feature_list.h"
 #else  // OS_ANDROID
 #include "ui/message_center/message_center_switches.h"
 #endif  // OS_ANDROID
@@ -1394,6 +1394,9 @@
          proximity_auth::switches::kEnableBluetoothLowEnergyDiscovery)},
 #endif  // OS_CHROMEOS
 #if defined(USE_ASH)
+    {"ash-enable-night-light", flag_descriptions::kEnableNightLightName,
+     flag_descriptions::kEnableNightLightDescription, kOsAll,
+     SINGLE_VALUE_TYPE(ash::switches::kAshEnableNightLight)},
     {"show-touch-hud", flag_descriptions::kShowTouchHudName,
      flag_descriptions::kShowTouchHudDescription, kOsAll,
      SINGLE_VALUE_TYPE(ash::switches::kAshTouchHud)},
@@ -1791,14 +1794,12 @@
      flag_descriptions::kChromeHomeSwipeLogicDescription, kOsAndroid,
      MULTI_VALUE_TYPE(kChromeHomeSwipeLogicChoices)},
 #endif  // OS_ANDROID
-#if defined(OS_ANDROID)
     {"iph-demo-mode-choice", flag_descriptions::kIphDemoModeChoiceName,
-     flag_descriptions::kIphDemoModeChoiceDescription, kOsAndroid,
+     flag_descriptions::kIphDemoModeChoiceDescription, kOsAll,
      FEATURE_WITH_PARAMS_VALUE_TYPE(
          feature_engagement_tracker::kIPHDemoMode,
          feature_engagement_tracker::kIPHDemoModeChoiceVariations,
          feature_engagement_tracker::kIPHDemoMode.name)},
-#endif  // OS_ANDROID
     {"num-raster-threads", flag_descriptions::kNumRasterThreadsName,
      flag_descriptions::kNumRasterThreadsDescription, kOsAll,
      MULTI_VALUE_TYPE(kNumRasterThreadsChoices)},
@@ -1921,12 +1922,6 @@
      SINGLE_DISABLE_VALUE_TYPE(
          chromeos::switches::kDisableCaptivePortalBypassProxy)},
 #endif  // OS_CHROMEOS
-#if defined(OS_ANDROID)
-    {"enable-seccomp-sandbox-android",
-     flag_descriptions::kSeccompFilterSandboxAndroidName,
-     flag_descriptions::kSeccompFilterSandboxAndroidDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kSeccompSandboxAndroid)},
-#endif  // OS_ANDROID
 #if defined(OS_CHROMEOS)
     {"enable-wifi-credential-sync", flag_descriptions::kWifiCredentialSyncName,
      flag_descriptions::kWifiCredentialSyncDescription, kOsCrOS,
diff --git a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
index b1fb522..932ec7c 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.cc
@@ -13,9 +13,9 @@
 #include "components/ukm/public/ukm_recorder.h"
 #include "jni/ContextualSearchRankerLoggerImpl_jni.h"
 
-ContextualSearchRankerLoggerImpl::ContextualSearchRankerLoggerImpl(
-    JNIEnv* env,
-    jobject obj) {
+ContextualSearchRankerLoggerImpl::ContextualSearchRankerLoggerImpl(JNIEnv* env,
+                                                                   jobject obj)
+    : ukm_recorder_(nullptr), builder_(nullptr) {
   java_object_.Reset(env, obj);
 }
 
@@ -37,6 +37,11 @@
 void ContextualSearchRankerLoggerImpl::SetUkmRecorder(
     ukm::UkmRecorder* ukm_recorder,
     const GURL& page_url) {
+  if (!ukm_recorder) {
+    builder_.reset();
+    return;
+  }
+
   ukm_recorder_ = ukm_recorder;
   source_id_ = ukm_recorder_->GetNewSourceID();
   ukm_recorder_->UpdateSourceURL(source_id_, page_url);
@@ -48,12 +53,18 @@
     jobject obj,
     const base::android::JavaParamRef<jstring>& j_feature,
     jlong j_long) {
+  if (!builder_)
+    return;
+
   std::string feature = base::android::ConvertJavaStringToUTF8(env, j_feature);
   builder_->AddMetric(feature.c_str(), j_long);
 }
 
 void ContextualSearchRankerLoggerImpl::WriteLogAndReset(JNIEnv* env,
                                                         jobject obj) {
+  if (!ukm_recorder_)
+    return;
+
   // Set up another builder for the next record (in case it's needed).
   builder_ = ukm_recorder_->GetEntryBuilder(source_id_, "ContextualSearch");
 }
diff --git a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
index 52490590..351b26c5 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
+++ b/chrome/browser/android/contextualsearch/contextual_search_ranker_logger_impl.h
@@ -48,13 +48,14 @@
   // TODO(donnd): write a test, using this to inject a test-ukm-recorder.
   void SetUkmRecorder(ukm::UkmRecorder* ukm_recorder, const GURL& page_url);
 
-  // Used to log URL-keyed metrics. This pointer will outlive |this|.
+  // Used to log URL-keyed metrics. This pointer will outlive |this|, and may
+  // be nullptr.
   ukm::UkmRecorder* ukm_recorder_;
 
   // The UKM source ID being used for this session.
   int32_t source_id_;
 
-  // The entry builder for the current record.
+  // The entry builder for the current record, or nullptr if not yet configured.
   std::unique_ptr<ukm::UkmEntryBuilder> builder_;
 
   // The linked Java object.
diff --git a/chrome/browser/chromeos/status/network_menu.cc b/chrome/browser/chromeos/status/network_menu.cc
index 0673559..530ba8af 100644
--- a/chrome/browser/chromeos/status/network_menu.cc
+++ b/chrome/browser/chromeos/status/network_menu.cc
@@ -471,7 +471,7 @@
     }
   } else {
     int initializing_message_id =
-        ash::network_icon::GetCellularUninitializedMsg();
+        ash::network_icon::GetMobileUninitializedMsg();
     if (initializing_message_id) {
       // Initializing cellular modem...
       AddMessageItem(l10n_util::GetStringUTF16(initializing_message_id));
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
index 5fd289d..15b8f360 100644
--- a/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
+++ b/chrome/browser/extensions/api/extension_action/browser_action_interactive_test.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/test/test_timeouts.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/browser_action_test_util.h"
 #include "chrome/browser/extensions/extension_action.h"
@@ -37,6 +38,52 @@
 namespace extensions {
 namespace {
 
+// Helper to ensure all extension hosts are destroyed during the test. If a host
+// is still alive, the Profile can not be destroyed in
+// BrowserProcessImpl::StartTearDown(). TODO(tapted): The existence of this
+// helper is probably a bug. Extension hosts do not currently block shutdown the
+// way a browser tab does. Maybe they should. See http://crbug.com/729476.
+class ExtensionHostWatcher : public content::NotificationObserver {
+ public:
+  ExtensionHostWatcher() {
+    registrar_.Add(this, NOTIFICATION_EXTENSION_HOST_CREATED,
+                   content::NotificationService::AllSources());
+    registrar_.Add(this, NOTIFICATION_EXTENSION_HOST_DESTROYED,
+                   content::NotificationService::AllSources());
+  }
+
+  void Wait() {
+    if (created_ == destroyed_)
+      return;
+
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, quit_closure_, TestTimeouts::action_timeout());
+    run_loop.Run();
+  }
+
+  int created() const { return created_; }
+  int destroyed() const { return destroyed_; }
+
+  // NotificationObserver:
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override {
+    ++(type == NOTIFICATION_EXTENSION_HOST_CREATED ? created_ : destroyed_);
+    if (!quit_closure_.is_null() && created_ == destroyed_)
+      quit_closure_.Run();
+  }
+
+ private:
+  content::NotificationRegistrar registrar_;
+  base::Closure quit_closure_;
+  int created_ = 0;
+  int destroyed_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionHostWatcher);
+};
+
 // chrome.browserAction API tests that interact with the UI in such a way that
 // they cannot be run concurrently (i.e. openPopup API tests that require the
 // window be focused/active).
@@ -47,10 +94,21 @@
 
   // BrowserTestBase:
   void SetUpOnMainThread() override {
+    host_watcher_ = base::MakeUnique<ExtensionHostWatcher>();
     ExtensionApiTest::SetUpOnMainThread();
     EXPECT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
   }
 
+  void TearDownOnMainThread() override {
+    // Note browser windows are closed in PostRunTestOnMainThread(), which is
+    // called after this. But relying on the window close to close the
+    // extension host can cause flakes. See http://crbug.com/729476.
+    // Waiting here requires individual tests to ensure their popup has closed.
+    ExtensionApiTest::TearDownOnMainThread();
+    host_watcher_->Wait();
+    EXPECT_EQ(host_watcher_->created(), host_watcher_->destroyed());
+  }
+
  protected:
   // Function to control whether to run popup tests for the current platform.
   // These tests require RunExtensionSubtest to work as expected and the browser
@@ -95,6 +153,9 @@
     EnsurePopupActive();
   }
 
+  // Close the popup window directly.
+  void ClosePopup() { BrowserActionTestUtil(browser()).HidePopup(); }
+
   // Trigger a focus loss to close the popup.
   void ClosePopupViaFocusLoss() {
     EXPECT_TRUE(BrowserActionTestUtil(browser()).HasPopup());
@@ -123,6 +184,11 @@
     observer.Wait();
     base::RunLoop().RunUntilIdle();
   }
+
+ private:
+  std::unique_ptr<ExtensionHostWatcher> host_watcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowserActionInteractiveTest);
 };
 
 // Tests opening a popup using the chrome.browserAction.openPopup API. This test
@@ -160,8 +226,6 @@
 
   EXPECT_TRUE(new_browser != NULL);
 
-// Flaky on non-aura linux http://crbug.com/309749
-#if !(defined(OS_LINUX) && !defined(USE_AURA))
   ResultCatcher catcher;
   {
     content::WindowedNotificationObserver frame_observer(
@@ -173,7 +237,7 @@
     EXPECT_TRUE(BrowserActionTestUtil(new_browser).HasPopup());
   }
   ASSERT_TRUE(catcher.GetNextResult()) << message_;
-#endif
+  BrowserActionTestUtil(new_browser).HidePopup();
 }
 
 // Tests opening a popup in an incognito window.
@@ -196,8 +260,9 @@
   EXPECT_FALSE(BrowserActionTestUtil(browser()).HasPopup());
 #endif
   // Incognito window should have a popup.
-  EXPECT_TRUE(BrowserActionTestUtil(BrowserList::GetInstance()->GetLastActive())
-                  .HasPopup());
+  BrowserActionTestUtil test_util(BrowserList::GetInstance()->GetLastActive());
+  EXPECT_TRUE(test_util.HasPopup());
+  test_util.HidePopup();
 }
 
 // Tests that an extension can open a popup in the last active incognito window
@@ -227,7 +292,9 @@
       OpenURLOffTheRecord(profile(), GURL("chrome://newtab/"));
   listener.WaitUntilSatisfied();
   EXPECT_EQ(std::string("opened"), listener.message());
-  EXPECT_TRUE(BrowserActionTestUtil(incognito_browser).HasPopup());
+  BrowserActionTestUtil test_util(incognito_browser);
+  EXPECT_TRUE(test_util.HasPopup());
+  test_util.HidePopup();
 }
 
 #if defined(OS_LINUX)
@@ -259,6 +326,7 @@
   // Return control to javascript to validate that opening a popup fails now.
   listener.Reply("show another");
   ASSERT_TRUE(catcher.GetNextResult()) << message_;
+  ClosePopup();
 }
 
 // Test that openPopup does not grant tab permissions like for browser action
@@ -278,6 +346,7 @@
               SessionTabHelper::IdForTab(
                   browser()->tab_strip_model()->GetActiveWebContents()),
               APIPermission::kTab));
+  ClosePopup();
 }
 
 // Test that the extension popup is closed when the browser window is focused.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 381a31ee..054f5e6b 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -353,6 +353,11 @@
     "Experiment to have all APIs reflect the layout viewport. This will "
     "make window.scroll properties relative to the layout viewport.";
 
+const char kIphDemoModeChoiceName[] = "In-Product Help Demo Mode";
+
+const char kIphDemoModeChoiceDescription[] =
+    "Selects the In-Product Help demo mode.";
+
 const char kColorCorrectRenderingName[] = "Color correct rendering";
 
 const char kColorCorrectRenderingDescription[] =
@@ -1067,6 +1072,11 @@
     "flag. The trace may include personally identifiable information (PII) "
     "such as the titles and URLs of websites you visit.";
 
+const char kEnableNightLightName[] = "Enable Night Light";
+const char kEnableNightLightDescription[] =
+    "Enable the Night Light feature to control the color temperature of the "
+    "screen.";
+
 const char kEnablePictureInPictureName[] = "Enable picture in picture.";
 
 const char kEnablePictureInPictureDescription[] =
@@ -1980,17 +1990,6 @@
 
 #endif  // defined(OS_ANDROID)
 
-//  In-Product Help flags
-
-#if defined(OS_ANDROID)
-
-const char kIphDemoModeChoiceName[] = "In-Product Help Demo Mode";
-
-const char kIphDemoModeChoiceDescription[] =
-    "Selects the In-Product Help demo mode.";
-
-#endif  // defined(OS_ANDROID)
-
 //  Settings window flags
 
 const char kSettingsWindowName[] = "Show settings in a window";
@@ -1999,21 +1998,6 @@
     "Settings will be shown in a dedicated window instead of as a browser "
     "tab.";
 
-//  Mixed content issue workaround flags
-
-#if defined(OS_ANDROID)
-
-//  Flag strings for seccomp-bpf sandbox flag.
-
-const char kSeccompFilterSandboxAndroidName[] = "Seccomp-bpf renderer sandbox";
-
-const char kSeccompFilterSandboxAndroidDescription[] =
-    "Renderers will have a second-layer sandbox provided by seccomp-bpf. "
-    "This requires kernel features only available on select Android "
-    "versions.";
-
-#endif  // defined(OS_ANDROID)
-
 //  Extension Content Verification
 
 const char kExtensionContentVerificationName[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 2f609a6..4bc25493 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -235,6 +235,9 @@
 extern const char kEnableNavigationTracingName[];
 extern const char kEnableNavigationTracingDescription[];
 
+extern const char kEnableNightLightName[];
+extern const char kEnableNightLightDescription[];
+
 extern const char kEnablePictureInPictureName[];
 extern const char kEnablePictureInPictureDescription[];
 
@@ -403,6 +406,9 @@
 extern const char kInertVisualViewportName[];
 extern const char kInertVisualViewportDescription[];
 
+extern const char kIphDemoModeChoiceName[];
+extern const char kIphDemoModeChoiceDescription[];
+
 extern const char kJavascriptHarmonyName[];
 extern const char kJavascriptHarmonyDescription[];
 
@@ -982,9 +988,6 @@
 extern const char kHerbPrototypeChoicesDescription[];
 extern const char kHerbPrototypeFlavorElderberry[];
 
-extern const char kIphDemoModeChoiceName[];
-extern const char kIphDemoModeChoiceDescription[];
-
 extern const char kLsdPermissionPromptName[];
 extern const char kLsdPermissionPromptDescription[];
 
@@ -1075,9 +1078,6 @@
 extern const char kReaderModeHeuristicsAlwaysOff[];
 extern const char kReaderModeHeuristicsAlwaysOn[];
 
-extern const char kSeccompFilterSandboxAndroidName[];
-extern const char kSeccompFilterSandboxAndroidDescription[];
-
 extern const char kServiceWorkerPaymentAppsName[];
 extern const char kServiceWorkerPaymentAppsDescription[];
 
diff --git a/chrome/browser/resources/settings/device_page/display.html b/chrome/browser/resources/settings/device_page/display.html
index b4d7fbc..9e0676c 100644
--- a/chrome/browser/resources/settings/device_page/display.html
+++ b/chrome/browser/resources/settings/device_page/display.html
@@ -202,32 +202,34 @@
     </div>
 
     <!-- Night Light Settings -->
-    <div class="settings-box">
-      <div class="start">
-        <h2 id="nightLightLabel">$i18n{displayNightLightLabel}</h2>
-        <div class="secondary">$i18n{displayNightLightText}</div>
-      </div>
-      <settings-toggle-button
-          id="nightLightToggleButton"
-          pref="{{prefs.ash.night_light.enabled}}"
-          aria-labelledby="nightLightLabel">
-      </settings-toggle-button>
-    </div>
-    <div id="nightLightSettingsDiv"
-        class="settings-box continuation start layout vertical">
-      <!-- Color temperature slider -->
-      <div class="settings-box embedded continuation">
-        <div class="start textarea" id="colorTemperatureLabel">
-          $i18n{displayNightLightTemperatureLabel}
+    <template is="dom-if" if="[[nightLightFeatureEnabled_]]" restamp>
+      <div class="settings-box">
+        <div class="start">
+          <h2 id="nightLightLabel">$i18n{displayNightLightLabel}</h2>
+          <div class="secondary">$i18n{displayNightLightText}</div>
         </div>
-        <settings-slider id="colorTemperatureSlider"
-          aria-labelledby="colorTemperatureLabel" min="0" max="100" scale="100"
-          label-min="$i18n{displayNightLightTempSliderMinLabel}"
-          label-max="$i18n{displayNightLightTempSliderMaxLabel}"
-          pref="{{prefs.ash.night_light.color_temperature}}">
-        </settings-slider>
+        <settings-toggle-button
+            id="nightLightToggleButton"
+            pref="{{prefs.ash.night_light.enabled}}"
+            aria-labelledby="nightLightLabel">
+        </settings-toggle-button>
       </div>
-    </div>
+      <div id="nightLightSettingsDiv"
+          class="settings-box continuation start layout vertical">
+        <!-- Color temperature slider -->
+        <div class="settings-box embedded continuation">
+          <div class="start textarea" id="colorTemperatureLabel">
+            $i18n{displayNightLightTemperatureLabel}
+          </div>
+          <settings-slider id="colorTemperatureSlider"
+              aria-labelledby="colorTemperatureLabel" min="0" max="100"
+              scale="100" label-min="$i18n{displayNightLightTempSliderMinLabel}"
+              label-max="$i18n{displayNightLightTempSliderMaxLabel}"
+              pref="{{prefs.ash.night_light.color_temperature}}">
+          </settings-slider>
+        </div>
+      </div>
+    </template>
 
   </template>
   <script src="display.js"></script>
diff --git a/chrome/browser/resources/settings/device_page/display.js b/chrome/browser/resources/settings/device_page/display.js
index 53ef67e..d7c2bd7 100644
--- a/chrome/browser/resources/settings/device_page/display.js
+++ b/chrome/browser/resources/settings/device_page/display.js
@@ -81,6 +81,14 @@
     },
 
     /** @private */
+    nightLightFeatureEnabled_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('nightLightFeatureEnabled');
+      }
+    },
+
+    /** @private */
     unifiedDesktopMode_: {
       type: Boolean,
       value: false,
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
index c265435..be1fa7f 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_browsertest.cc
@@ -388,15 +388,9 @@
       ReportExpectation::Successful({{"report1", RetryStatus::RETRIED}}));
 }
 
-// Failing on some Win and Mac buildbots.  See crbug.com/719138.
-#if defined(OS_WIN) || defined(OS_MACOSX)
-#define MAYBE_DontSendOldReports DISABLED_DontSendOldReports
-#else
-#define MAYBE_DontSendOldReports DontSendOldReports
-#endif
 // CertificateReportingService should ignore reports older than the report TTL.
 IN_PROC_BROWSER_TEST_F(CertificateReportingServiceBrowserTest,
-                       MAYBE_DontSendOldReports) {
+                       DontSendOldReports) {
   SetExpectedHistogramCountOnTeardown(5);
 
   base::SimpleTestClock* clock = new base::SimpleTestClock();
@@ -456,6 +450,15 @@
   // Send pending reports. report2 should be discarded since it's too old. No
   // other reports remain. If any report is sent, test teardown will catch it.
   SendPendingReports();
+
+  // Let all reports succeed and send a single report. This is to make sure
+  // that any (incorrectly) pending reports are dropped before the test tear
+  // down.
+  test_helper()->SetFailureMode(certificate_reporting_test_utils::
+                                    ReportSendingResult::REPORTS_SUCCESSFUL);
+  SendReport("report3");
+  test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({{"report3", RetryStatus::NOT_RETRIED}}));
 }
 
 // CertificateReportingService should drop old reports from its pending report
diff --git a/chrome/browser/subresource_filter/subresource_filter_unittest.cc b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
index 60cc7e4..a98db80 100644
--- a/chrome/browser/subresource_filter/subresource_filter_unittest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
@@ -30,6 +30,7 @@
 #include "components/subresource_filter/content/browser/content_ruleset_service.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
+#include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
 #include "components/subresource_filter/core/browser/ruleset_service.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features_test_support.h"
@@ -44,6 +45,7 @@
 namespace {
 using subresource_filter::testing::ScopedSubresourceFilterConfigurator;
 using subresource_filter::testing::ScopedSubresourceFilterFeatureToggle;
+const char kDisallowedUrl[] = "https://example.test/disallowed.html";
 }  // namespace
 
 // End to end unit test harness of (most of) the browser process portions of the
@@ -148,8 +150,7 @@
       content::RenderFrameHost* parent) {
     auto* subframe =
         content::RenderFrameHostTester::For(parent)->AppendChild("subframe");
-    return SimulateNavigateAndCommit(
-        GURL("https://example.test/disallowed.html"), subframe);
+    return SimulateNavigateAndCommit(GURL(kDisallowedUrl), subframe);
   }
 
   void ConfigureAsSubresourceFilterOnlyURL(const GURL& url) {
@@ -296,3 +297,21 @@
   EXPECT_EQ(subresource_filter::ActivationDecision::UNSUPPORTED_SCHEME,
             driver_factory->GetActivationDecisionForLastCommittedPageLoad());
 }
+
+TEST_F(SubresourceFilterTest, SimpleDisallowedLoad_WithObserver) {
+  GURL url("https://example.test");
+  ConfigureAsSubresourceFilterOnlyURL(url);
+
+  subresource_filter::TestSubresourceFilterObserver observer(web_contents());
+  SimulateNavigateAndCommit(url, main_rfh());
+
+  EXPECT_EQ(subresource_filter::ActivationDecision::ACTIVATED,
+            observer.GetPageActivation(url).value());
+
+  EXPECT_FALSE(CreateAndNavigateDisallowedSubframe(main_rfh()));
+  auto optional_load_policy =
+      observer.GetSubframeLoadPolicy(GURL(kDisallowedUrl));
+  EXPECT_TRUE(optional_load_policy.has_value());
+  EXPECT_EQ(subresource_filter::LoadPolicy::DISALLOW,
+            observer.GetSubframeLoadPolicy(GURL(kDisallowedUrl)).value());
+}
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 21c8ad2..2730606 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -35,6 +35,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "ash/system/devicetype_utils.h"
+#include "ash/system/night_light/night_light_controller.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
@@ -699,6 +700,9 @@
       base::CommandLine::ForCurrentProcess()->HasSwitch(
           chromeos::switches::kEnableTouchCalibrationSetting));
 
+  html_source->AddBoolean("nightLightFeatureEnabled",
+                          ash::NightLightController::IsFeatureEnabled());
+
   LocalizedString storage_strings[] = {
       {"storageTitle", IDS_SETTINGS_STORAGE_TITLE},
       {"storageItemInUse", IDS_SETTINGS_STORAGE_ITEM_IN_USE},
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index ac414e1..859e32f 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -1968,20 +1968,20 @@
 
   def testDeviceName(self):
     driver = self.CreateDriver(
-        mobile_emulation = {'deviceName': 'Google Nexus 5'})
+        mobile_emulation = {'deviceName': 'Nexus 5'})
     driver.Load(self._http_server.GetUrl() + '/userAgentUseDeviceWidth')
     self.assertEqual(360, driver.ExecuteScript('return window.screen.width'))
     self.assertEqual(640, driver.ExecuteScript('return window.screen.height'))
     body_tag = driver.FindElement('tag name', 'body')
     self.assertEqual(
-        'Mozilla/5.0 (Linux; Android 4.4.4; Nexus 5 Build/KTU84P) '
-        'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.114 Mobile '
+        'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) '
+        'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile '
         'Safari/537.36',
         body_tag.GetText())
 
   def testSendKeysToElement(self):
     driver = self.CreateDriver(
-        mobile_emulation = {'deviceName': 'Google Nexus 5'})
+        mobile_emulation = {'deviceName': 'Nexus 5'})
     text = driver.ExecuteScript(
         'document.body.innerHTML = \'<input type="text">\';'
         'var input = document.getElementsByTagName("input")[0];'
@@ -1996,7 +1996,7 @@
 
   def testClickElement(self):
     driver = self.CreateDriver(
-        mobile_emulation = {'deviceName': 'Google Nexus 5'})
+        mobile_emulation = {'deviceName': 'Nexus 5'})
     driver.Load('about:blank')
     div = driver.ExecuteScript(
         'document.body.innerHTML = "<div>old</div>";'
@@ -2010,7 +2010,7 @@
 
   def testTapElement(self):
     driver = self.CreateDriver(
-        mobile_emulation = {'deviceName': 'Google Nexus 5'})
+        mobile_emulation = {'deviceName': 'Nexus 5'})
     driver.Load('about:blank')
     div = driver.ExecuteScript(
         'document.body.innerHTML = "<div>old</div>";'
@@ -2024,7 +2024,7 @@
 
   def testHasTouchScreen(self):
     driver = self.CreateDriver(
-        mobile_emulation = {'deviceName': 'Google Nexus 5'})
+        mobile_emulation = {'deviceName': 'Nexus 5'})
     self.assertIn('hasTouchScreen', driver.capabilities)
     self.assertTrue(driver.capabilities['hasTouchScreen'])
 
@@ -2073,14 +2073,14 @@
   def testNetworkConnectionEnabled(self):
     # mobileEmulation must be enabled for networkConnection to be enabled
     driver = self.CreateDriver(
-        mobile_emulation={'deviceName': 'Google Nexus 5'},
+        mobile_emulation={'deviceName': 'Nexus 5'},
         network_connection=True)
     self.assertTrue(driver.capabilities['mobileEmulationEnabled'])
     self.assertTrue(driver.capabilities['networkConnectionEnabled'])
 
   def testEmulateNetworkConnection4g(self):
     driver = self.CreateDriver(
-        mobile_emulation={'deviceName': 'Google Nexus 5'},
+        mobile_emulation={'deviceName': 'Nexus 5'},
         network_connection=True)
     # Test 4G connection.
     connection_type = 0x8
@@ -2091,7 +2091,7 @@
 
   def testEmulateNetworkConnectionMultipleBits(self):
     driver = self.CreateDriver(
-        mobile_emulation={'deviceName': 'Google Nexus 5'},
+        mobile_emulation={'deviceName': 'Nexus 5'},
         network_connection=True)
     # Connection with 4G, 3G, and 2G bits on.
     # Tests that 4G takes precedence.
@@ -2103,7 +2103,7 @@
 
   def testWifiAndAirplaneModeEmulation(self):
     driver = self.CreateDriver(
-        mobile_emulation={'deviceName': 'Google Nexus 5'},
+        mobile_emulation={'deviceName': 'Nexus 5'},
         network_connection=True)
     # Connection with both Wifi and Airplane Mode on.
     # Tests that Wifi takes precedence over Airplane Mode.
@@ -2124,7 +2124,7 @@
       '/helloworld', respondWithString)
 
     driver = self.CreateDriver(
-        mobile_emulation={'deviceName': 'Google Nexus 5'},
+        mobile_emulation={'deviceName': 'Nexus 5'},
         network_connection=True)
 
     # Set network to online
@@ -2158,7 +2158,7 @@
 
   def testNetworkConnectionTypeIsAppliedToAllTabs(self):
     driver = self.CreateDriver(
-        mobile_emulation={'deviceName': 'Google Nexus 5'},
+        mobile_emulation={'deviceName': 'Nexus 5'},
         network_connection=True)
     driver.Load(self._http_server.GetUrl() +'/chromedriver/page_test.html')
     window1_handle = driver.GetCurrentWindowHandle()
diff --git a/chromeos/network/network_connect.cc b/chromeos/network/network_connect.cc
index 5778905..c3553e2b6 100644
--- a/chromeos/network/network_connect.cc
+++ b/chromeos/network/network_connect.cc
@@ -435,31 +435,27 @@
                                   network_handler::ErrorCallback());
     return;
   }
-  // If we're dealing with a mobile network, then handle SIM lock here.
-  // SIM locking only applies to cellular, so the code below won't execute
-  // if |technology| has been explicitly set to WiMAX.
-  if (technology.MatchesPattern(NetworkTypePattern::Mobile())) {
+  // If we're dealing with a cellular network, then handle SIM lock here.
+  // SIM locking only applies to cellular.
+  if (technology.MatchesPattern(NetworkTypePattern::Cellular())) {
     const DeviceState* mobile = handler->GetDeviceStateByType(technology);
     if (!mobile) {
       NET_LOG_ERROR("SetTechnologyEnabled with no device", log_string);
       return;
     }
-    // The following only applies to cellular.
-    if (mobile->type() == shill::kTypeCellular) {
-      if (mobile->IsSimAbsent()) {
-        // If this is true, then we have a cellular device with no SIM
-        // inserted. TODO(armansito): Chrome should display a notification here,
-        // prompting the user to insert a SIM card and restart the device to
-        // enable cellular. See crbug.com/125171.
-        NET_LOG_USER("Cannot enable cellular device without SIM.", log_string);
-        return;
-      }
-      if (!mobile->sim_lock_type().empty()) {
-        // A SIM has been inserted, but it is locked. Let the user unlock it
-        // via the dialog.
-        delegate_->ShowMobileSimDialog();
-        return;
-      }
+    if (mobile->IsSimAbsent()) {
+      // If this is true, then we have a cellular device with no SIM
+      // inserted. TODO(armansito): Chrome should display a notification here,
+      // prompting the user to insert a SIM card and restart the device to
+      // enable cellular. See crbug.com/125171.
+      NET_LOG_USER("Cannot enable cellular device without SIM.", log_string);
+      return;
+    }
+    if (!mobile->sim_lock_type().empty()) {
+      // A SIM has been inserted, but it is locked. Let the user unlock it
+      // via the dialog.
+      delegate_->ShowMobileSimDialog();
+      return;
     }
   }
   handler->SetTechnologyEnabled(technology, true,
diff --git a/chromeos/network/network_type_pattern.cc b/chromeos/network/network_type_pattern.cc
index dbda85ba..0cfe2d5 100644
--- a/chromeos/network/network_type_pattern.cc
+++ b/chromeos/network/network_type_pattern.cc
@@ -15,11 +15,9 @@
 namespace {
 
 const char kPatternDefault[] = "PatternDefault";
-const char kPatternEthernet[] = "PatternEthernet";
 const char kPatternWireless[] = "PatternWireless";
 const char kPatternMobile[] = "PatternMobile";
 const char kPatternNonVirtual[] = "PatternNonVirtual";
-const char kPatternTether[] = "PatternTether";
 
 enum NetworkTypeBitFlag {
   kNetworkTypeNone = 0,
@@ -69,7 +67,8 @@
 
 // static
 NetworkTypePattern NetworkTypePattern::Mobile() {
-  return NetworkTypePattern(kNetworkTypeCellular | kNetworkTypeWimax);
+  return NetworkTypePattern(kNetworkTypeCellular | kNetworkTypeWimax |
+                            kNetworkTypeTether);
 }
 
 // static
@@ -133,17 +132,14 @@
 std::string NetworkTypePattern::ToDebugString() const {
   if (Equals(Default()))
     return kPatternDefault;
-  if (Equals(Ethernet()))
-    return kPatternEthernet;
   if (Equals(Wireless()))
     return kPatternWireless;
   if (Equals(Mobile()))
     return kPatternMobile;
   if (Equals(NonVirtual()))
     return kPatternNonVirtual;
-  if (Equals(Tether()))
-    return kPatternTether;
 
+  // Note: shill_type_to_flag includes kTypeTether.
   std::string str;
   for (size_t i = 0; i < arraysize(shill_type_to_flag); ++i) {
     if (!(pattern_ & shill_type_to_flag[i].bit_flag))
diff --git a/chromeos/network/network_type_pattern.h b/chromeos/network/network_type_pattern.h
index 8dd214fc..1fde992b 100644
--- a/chromeos/network/network_type_pattern.h
+++ b/chromeos/network/network_type_pattern.h
@@ -17,10 +17,10 @@
   // Matches any network.
   static NetworkTypePattern Default();
 
-  // Matches wireless (WiFi, cellular, etc.) networks
+  // Matches wireless (WiFi, Cellular, etc.) networks
   static NetworkTypePattern Wireless();
 
-  // Matches cellular or wimax networks.
+  // Matches Cellular, WiMAX, or Tether networks.
   static NetworkTypePattern Mobile();
 
   // Matches non virtual networks.
diff --git a/chromeos/network/network_type_pattern_unittest.cc b/chromeos/network/network_type_pattern_unittest.cc
index 142f403b..53f10ea 100644
--- a/chromeos/network/network_type_pattern_unittest.cc
+++ b/chromeos/network/network_type_pattern_unittest.cc
@@ -22,7 +22,9 @@
         non_virtual_(NetworkTypePattern::NonVirtual()),
         wimax_(NetworkTypePattern::Wimax()),
         wireless_(NetworkTypePattern::Wireless()),
-        tether_(NetworkTypePattern::Tether()) {}
+        tether_(NetworkTypePattern::Tether()),
+        vpn_(NetworkTypePattern::VPN()),
+        wifi_(NetworkTypePattern::WiFi()) {}
 
   bool MatchesPattern(const NetworkTypePattern& a,
                       const NetworkTypePattern& b) {
@@ -40,21 +42,38 @@
   const NetworkTypePattern wimax_;
   const NetworkTypePattern wireless_;
   const NetworkTypePattern tether_;
+  const NetworkTypePattern vpn_;
+  const NetworkTypePattern wifi_;
 };
 
 }  // namespace
 
 TEST_F(NetworkTypePatternTest, MatchesType) {
+  // Mobile contains Cellular, Wimax, and Tether.
   EXPECT_TRUE(mobile_.MatchesType(shill::kTypeCellular));
   EXPECT_TRUE(mobile_.MatchesType(shill::kTypeWimax));
+  EXPECT_TRUE(mobile_.MatchesType(kTypeTether));
   EXPECT_FALSE(mobile_.MatchesType(shill::kTypeWifi));
+  EXPECT_FALSE(mobile_.MatchesType(shill::kTypeEthernet));
+  EXPECT_FALSE(mobile_.MatchesType(shill::kTypeVPN));
 
+  // Wireless contains Wifi, Cellular, and Wimax.
   EXPECT_TRUE(wireless_.MatchesType(shill::kTypeWifi));
   EXPECT_TRUE(wireless_.MatchesType(shill::kTypeCellular));
   EXPECT_TRUE(wireless_.MatchesType(shill::kTypeWimax));
   EXPECT_FALSE(wireless_.MatchesType(shill::kTypeEthernet));
-
+  EXPECT_FALSE(wireless_.MatchesType(shill::kTypeVPN));
   EXPECT_FALSE(wireless_.MatchesType(kTypeTether));
+
+  // Non-virtual contains everything except VPN and Tether.
+  EXPECT_TRUE(non_virtual_.MatchesType(shill::kTypeCellular));
+  EXPECT_TRUE(non_virtual_.MatchesType(shill::kTypeWifi));
+  EXPECT_TRUE(non_virtual_.MatchesType(shill::kTypeEthernet));
+  EXPECT_TRUE(non_virtual_.MatchesType(shill::kTypeWimax));
+  EXPECT_FALSE(non_virtual_.MatchesType(shill::kTypeVPN));
+  EXPECT_FALSE(non_virtual_.MatchesType(kTypeTether));
+
+  EXPECT_TRUE(wimax_.MatchesType(shill::kTypeWimax));
   EXPECT_FALSE(wimax_.MatchesType(kTypeTether));
 }
 
@@ -104,8 +123,15 @@
 
 TEST_F(NetworkTypePatternTest, ToDebugString) {
   EXPECT_EQ(default_.ToDebugString(), "PatternDefault");
+  EXPECT_EQ(wireless_.ToDebugString(), "PatternWireless");
   EXPECT_EQ(mobile_.ToDebugString(), "PatternMobile");
-  EXPECT_EQ(cellular_.ToDebugString(), "cellular");
+  EXPECT_EQ(non_virtual_.ToDebugString(), "PatternNonVirtual");
+  EXPECT_EQ(ethernet_.ToDebugString(), shill::kTypeEthernet);
+  EXPECT_EQ(cellular_.ToDebugString(), shill::kTypeCellular);
+  EXPECT_EQ(wimax_.ToDebugString(), shill::kTypeWimax);
+  EXPECT_EQ(tether_.ToDebugString(), kTypeTether);
+  EXPECT_EQ(wifi_.ToDebugString(), shill::kTypeWifi);
+  EXPECT_EQ(vpn_.ToDebugString(), shill::kTypeVPN);
 }
 
 }  // namespace chromeos
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 482b3b8..71bc853 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -85,6 +85,7 @@
     "//components/download:unit_tests",
     "//components/favicon/core:unit_tests",
     "//components/favicon_base:unit_tests",
+    "//components/feature_engagement_tracker:unit_tests",
     "//components/flags_ui:unit_tests",
     "//components/gcm_driver:unit_tests",
     "//components/gcm_driver/crypto:unit_tests",
@@ -243,7 +244,6 @@
   if (is_android) {
     deps += [
       "//components/cdm/browser:unit_tests",
-      "//components/feature_engagement_tracker:unit_tests",
       "//components/gcm_driver/instance_id:test_support",
       "//components/gcm_driver/instance_id/android:instance_id_driver_java",
       "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
diff --git a/components/browser_watcher/BUILD.gn b/components/browser_watcher/BUILD.gn
index 7e459f07..124571da 100644
--- a/components/browser_watcher/BUILD.gn
+++ b/components/browser_watcher/BUILD.gn
@@ -126,6 +126,7 @@
       "postmortem_minidump_writer_win_unittest.cc",
       "postmortem_report_collector_unittest.cc",
       "stability_debugging_win_unittest.cc",
+      "stability_paths_unittest.cc",
       "system_session_analyzer_win_unittest.cc",
       "watcher_client_win_unittest.cc",
       "watcher_metrics_provider_win_unittest.cc",
diff --git a/components/browser_watcher/features.cc b/components/browser_watcher/features.cc
index 399e49b..76ecd82 100644
--- a/components/browser_watcher/features.cc
+++ b/components/browser_watcher/features.cc
@@ -11,4 +11,6 @@
 
 const char kInitFlushParam[] = "init_flush";
 
+const char kCollectPostmortemParam[] = "collect_postmortem";
+
 }  // namespace browser_watcher
diff --git a/components/browser_watcher/features.h b/components/browser_watcher/features.h
index 8f3503a6c..5f067f6 100644
--- a/components/browser_watcher/features.h
+++ b/components/browser_watcher/features.h
@@ -17,6 +17,10 @@
 // flush.
 extern const char kInitFlushParam[];
 
+// Name of an experiment parameter that controls whether to collect postmortem
+// reports.
+extern const char kCollectPostmortemParam[];
+
 }  // namespace browser_watcher
 
 #endif  // COMPONENTS_BROWSER_WATCHER_FEATURES_H_
diff --git a/components/browser_watcher/postmortem_report_collector.cc b/components/browser_watcher/postmortem_report_collector.cc
index ef21d84..80ce8c75 100644
--- a/components/browser_watcher/postmortem_report_collector.cc
+++ b/components/browser_watcher/postmortem_report_collector.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
@@ -60,102 +59,58 @@
 
 }  // namespace
 
+void PostmortemDeleter::Process(
+    const std::vector<base::FilePath>& stability_files) {
+  for (const FilePath& file : stability_files) {
+    if (base::DeleteFile(file, false))
+      LogCollectionStatus(UNCLEAN_SHUTDOWN);
+    else
+      LogCollectionStatus(DEBUG_FILE_DELETION_FAILED);
+  }
+}
+
 PostmortemReportCollector::PostmortemReportCollector(
     const std::string& product_name,
     const std::string& version_number,
     const std::string& channel_name,
+    crashpad::CrashReportDatabase* report_database,
     SystemSessionAnalyzer* analyzer)
     : product_name_(product_name),
       version_number_(version_number),
       channel_name_(channel_name),
-      system_session_analyzer_(analyzer) {}
+      report_database_(report_database),
+      system_session_analyzer_(analyzer) {
+  DCHECK_NE(nullptr, report_database);
+}
 
 PostmortemReportCollector::~PostmortemReportCollector() {}
 
-int PostmortemReportCollector::CollectAndSubmitAllPendingReports(
-    const base::FilePath& debug_info_dir,
-    const base::FilePath::StringType& debug_file_pattern,
-    const std::set<base::FilePath>& excluded_debug_files,
-    crashpad::CrashReportDatabase* report_database) {
-  DCHECK_NE(true, debug_info_dir.empty());
-  DCHECK_NE(true, debug_file_pattern.empty());
-  DCHECK_NE(nullptr, report_database);
-
-  // Collect the list of files to harvest.
-  std::vector<FilePath> debug_files = GetDebugStateFilePaths(
-      debug_info_dir, debug_file_pattern, excluded_debug_files);
-  UMA_HISTOGRAM_COUNTS_100("ActivityTracker.Collect.StabilityFileCount",
-                           debug_files.size());
-
+void PostmortemReportCollector::Process(
+    const std::vector<base::FilePath>& stability_files) {
   // Determine the crashpad client id.
   crashpad::UUID client_id;
-  crashpad::Settings* settings = report_database->GetSettings();
+  crashpad::Settings* settings = report_database_->GetSettings();
   if (settings) {
     // If GetSettings() or GetClientID() fails client_id will be left at its
     // default value, all zeroes, which is appropriate.
     settings->GetClientID(&client_id);
   }
 
-  // Number of unclean shutdowns based on successful collection of stability
-  // reports.
-  int unclean_cnt = 0;
-  // Number of unclean shutdowns that may be attributable to system instability
-  // based on successful collection of stability reports. This number should be
-  // smaller or equal to |unclean_cnt|.
-  int unclean_system_cnt = 0;
-
-  // Process each stability file.
-  for (const FilePath& file : debug_files) {
-    bool system_unclean = false;
-    if (CollectAndSubmitOneReport(client_id, file, report_database,
-                                  &system_unclean)) {
-      ++unclean_cnt;
-      if (system_unclean)
-        ++unclean_system_cnt;
-    }
+  for (const FilePath& file : stability_files) {
+    CollectAndSubmitOneReport(client_id, file);
   }
-
-  UMA_STABILITY_HISTOGRAM_COUNTS_100(
-      "ActivityTracker.Collect.UncleanShutdownCount", unclean_cnt);
-  UMA_STABILITY_HISTOGRAM_COUNTS_100(
-      "ActivityTracker.Collect.UncleanSystemCount", unclean_system_cnt);
-
-  return unclean_cnt;
 }
 
-std::vector<FilePath> PostmortemReportCollector::GetDebugStateFilePaths(
-    const FilePath& debug_info_dir,
-    const FilePath::StringType& debug_file_pattern,
-    const std::set<FilePath>& excluded_debug_files) {
-  DCHECK_NE(true, debug_info_dir.empty());
-  DCHECK_NE(true, debug_file_pattern.empty());
-
-  std::vector<FilePath> paths;
-  base::FileEnumerator enumerator(debug_info_dir, false /* recursive */,
-                                  base::FileEnumerator::FILES,
-                                  debug_file_pattern);
-  FilePath path;
-  for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
-    if (excluded_debug_files.find(path) == excluded_debug_files.end())
-      paths.push_back(path);
-  }
-  return paths;
-}
-
-bool PostmortemReportCollector::CollectAndSubmitOneReport(
+void PostmortemReportCollector::CollectAndSubmitOneReport(
     const crashpad::UUID& client_id,
-    const FilePath& file,
-    crashpad::CrashReportDatabase* report_database,
-    bool* system_unclean) {
-  DCHECK_NE(nullptr, report_database);
-  DCHECK_NE(nullptr, system_unclean);
-  *system_unclean = false;
+    const FilePath& file) {
+  DCHECK_NE(nullptr, report_database_);
+  LogCollectionStatus(COLLECTION_ATTEMPT);
 
   // Note: the code below involves two notions of report: chrome internal state
   // reports and the crashpad reports they get wrapped into.
 
-  // Collect the data from the debug file to a proto. Note: a non-empty report
-  // is interpreted here as an unclean exit.
+  // Collect the data from the debug file to a proto.
   StabilityReport report_proto;
   CollectionStatus status = CollectOneReport(file, &report_proto);
   if (status != SUCCESS) {
@@ -164,41 +119,41 @@
     if (!base::DeleteFile(file, false))
       DLOG(ERROR) << "Failed to delete " << file.value();
     LogCollectionStatus(status);
-    return false;
+    return;
   }
 
+  // Delete the stability file. If the file cannot be deleted, do not report its
+  // contents - it will be retried in a future processing run. Note that this
+  // approach can lead to under reporting and retries. However, under reporting
+  // is preferable to the over reporting that would happen with a file that
+  // cannot be deleted. Also note that the crash registration may still fail at
+  // this point: losing the report in such a case is deemed acceptable.
+  if (!base::DeleteFile(file, false)) {
+    DLOG(ERROR) << "Failed to delete " << file.value();
+    LogCollectionStatus(DEBUG_FILE_DELETION_FAILED);
+    return;
+  }
+
+  LogCollectionStatus(UNCLEAN_SHUTDOWN);
+  if (report_proto.system_state().session_state() == SystemState::UNCLEAN)
+    LogCollectionStatus(UNCLEAN_SESSION);
+
   // Prepare a crashpad report.
   CrashReportDatabase::NewReport* new_report = nullptr;
   CrashReportDatabase::OperationStatus database_status =
-      report_database->PrepareNewCrashReport(&new_report);
+      report_database_->PrepareNewCrashReport(&new_report);
   if (database_status != CrashReportDatabase::kNoError) {
-    // Assume this is recoverable: not deleting the file.
-    DLOG(ERROR) << "PrepareNewCrashReport failed";
     LogCollectionStatus(PREPARE_NEW_CRASH_REPORT_FAILED);
-    return false;
+    return;
   }
   CrashReportDatabase::CallErrorWritingCrashReport
-      call_error_writing_crash_report(report_database, new_report);
+      call_error_writing_crash_report(report_database_, new_report);
 
   // Write the report to a minidump.
   if (!WriteReportToMinidump(&report_proto, client_id, new_report->uuid,
                              reinterpret_cast<FILE*>(new_report->handle))) {
-    // Assume this is not recoverable and delete the file.
-    if (!base::DeleteFile(file, false))
-      DLOG(ERROR) << "Failed to delete " << file.value();
     LogCollectionStatus(WRITE_TO_MINIDUMP_FAILED);
-    return false;
-  }
-
-  // If the file cannot be deleted, do not report its contents. Note this can
-  // lead to under reporting and retries. However, under reporting is
-  // preferable to the over reporting that would happen with a file that
-  // cannot be deleted.
-  // TODO(manzagop): metrics for the number of non-deletable files.
-  if (!base::DeleteFile(file, false)) {
-    DLOG(ERROR) << "Failed to delete " << file.value();
-    LogCollectionStatus(DEBUG_FILE_DELETION_FAILED);
-    return false;
+    return;
   }
 
   // Finalize the report wrt the report database. Note that this doesn't trigger
@@ -206,18 +161,14 @@
   // writing, the delay is on the order of up to 15 minutes).
   call_error_writing_crash_report.Disarm();
   crashpad::UUID unused_report_id;
-  database_status = report_database->FinishedWritingCrashReport(
+  database_status = report_database_->FinishedWritingCrashReport(
       new_report, &unused_report_id);
   if (database_status != CrashReportDatabase::kNoError) {
-    DLOG(ERROR) << "FinishedWritingCrashReport failed";
     LogCollectionStatus(FINISHED_WRITING_CRASH_REPORT_FAILED);
-    return false;
+    return;
   }
 
   LogCollectionStatus(SUCCESS);
-  if (report_proto.system_state().session_state() == SystemState::UNCLEAN)
-    *system_unclean = true;
-  return true;
 }
 
 CollectionStatus PostmortemReportCollector::CollectOneReport(
diff --git a/components/browser_watcher/postmortem_report_collector.h b/components/browser_watcher/postmortem_report_collector.h
index 0187181..436c16d4 100644
--- a/components/browser_watcher/postmortem_report_collector.h
+++ b/components/browser_watcher/postmortem_report_collector.h
@@ -28,6 +28,15 @@
 
 namespace browser_watcher {
 
+// Deletes stability files.
+class PostmortemDeleter {
+ public:
+  PostmortemDeleter() = default;
+  ~PostmortemDeleter() = default;
+
+  void Process(const std::vector<base::FilePath>& stability_files);
+};
+
 // Handles postmortem report collection by establishing the set of stability
 // files to collect, then for each file:
 //   - extracting a report protocol buffer
@@ -39,20 +48,13 @@
   PostmortemReportCollector(const std::string& product_name,
                             const std::string& version_number,
                             const std::string& channel_name,
+                            crashpad::CrashReportDatabase* report_database,
                             SystemSessionAnalyzer* analyzer);
-  virtual ~PostmortemReportCollector();
+  ~PostmortemReportCollector();
 
-  // Collects postmortem stability reports from files found in |debug_info_dir|,
-  // relying on |debug_file_pattern| and |excluded_debug_files|. Reports are
-  // then wrapped in Crashpad reports, manufactured via |report_database|.
-  // Returns the number crash reports successfully registered with the reporter.
-  // TODO(manzagop): consider mechanisms for partial collection if this is to be
-  //     used on a critical path.
-  int CollectAndSubmitAllPendingReports(
-      const base::FilePath& debug_info_dir,
-      const base::FilePath::StringType& debug_file_pattern,
-      const std::set<base::FilePath>& excluded_debug_files,
-      crashpad::CrashReportDatabase* report_database);
+  // Collects postmortem stability reports from |stability_files|. Reports are
+  // then wrapped in Crashpad reports and registered with the crash database.
+  void Process(const std::vector<base::FilePath>& stability_files);
 
   const std::string& product_name() const { return product_name_; }
   const std::string& version_number() const { return version_number_; }
@@ -81,18 +83,10 @@
       PostmortemReportCollectorCollectionFromGlobalTrackerTest,
       SystemStateTest);
 
-  // Virtual for unittesting.
-  virtual std::vector<base::FilePath> GetDebugStateFilePaths(
-      const base::FilePath& debug_info_dir,
-      const base::FilePath::StringType& debug_file_pattern,
-      const std::set<base::FilePath>& excluded_debug_files);
-
   // Collects a stability file, generates a report and registers it with the
-  // database. Returns true on success. False otherwise.
-  bool CollectAndSubmitOneReport(const crashpad::UUID& client_id,
-                                 const base::FilePath& file,
-                                 crashpad::CrashReportDatabase* report_database,
-                                 bool* system_unclean);
+  // database.
+  void CollectAndSubmitOneReport(const crashpad::UUID& client_id,
+                                 const base::FilePath& file);
 
   virtual CollectionStatus CollectOneReport(
       const base::FilePath& stability_file,
@@ -111,6 +105,7 @@
   std::string version_number_;
   std::string channel_name_;
 
+  crashpad::CrashReportDatabase* report_database_;  // Not owned.
   SystemSessionAnalyzer* system_session_analyzer_;  // Not owned.
 
   DISALLOW_COPY_AND_ASSIGN(PostmortemReportCollector);
diff --git a/components/browser_watcher/postmortem_report_collector_unittest.cc b/components/browser_watcher/postmortem_report_collector_unittest.cc
index 791a6cb..bd615e6e 100644
--- a/components/browser_watcher/postmortem_report_collector_unittest.cc
+++ b/components/browser_watcher/postmortem_report_collector_unittest.cc
@@ -40,6 +40,7 @@
 using base::debug::GlobalActivityTracker;
 using base::debug::ThreadActivityTracker;
 using base::File;
+using base::FilePath;
 using base::FilePersistentMemoryAllocator;
 using base::MemoryMappedFile;
 using base::PersistentMemoryAllocator;
@@ -54,6 +55,58 @@
 
 namespace {
 
+TEST(PostmortemDeleterTest, BasicTest) {
+  base::HistogramTester histogram_tester;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  // Create three files.
+  FilePath path_one = temp_dir.GetPath().AppendASCII("a.pma");
+  FilePath path_two = temp_dir.GetPath().AppendASCII("b.pma");
+  FilePath path_three = temp_dir.GetPath().AppendASCII("c.pma");
+
+  std::vector<FilePath> stability_files = {path_one, path_two, path_three};
+
+  for (const FilePath& path : stability_files) {
+    {
+      base::ScopedFILE file(base::OpenFile(path, "w"));
+      ASSERT_NE(file.get(), nullptr);
+    }
+    ASSERT_TRUE(base::PathExists(path));
+  }
+
+  // Open one stability file to prevent its deletion.
+  base::ScopedFILE file(base::OpenFile(path_two, "w"));
+  ASSERT_NE(file.get(), nullptr);
+
+  // Validate deletion and metrics.
+  PostmortemDeleter deleter;
+  deleter.Process(stability_files);
+
+  ASSERT_FALSE(base::PathExists(path_one));
+  ASSERT_FALSE(base::PathExists(path_three));
+  histogram_tester.ExpectBucketCount("ActivityTracker.Collect.Status",
+                                     UNCLEAN_SHUTDOWN, 2);
+
+  ASSERT_TRUE(base::PathExists(path_two));
+  histogram_tester.ExpectBucketCount("ActivityTracker.Collect.Status",
+                                     DEBUG_FILE_DELETION_FAILED, 1);
+
+  std::vector<CollectionStatus> unexpected_statuses = {
+      NONE,
+      SUCCESS,
+      ANALYZER_CREATION_FAILED,
+      DEBUG_FILE_NO_DATA,
+      PREPARE_NEW_CRASH_REPORT_FAILED,
+      WRITE_TO_MINIDUMP_FAILED,
+      FINISHED_WRITING_CRASH_REPORT_FAILED,
+      COLLECTION_ATTEMPT};
+  for (CollectionStatus status : unexpected_statuses) {
+    histogram_tester.ExpectBucketCount("ActivityTracker.Collect.Status", status,
+                                       0);
+  }
+}
+
 const char kProductName[] = "TestProduct";
 const char kVersionNumber[] = "TestVersionNumber";
 const char kChannelName[] = "TestChannel";
@@ -113,23 +166,17 @@
                CrashReportDatabase::OperationStatus(const UUID& uuid));
 };
 
-// Used for testing CollectAndSubmitAllPendingReports.
 class MockPostmortemReportCollector : public PostmortemReportCollector {
  public:
-  MockPostmortemReportCollector()
+  explicit MockPostmortemReportCollector(CrashReportDatabase* crash_database)
       : PostmortemReportCollector(kProductName,
                                   kVersionNumber,
                                   kChannelName,
+                                  crash_database,
                                   nullptr) {}
 
-  MOCK_METHOD3(GetDebugStateFilePaths,
-               std::vector<base::FilePath>(
-                   const base::FilePath& debug_info_dir,
-                   const base::FilePath::StringType& debug_file_pattern,
-                   const std::set<base::FilePath>&));
   MOCK_METHOD2(CollectOneReport,
-               CollectionStatus(const base::FilePath&,
-                                StabilityReport* report));
+               CollectionStatus(const FilePath&, StabilityReport* report));
   MOCK_METHOD4(WriteReportToMinidump,
                bool(StabilityReport* report,
                     const crashpad::UUID& client_id,
@@ -164,10 +211,11 @@
 
 }  // namespace
 
-class PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest
-    : public testing::Test {
+class PostmortemReportCollectorProcessTest : public testing::Test {
  public:
-  void SetUpTest(bool system_session_clean) {
+  void SetUpTest(bool system_session_clean, bool expect_write_dump) {
+    collector_.reset(new MockPostmortemReportCollector(&database_));
+
     // Create a dummy debug file.
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     debug_file_ = temp_dir_.GetPath().AppendASCII("foo-1.pma");
@@ -177,29 +225,23 @@
     }
     ASSERT_TRUE(base::PathExists(debug_file_));
 
-    // Expect collection of the debug file paths.
-    debug_file_pattern_ = FILE_PATH_LITERAL("foo-*.pma");
-    std::vector<base::FilePath> debug_files{debug_file_};
-    EXPECT_CALL(collector_,
-                GetDebugStateFilePaths(debug_file_.DirName(),
-                                       debug_file_pattern_, no_excluded_files_))
-        .Times(1)
-        .WillOnce(Return(debug_files));
-
     EXPECT_CALL(database_, GetSettings()).Times(1).WillOnce(Return(nullptr));
 
     // Expect a single collection call.
     StabilityReport report;
     report.mutable_system_state()->set_session_state(
         system_session_clean ? SystemState::CLEAN : SystemState::UNCLEAN);
-    EXPECT_CALL(collector_, CollectOneReport(debug_file_, _))
+    EXPECT_CALL(*collector_, CollectOneReport(debug_file_, _))
         .Times(1)
         .WillOnce(DoAll(SetArgPointee<1>(report), Return(SUCCESS)));
 
+    if (!expect_write_dump)
+      return;
+
     // Expect the call to write the proto to a minidump. This involves
     // requesting a report from the crashpad database, writing the report, then
     // finalizing it with the database.
-    base::FilePath minidump_path = temp_dir_.GetPath().AppendASCII("foo-1.dmp");
+    FilePath minidump_path = temp_dir_.GetPath().AppendASCII("foo-1.dmp");
     base::File minidump_file(
         minidump_path, base::File::FLAG_CREATE | base::File::File::FLAG_WRITE);
     crashpad::UUID new_report_uuid;
@@ -211,137 +253,79 @@
         .WillOnce(DoAll(SetArgPointee<0>(&crashpad_report_),
                         Return(CrashReportDatabase::kNoError)));
 
-    EXPECT_CALL(collector_,
+    EXPECT_CALL(*collector_,
                 WriteReportToMinidump(_, _, _, minidump_file.GetPlatformFile()))
         .Times(1)
         .WillOnce(Return(true));
   }
   void ValidateHistograms(int unclean_cnt, int unclean_system_cnt) {
-    histogram_tester_.ExpectTotalCount(
-        "ActivityTracker.Collect.StabilityFileCount", 1);
-    histogram_tester_.ExpectBucketCount(
-        "ActivityTracker.Collect.StabilityFileCount", 1, 1);
-    histogram_tester_.ExpectTotalCount(
-        "ActivityTracker.Collect.UncleanShutdownCount", 1);
-    histogram_tester_.ExpectBucketCount(
-        "ActivityTracker.Collect.UncleanShutdownCount", unclean_cnt, 1);
-    histogram_tester_.ExpectTotalCount(
-        "ActivityTracker.Collect.UncleanSystemCount", 1);
-    histogram_tester_.ExpectBucketCount(
-        "ActivityTracker.Collect.UncleanSystemCount", unclean_system_cnt, 1);
+    histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
+                                        UNCLEAN_SHUTDOWN, unclean_cnt);
+    histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
+                                        UNCLEAN_SESSION, unclean_system_cnt);
   }
   void CollectReports(bool is_session_clean) {
-    SetUpTest(is_session_clean);
+    SetUpTest(is_session_clean, true);
 
     EXPECT_CALL(database_, FinishedWritingCrashReport(&crashpad_report_, _))
         .Times(1)
         .WillOnce(Return(CrashReportDatabase::kNoError));
 
     // Run the test.
-    int success_cnt = collector_.CollectAndSubmitAllPendingReports(
-        debug_file_.DirName(), debug_file_pattern_, no_excluded_files_,
-        &database_);
-    ASSERT_EQ(1, success_cnt);
+    std::vector<FilePath> debug_files{debug_file_};
+    collector_->Process(debug_files);
     ASSERT_FALSE(base::PathExists(debug_file_));
   }
 
  protected:
   base::HistogramTester histogram_tester_;
   base::ScopedTempDir temp_dir_;
-  base::FilePath debug_file_;
+  FilePath debug_file_;
   MockCrashReportDatabase database_;
-  MockPostmortemReportCollector collector_;
-  base::FilePath::StringType debug_file_pattern_;
-  std::set<base::FilePath> no_excluded_files_;
+  std::unique_ptr<MockPostmortemReportCollector> collector_;
   CrashReportDatabase::NewReport crashpad_report_;
 };
 
-TEST_F(PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest,
-       CollectAndSubmitAllPendingReportsCleanSession) {
+TEST_F(PostmortemReportCollectorProcessTest, ProcessCleanSession) {
   CollectReports(true);
   int expected_unclean = 1;
   int expected_system_unclean = 0;
   ValidateHistograms(expected_unclean, expected_system_unclean);
 }
 
-TEST_F(PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest,
-       CollectAndSubmitAllPendingReportsUncleanSession) {
+TEST_F(PostmortemReportCollectorProcessTest, ProcessUncleanSession) {
   CollectReports(false);
   int expected_unclean = 1;
   int expected_system_unclean = 1;
   ValidateHistograms(expected_unclean, expected_system_unclean);
 }
 
-TEST_F(PostmortemReportCollectorCollectAndSubmitAllPendingReportsTest,
-       CollectAndSubmitAllPendingReportsStuckFile) {
-  SetUpTest(true);
+TEST_F(PostmortemReportCollectorProcessTest, ProcessStuckFile) {
+  bool system_session_clean = true;
+  bool expect_write_dump = false;
+  SetUpTest(system_session_clean, expect_write_dump);
 
   // Open the stability debug file to prevent its deletion.
   base::ScopedFILE file(base::OpenFile(debug_file_, "w"));
   ASSERT_NE(file.get(), nullptr);
 
-  // Expect Crashpad is notified of an error writing the crash report.
-  EXPECT_CALL(database_, ErrorWritingCrashReport(&crashpad_report_))
-      .Times(1)
-      .WillOnce(Return(CrashReportDatabase::kNoError));
-
   // Run the test.
-  int success_cnt = collector_.CollectAndSubmitAllPendingReports(
-      debug_file_.DirName(), debug_file_pattern_, no_excluded_files_,
-      &database_);
-  ASSERT_EQ(0, success_cnt);
+  std::vector<FilePath> debug_files{debug_file_};
+  collector_->Process(debug_files);
   ASSERT_TRUE(base::PathExists(debug_file_));
 
+  histogram_tester_.ExpectBucketCount("ActivityTracker.Collect.Status",
+                                      DEBUG_FILE_DELETION_FAILED, 1);
   int expected_unclean = 0;
   int expected_system_unclean = 0;
   ValidateHistograms(expected_unclean, expected_system_unclean);
 }
 
-TEST(PostmortemReportCollectorTest, GetDebugStateFilePaths) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
-  // Create files.
-  std::vector<base::FilePath> expected_paths;
-  std::set<base::FilePath> excluded_paths;
-  {
-    // Matches the pattern.
-    base::FilePath path = temp_dir.GetPath().AppendASCII("foo1.pma");
-    base::ScopedFILE file(base::OpenFile(path, "w"));
-    ASSERT_NE(file.get(), nullptr);
-    expected_paths.push_back(path);
-
-    // Matches the pattern, but is excluded.
-    path = temp_dir.GetPath().AppendASCII("foo2.pma");
-    file.reset(base::OpenFile(path, "w"));
-    ASSERT_NE(file.get(), nullptr);
-    ASSERT_TRUE(excluded_paths.insert(path).second);
-
-    // Matches the pattern.
-    path = temp_dir.GetPath().AppendASCII("foo3.pma");
-    file.reset(base::OpenFile(path, "w"));
-    ASSERT_NE(file.get(), nullptr);
-    expected_paths.push_back(path);
-
-    // Does not match the pattern.
-    path = temp_dir.GetPath().AppendASCII("bar.baz");
-    file.reset(base::OpenFile(path, "w"));
-    ASSERT_NE(file.get(), nullptr);
-  }
-
-  PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, nullptr);
-  EXPECT_THAT(
-      collector.GetDebugStateFilePaths(
-          temp_dir.GetPath(), FILE_PATH_LITERAL("foo*.pma"), excluded_paths),
-      testing::UnorderedElementsAreArray(expected_paths));
-}
-
 TEST(PostmortemReportCollectorTest, CollectEmptyFile) {
   // Create an empty file.
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath file_path = temp_dir.GetPath().AppendASCII("empty.pma");
+  FilePath file_path = temp_dir.GetPath().AppendASCII("empty.pma");
   {
     base::ScopedFILE file(base::OpenFile(file_path, "w"));
     ASSERT_NE(file.get(), nullptr);
@@ -349,8 +333,9 @@
   ASSERT_TRUE(PathExists(file_path));
 
   // Validate collection: an empty file cannot suppport an analyzer.
+  MockCrashReportDatabase crash_db;
   PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, nullptr);
+                                      kChannelName, &crash_db, nullptr);
   StabilityReport report;
   ASSERT_EQ(ANALYZER_CREATION_FAILED,
             collector.CollectOneReport(file_path, &report));
@@ -360,8 +345,7 @@
   // Create a file with content we don't expect to be valid for a debug file.
   base::ScopedTempDir temp_dir;
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-  base::FilePath file_path =
-      temp_dir.GetPath().AppendASCII("invalid_content.pma");
+  FilePath file_path = temp_dir.GetPath().AppendASCII("invalid_content.pma");
   {
     base::ScopedFILE file(base::OpenFile(file_path, "w"));
     ASSERT_NE(file.get(), nullptr);
@@ -376,8 +360,9 @@
 
   // Validate collection: random content appears as though there is not
   // stability data.
+  MockCrashReportDatabase crash_db;
   PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, nullptr);
+                                      kChannelName, &crash_db, nullptr);
   StabilityReport report;
   ASSERT_NE(SUCCESS, collector.CollectOneReport(file_path, &report));
 }
@@ -466,11 +451,11 @@
     return WrapUnique(new ThreadActivityTracker(mem_base, tracker_mem_size));
   }
 
-  const base::FilePath& debug_file_path() const { return debug_file_path_; }
+  const FilePath& debug_file_path() const { return debug_file_path_; }
 
  protected:
   base::ScopedTempDir temp_dir_;
-  base::FilePath debug_file_path_;
+  FilePath debug_file_path_;
 
   std::unique_ptr<PersistentMemoryAllocator> allocator_;
   std::unique_ptr<ThreadActivityTracker> tracker_;
@@ -506,8 +491,9 @@
   user_data->SetInt("some_int", 42);
 
   // Validate collection returns the expected report.
+  MockCrashReportDatabase crash_db;
   PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, nullptr);
+                                      kChannelName, &crash_db, nullptr);
   StabilityReport report;
   ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
 
@@ -594,11 +580,11 @@
     debug_file_path_ = temp_dir_.GetPath().AppendASCII("debug.pma");
   }
 
-  const base::FilePath& debug_file_path() { return debug_file_path_; }
+  const FilePath& debug_file_path() { return debug_file_path_; }
 
  protected:
   base::ScopedTempDir temp_dir_;
-  base::FilePath debug_file_path_;
+  FilePath debug_file_path_;
 };
 
 TEST_F(PostmortemReportCollectorCollectionFromGlobalTrackerTest,
@@ -610,8 +596,9 @@
   GlobalActivityTracker::Get()->RecordLogMessage("foo bar");
 
   // Collect the stability report.
+  MockCrashReportDatabase crash_db;
   PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, nullptr);
+                                      kChannelName, &crash_db, nullptr);
   StabilityReport report;
   ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
 
@@ -643,8 +630,9 @@
   process_data.SetStringReference("sref", string2);
 
   // Collect the stability report.
+  MockCrashReportDatabase crash_db;
   PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, nullptr);
+                                      kChannelName, &crash_db, nullptr);
   StabilityReport report;
   ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
 
@@ -712,8 +700,9 @@
   process_data.SetString("FieldTrial.foo", "bar");
 
   // Collect the stability report.
+  MockCrashReportDatabase crash_db;
   PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, nullptr);
+                                      kChannelName, &crash_db, nullptr);
   StabilityReport report;
   ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
 
@@ -753,8 +742,9 @@
   GlobalActivityTracker::Get()->RecordModuleInfo(module_info);
 
   // Collect the stability report.
+  MockCrashReportDatabase crash_db;
   PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, nullptr);
+                                      kChannelName, &crash_db, nullptr);
   StabilityReport report;
   ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
 
@@ -791,8 +781,9 @@
               IsSessionUnclean(base::Time::FromInternalValue(12345LL)))
       .Times(1)
       .WillOnce(Return(SystemSessionAnalyzer::CLEAN));
+  MockCrashReportDatabase crash_db;
   PostmortemReportCollector collector(kProductName, kVersionNumber,
-                                      kChannelName, &analyzer);
+                                      kChannelName, &crash_db, &analyzer);
   StabilityReport report;
   ASSERT_EQ(SUCCESS, collector.CollectOneReport(debug_file_path(), &report));
 
diff --git a/components/browser_watcher/stability_paths.cc b/components/browser_watcher/stability_paths.cc
index 329d89c..a8164df 100644
--- a/components/browser_watcher/stability_paths.cc
+++ b/components/browser_watcher/stability_paths.cc
@@ -15,6 +15,7 @@
 #include "base/debug/activity_tracker.h"
 #include "base/feature_list.h"
 #include "base/files/file.h"
+#include "base/files/file_enumerator.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/metrics/persistent_memory_allocator.h"
 #include "base/strings/stringprintf.h"
@@ -28,6 +29,7 @@
 
 namespace browser_watcher {
 
+using base::FilePath;
 using base::FilePersistentMemoryAllocator;
 using base::MemoryMappedFile;
 using base::PersistentMemoryAllocator;
@@ -57,14 +59,14 @@
 
 }  // namespace
 
-base::FilePath GetStabilityDir(const base::FilePath& user_data_dir) {
+FilePath GetStabilityDir(const FilePath& user_data_dir) {
   return user_data_dir.AppendASCII("Stability");
 }
 
-base::FilePath GetStabilityFileForProcess(base::ProcessId pid,
-                                          timeval creation_time,
-                                          const base::FilePath& user_data_dir) {
-  base::FilePath stability_dir = GetStabilityDir(user_data_dir);
+FilePath GetStabilityFileForProcess(base::ProcessId pid,
+                                    timeval creation_time,
+                                    const FilePath& user_data_dir) {
+  FilePath stability_dir = GetStabilityDir(user_data_dir);
 
   constexpr uint64_t kMicrosecondsPerSecond = static_cast<uint64_t>(1E6);
   int64_t creation_time_us =
@@ -76,8 +78,8 @@
 }
 
 bool GetStabilityFileForProcess(const base::Process& process,
-                                const base::FilePath& user_data_dir,
-                                base::FilePath* file_path) {
+                                const FilePath& user_data_dir,
+                                FilePath* file_path) {
   DCHECK(file_path);
 
   FILETIME creation_time;
@@ -92,11 +94,30 @@
   return true;
 }
 
-base::FilePath::StringType GetStabilityFilePattern() {
-  return base::FilePath::StringType(FILE_PATH_LITERAL("*-*")) +
+FilePath::StringType GetStabilityFilePattern() {
+  return FilePath::StringType(FILE_PATH_LITERAL("*-*")) +
          base::PersistentMemoryAllocator::kFileExtension;
 }
 
+std::vector<FilePath> GetStabilityFiles(
+    const FilePath& stability_dir,
+    const FilePath::StringType& stability_file_pattern,
+    const std::set<FilePath>& excluded_stability_files) {
+  DCHECK_NE(true, stability_dir.empty());
+  DCHECK_NE(true, stability_file_pattern.empty());
+
+  std::vector<FilePath> paths;
+  base::FileEnumerator enumerator(stability_dir, false /* recursive */,
+                                  base::FileEnumerator::FILES,
+                                  stability_file_pattern);
+  FilePath path;
+  for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) {
+    if (excluded_stability_files.find(path) == excluded_stability_files.end())
+      paths.push_back(path);
+  }
+  return paths;
+}
+
 void MarkOwnStabilityFileDeleted(const base::FilePath& user_data_dir) {
   base::debug::GlobalActivityTracker* global_tracker =
       base::debug::GlobalActivityTracker::Get();
diff --git a/components/browser_watcher/stability_paths.h b/components/browser_watcher/stability_paths.h
index 7941c37..0b2078f 100644
--- a/components/browser_watcher/stability_paths.h
+++ b/components/browser_watcher/stability_paths.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_BROWSER_WATCHER_STABILITY_PATHS_H_
 #define COMPONENTS_BROWSER_WATCHER_STABILITY_PATHS_H_
 
+#include <set>
+#include <vector>
+
 #include "base/files/file_path.h"
 #include "base/process/process.h"
 #include "build/build_config.h"
@@ -35,6 +38,13 @@
 // Returns a pattern that matches file names returned by GetFileForProcess.
 base::FilePath::StringType GetStabilityFilePattern();
 
+// Returns files in |stability_dir| that match |stability_file_pattern|,
+// excluding those in |excluded_stability_files|.
+std::vector<base::FilePath> GetStabilityFiles(
+    const base::FilePath& stability_dir,
+    const base::FilePath::StringType& stability_file_pattern,
+    const std::set<base::FilePath>& excluded_stability_files);
+
 // Sets the current process's stability file's state to deleted (via the
 // GlobalActivityTracker) and opens the file for deletion. Metrics pertaining to
 // stability recording are logged.
diff --git a/components/browser_watcher/stability_paths_unittest.cc b/components/browser_watcher/stability_paths_unittest.cc
new file mode 100644
index 0000000..4b9eea0
--- /dev/null
+++ b/components/browser_watcher/stability_paths_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/browser_watcher/stability_paths.h"
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_watcher {
+
+TEST(StabilityPathsTest, GetStabilityFiles) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  // Create files.
+  std::vector<base::FilePath> expected_paths;
+  std::set<base::FilePath> excluded_paths;
+  {
+    // Matches the pattern.
+    base::FilePath path = temp_dir.GetPath().AppendASCII("foo1.pma");
+    base::ScopedFILE file(base::OpenFile(path, "w"));
+    ASSERT_NE(file.get(), nullptr);
+    expected_paths.push_back(path);
+
+    // Matches the pattern, but is excluded.
+    path = temp_dir.GetPath().AppendASCII("foo2.pma");
+    file.reset(base::OpenFile(path, "w"));
+    ASSERT_NE(file.get(), nullptr);
+    ASSERT_TRUE(excluded_paths.insert(path).second);
+
+    // Matches the pattern.
+    path = temp_dir.GetPath().AppendASCII("foo3.pma");
+    file.reset(base::OpenFile(path, "w"));
+    ASSERT_NE(file.get(), nullptr);
+    expected_paths.push_back(path);
+
+    // Does not match the pattern.
+    path = temp_dir.GetPath().AppendASCII("bar.baz");
+    file.reset(base::OpenFile(path, "w"));
+    ASSERT_NE(file.get(), nullptr);
+  }
+
+  EXPECT_THAT(GetStabilityFiles(temp_dir.GetPath(),
+                                FILE_PATH_LITERAL("foo*.pma"), excluded_paths),
+              testing::UnorderedElementsAreArray(expected_paths));
+}
+
+}  // namespace browser_watcher
diff --git a/components/browser_watcher/stability_report_extractor.h b/components/browser_watcher/stability_report_extractor.h
index 5ff9033..8049146 100644
--- a/components/browser_watcher/stability_report_extractor.h
+++ b/components/browser_watcher/stability_report_extractor.h
@@ -12,7 +12,7 @@
 
 namespace browser_watcher {
 
-// DO NOT CHANGE VALUES. This is logged persistently in a histogram.
+// DO NOT REMOVE OR REORDER VALUES. This is logged persistently in a histogram.
 enum CollectionStatus {
   NONE = 0,
   SUCCESS = 1,  // Successfully registered a report with Crashpad.
@@ -22,7 +22,11 @@
   WRITE_TO_MINIDUMP_FAILED = 5,
   DEBUG_FILE_DELETION_FAILED = 6,
   FINISHED_WRITING_CRASH_REPORT_FAILED = 7,
-  COLLECTION_STATUS_MAX = 8
+  UNCLEAN_SHUTDOWN = 8,
+  UNCLEAN_SESSION = 9,
+  COLLECTION_ATTEMPT = 10,
+  // New values go here.
+  COLLECTION_STATUS_MAX = 11
 };
 
 // Extracts a stability report from a stability file.
diff --git a/components/browser_watcher/watcher_metrics_provider_win.cc b/components/browser_watcher/watcher_metrics_provider_win.cc
index 6376079ba..dcd7881 100644
--- a/components/browser_watcher/watcher_metrics_provider_win.cc
+++ b/components/browser_watcher/watcher_metrics_provider_win.cc
@@ -14,6 +14,7 @@
 
 #include "base/bind.h"
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_base.h"
 #include "base/metrics/histogram_macros.h"
@@ -223,36 +224,49 @@
       done_callback);
 }
 
+// TODO(manzagop): consider mechanisms for partial collection if this is to be
+//     used on a critical path.
 void WatcherMetricsProviderWin::CollectPostmortemReportsOnBlockingPool() {
-  // Note: the feature controls both instrumentation and collection.
+  SCOPED_UMA_HISTOGRAM_TIMER("ActivityTracker.Collect.TotalTime");
+
   bool is_stability_debugging_on =
       base::FeatureList::IsEnabled(browser_watcher::kStabilityDebuggingFeature);
   if (!is_stability_debugging_on) {
-    // TODO(manzagop): delete possible leftover data.
-    return;
+    return;  // TODO(manzagop): scan for possible data to delete?
   }
 
-  SCOPED_UMA_HISTOGRAM_TIMER("ActivityTracker.Collect.TotalTime");
-
   if (user_data_dir_.empty() || crash_dir_.empty()) {
-    LOG(ERROR) << "User data directory or crash directory is unknown.";
     LogCollectionInitStatus(UNKNOWN_DIR);
     return;
   }
 
-  // Determine the stability directory and the stability file for the current
-  // process.
+  // Determine which files to harvest.
   base::FilePath stability_dir = GetStabilityDir(user_data_dir_);
+
   base::FilePath current_stability_file;
   if (!GetStabilityFileForProcess(base::Process::Current(), user_data_dir_,
                                   &current_stability_file)) {
-    LOG(ERROR) << "Failed to get the current stability file.";
     LogCollectionInitStatus(GET_STABILITY_FILE_PATH_FAILED);
     return;
   }
-  const std::set<base::FilePath>& excluded_debug_files = {
+  const std::set<base::FilePath>& excluded_stability_files = {
       current_stability_file};
 
+  std::vector<base::FilePath> stability_files = GetStabilityFiles(
+      stability_dir, GetStabilityFilePattern(), excluded_stability_files);
+  UMA_HISTOGRAM_COUNTS_100("ActivityTracker.Collect.StabilityFileCount",
+                           stability_files.size());
+
+  // If postmortem collection is disabled, delete the files.
+  const bool should_collect = base::GetFieldTrialParamByFeatureAsBool(
+      browser_watcher::kStabilityDebuggingFeature,
+      browser_watcher::kCollectPostmortemParam, false);
+  if (!should_collect) {
+    PostmortemDeleter deleter;
+    deleter.Process(stability_files);
+    return;
+  }
+
   // Create a database. Note: Chrome already has a g_database in crashpad.cc but
   // it has internal linkage. Create a new one.
   std::unique_ptr<crashpad::CrashReportDatabase> crashpad_database =
@@ -273,10 +287,8 @@
   SystemSessionAnalyzer analyzer(kSystemSessionsToInspect);
   PostmortemReportCollector collector(
       base::UTF16ToUTF8(product_name), base::UTF16ToUTF8(version_number),
-      base::UTF16ToUTF8(channel_name), &analyzer);
-  collector.CollectAndSubmitAllPendingReports(
-      stability_dir, GetStabilityFilePattern(), excluded_debug_files,
-      crashpad_database.get());
+      base::UTF16ToUTF8(channel_name), crashpad_database.get(), &analyzer);
+  collector.Process(stability_files);
 }
 
 }  // namespace browser_watcher
diff --git a/components/download/internal/BUILD.gn b/components/download/internal/BUILD.gn
index ae5c2ae..f5f2ca7 100644
--- a/components/download/internal/BUILD.gn
+++ b/components/download/internal/BUILD.gn
@@ -41,8 +41,13 @@
     "proto_conversions.h",
     "scheduler/battery_listener.cc",
     "scheduler/battery_listener.h",
+    "scheduler/device_status.cc",
+    "scheduler/device_status.h",
     "scheduler/network_listener.cc",
     "scheduler/network_listener.h",
+    "scheduler/scheduler.h",
+    "scheduler/scheduler_impl.cc",
+    "scheduler/scheduler_impl.h",
     "startup_status.cc",
     "startup_status.h",
     "stats.cc",
@@ -73,6 +78,7 @@
     "proto_conversions_unittest.cc",
     "scheduler/battery_listener_unittest.cc",
     "scheduler/network_listener_unittest.cc",
+    "scheduler/scheduler_impl_unittest.cc",
   ]
 
   deps = [
diff --git a/components/download/internal/entry.cc b/components/download/internal/entry.cc
index f68d5bf2..6cf6855e 100644
--- a/components/download/internal/entry.cc
+++ b/components/download/internal/entry.cc
@@ -12,6 +12,7 @@
 Entry::Entry(const DownloadParams& params)
     : client(params.client),
       guid(params.guid),
+      create_time(base::Time::Now()),
       scheduling_params(params.scheduling_params),
       request_params(params.request_params) {}
 
diff --git a/components/download/internal/entry.h b/components/download/internal/entry.h
index 7e971c07..e4e787e 100644
--- a/components/download/internal/entry.h
+++ b/components/download/internal/entry.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_DOWNLOAD_INTERNAL_ENTRY_H_
 #define COMPONENTS_DOWNLOAD_INTERNAL_ENTRY_H_
 
+#include "base/time/time.h"
 #include "components/download/public/client.h"
 #include "components/download/public/clients.h"
 #include "components/download/public/download_params.h"
@@ -52,6 +53,9 @@
   // A unique GUID that represents this download.  See | base::GenerateGUID()|.
   std::string guid;
 
+  // The time when the entry is created.
+  base::Time create_time;
+
   // The parameters that determine under what device conditions this download
   // will occur.
   SchedulingParams scheduling_params;
diff --git a/components/download/internal/entry_utils.cc b/components/download/internal/entry_utils.cc
index c5b38aa..b02ec79 100644
--- a/components/download/internal/entry_utils.cc
+++ b/components/download/internal/entry_utils.cc
@@ -35,5 +35,22 @@
   return categorized;
 }
 
+Criteria GetSchedulingCriteria(const Model::EntryList& entries) {
+  Criteria criteria;
+  for (auto* const entry : entries) {
+    DCHECK(entry);
+    const SchedulingParams& scheduling_params = entry->scheduling_params;
+    if (scheduling_params.battery_requirements ==
+        SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE) {
+      criteria.requires_battery_charging = false;
+    }
+    if (scheduling_params.network_requirements ==
+        SchedulingParams::NetworkRequirements::NONE) {
+      criteria.requires_unmetered_network = false;
+    }
+  }
+  return criteria;
+}
+
 }  // namespace util
 }  // namespace download
diff --git a/components/download/internal/entry_utils.h b/components/download/internal/entry_utils.h
index 1d7ac884..7e46cdba 100644
--- a/components/download/internal/entry_utils.h
+++ b/components/download/internal/entry_utils.h
@@ -10,6 +10,8 @@
 #include <string>
 #include <vector>
 
+#include "components/download/internal/model.h"
+#include "components/download/internal/scheduler/device_status.h"
 #include "components/download/public/clients.h"
 
 namespace download {
@@ -31,6 +33,10 @@
     const std::set<DownloadClient>& clients,
     const std::vector<Entry*>& entries);
 
+// Get the least strict scheduling criteria from |entries|, the criteria is used
+// to schedule platform background tasks.
+Criteria GetSchedulingCriteria(const Model::EntryList& entries);
+
 }  // namespace util
 }  // namespace download
 
diff --git a/components/download/internal/proto/entry.proto b/components/download/internal/proto/entry.proto
index d5f9dfed..8241f9fa 100644
--- a/components/download/internal/proto/entry.proto
+++ b/components/download/internal/proto/entry.proto
@@ -16,8 +16,10 @@
 enum DownloadClient {
   INVALID = 0;
   TEST = 1;
-  OFFLINE_PAGE_PREFETCH = 2;
-  BOUNDARY = 3;
+  TEST_2 = 2;
+  TEST_3 = 3;
+  OFFLINE_PAGE_PREFETCH = 4;
+  BOUNDARY = 5;
 }
 
 // Stores the request params, internal state, metrics and metadata associated
@@ -42,6 +44,9 @@
   optional SchedulingParams scheduling_params = 3;
   optional RequestParams request_params = 4;
 
-  // Internal Tracking State.
+  // Internal Tracking States.
   optional State state = 5;
+
+  // Creation time of the entry, measured in milliseconds.
+  optional int64 create_time = 6;
 }
diff --git a/components/download/internal/proto_conversions.cc b/components/download/internal/proto_conversions.cc
index dc4187a..abe26287 100644
--- a/components/download/internal/proto_conversions.cc
+++ b/components/download/internal/proto_conversions.cc
@@ -59,6 +59,10 @@
       return protodb::DownloadClient::INVALID;
     case DownloadClient::TEST:
       return protodb::DownloadClient::TEST;
+    case DownloadClient::TEST_2:
+      return protodb::DownloadClient::TEST_2;
+    case DownloadClient::TEST_3:
+      return protodb::DownloadClient::TEST_3;
     case DownloadClient::OFFLINE_PAGE_PREFETCH:
       return protodb::DownloadClient::OFFLINE_PAGE_PREFETCH;
     case DownloadClient::BOUNDARY:
@@ -76,6 +80,10 @@
       return DownloadClient::INVALID;
     case protodb::DownloadClient::TEST:
       return DownloadClient::TEST;
+    case protodb::DownloadClient::TEST_2:
+      return DownloadClient::TEST_2;
+    case protodb::DownloadClient::TEST_3:
+      return DownloadClient::TEST_3;
     case protodb::DownloadClient::OFFLINE_PAGE_PREFETCH:
       return DownloadClient::OFFLINE_PAGE_PREFETCH;
     case protodb::DownloadClient::BOUNDARY:
@@ -244,7 +252,7 @@
       SchedulingParamsFromProto(proto.scheduling_params());
   entry.request_params = RequestParamsFromProto(proto.request_params());
   entry.state = RequestStateFromProto(proto.state());
-
+  entry.create_time = base::Time::FromInternalValue(proto.create_time());
   return entry;
 }
 
@@ -257,6 +265,7 @@
                           proto.mutable_scheduling_params());
   RequestParamsToProto(entry.request_params, proto.mutable_request_params());
   proto.set_state(RequestStateToProto(entry.state));
+  proto.set_create_time(entry.create_time.ToInternalValue());
 
   return proto;
 }
diff --git a/components/download/internal/scheduler/battery_listener.cc b/components/download/internal/scheduler/battery_listener.cc
index b015ca1..45a50b4e 100644
--- a/components/download/internal/scheduler/battery_listener.cc
+++ b/components/download/internal/scheduler/battery_listener.cc
@@ -9,11 +9,9 @@
 namespace download {
 
 // Helper function that converts the battery status to battery requirement.
-SchedulingParams::BatteryRequirements ToBatteryRequirement(
-    bool on_battery_power) {
-  return on_battery_power
-             ? SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE
-             : SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+BatteryStatus ToBatteryStatus(bool on_battery_power) {
+  return on_battery_power ? BatteryStatus::NOT_CHARGING
+                          : BatteryStatus::CHARGING;
 }
 
 BatteryListener::BatteryListener() = default;
@@ -22,9 +20,8 @@
   Stop();
 }
 
-SchedulingParams::BatteryRequirements BatteryListener::CurrentBatteryStatus()
-    const {
-  return ToBatteryRequirement(base::PowerMonitor::Get()->IsOnBatteryPower());
+BatteryStatus BatteryListener::CurrentBatteryStatus() const {
+  return ToBatteryStatus(base::PowerMonitor::Get()->IsOnBatteryPower());
 }
 
 void BatteryListener::Start() {
@@ -46,13 +43,12 @@
 }
 
 void BatteryListener::OnPowerStateChange(bool on_battery_power) {
-  NotifyBatteryChange(ToBatteryRequirement(on_battery_power));
+  NotifyBatteryChange(ToBatteryStatus(on_battery_power));
 }
 
-void BatteryListener::NotifyBatteryChange(
-    SchedulingParams::BatteryRequirements current_battery) {
+void BatteryListener::NotifyBatteryChange(BatteryStatus battery_status) {
   for (auto& observer : observers_)
-    observer.OnBatteryChange(current_battery);
+    observer.OnBatteryChange(battery_status);
 }
 
 }  // namespace download
diff --git a/components/download/internal/scheduler/battery_listener.h b/components/download/internal/scheduler/battery_listener.h
index 2ddc6109..02515dc 100644
--- a/components/download/internal/scheduler/battery_listener.h
+++ b/components/download/internal/scheduler/battery_listener.h
@@ -9,6 +9,7 @@
 
 #include "base/observer_list.h"
 #include "base/power_monitor/power_observer.h"
+#include "components/download/internal/scheduler/device_status.h"
 #include "components/download/public/download_params.h"
 
 namespace download {
@@ -21,15 +22,14 @@
   class Observer {
    public:
     // Called when certain battery requirements are meet.
-    virtual void OnBatteryChange(
-        SchedulingParams::BatteryRequirements battery_status) = 0;
+    virtual void OnBatteryChange(BatteryStatus battery_status) = 0;
   };
 
   BatteryListener();
   ~BatteryListener() override;
 
   // Retrieves the current minimum battery requirement.
-  SchedulingParams::BatteryRequirements CurrentBatteryStatus() const;
+  BatteryStatus CurrentBatteryStatus() const;
 
   // Start to listen to battery change.
   void Start();
@@ -46,7 +46,7 @@
   void OnPowerStateChange(bool on_battery_power) override;
 
   // Notifies |observers_| about battery requirement change.
-  void NotifyBatteryChange(SchedulingParams::BatteryRequirements);
+  void NotifyBatteryChange(BatteryStatus status);
 
   // Observers to monitor battery change in download service.
   base::ObserverList<Observer> observers_;
diff --git a/components/download/internal/scheduler/battery_listener_unittest.cc b/components/download/internal/scheduler/battery_listener_unittest.cc
index 3ddacff..2e7d4fe 100644
--- a/components/download/internal/scheduler/battery_listener_unittest.cc
+++ b/components/download/internal/scheduler/battery_listener_unittest.cc
@@ -10,6 +10,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::InSequence;
+
 namespace download {
 namespace {
 
@@ -17,7 +19,7 @@
 
 class MockObserver : public BatteryListener::Observer {
  public:
-  MOCK_METHOD1(OnBatteryChange, void(SchedulingParams::BatteryRequirements));
+  MOCK_METHOD1(OnBatteryChange, void(BatteryStatus));
 };
 
 class BatteryListenerTest : public testing::Test {
@@ -57,14 +59,13 @@
 
 // Ensures observer methods are corrected called.
 TEST_F(BatteryListenerTest, NotifyObservers) {
+  InSequence s;
   listener_->Start();
-  EXPECT_CALL(*observer_.get(),
-              OnBatteryChange(BatteryRequirements::BATTERY_INSENSITIVE))
+  EXPECT_CALL(*observer_.get(), OnBatteryChange(BatteryStatus::NOT_CHARGING))
       .RetiresOnSaturation();
   CallBatteryChange(true);
 
-  EXPECT_CALL(*observer_.get(),
-              OnBatteryChange(BatteryRequirements::BATTERY_SENSITIVE))
+  EXPECT_CALL(*observer_.get(), OnBatteryChange(BatteryStatus::CHARGING))
       .RetiresOnSaturation();
   CallBatteryChange(false);
   listener_->Stop();
diff --git a/components/download/internal/scheduler/device_status.cc b/components/download/internal/scheduler/device_status.cc
new file mode 100644
index 0000000..a88967a
--- /dev/null
+++ b/components/download/internal/scheduler/device_status.cc
@@ -0,0 +1,64 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/download/internal/scheduler/device_status.h"
+
+namespace download {
+
+DeviceStatus::Result::Result()
+    : meets_battery_requirement(false), meets_network_requirement(false) {}
+
+bool DeviceStatus::Result::MeetsRequirements() const {
+  return meets_battery_requirement && meets_network_requirement;
+}
+
+DeviceStatus::DeviceStatus()
+    : battery_status(BatteryStatus::NOT_CHARGING),
+      network_status(NetworkStatus::DISCONNECTED) {}
+
+bool DeviceStatus::operator==(const DeviceStatus& rhs) {
+  return network_status == rhs.network_status &&
+         battery_status == rhs.battery_status;
+}
+
+DeviceStatus::Result DeviceStatus::MeetsCondition(
+    const SchedulingParams& params) const {
+  DeviceStatus::Result result;
+
+  switch (params.battery_requirements) {
+    case SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE:
+      result.meets_battery_requirement = true;
+      break;
+    case SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE:
+      result.meets_battery_requirement =
+          battery_status == BatteryStatus::CHARGING;
+      break;
+    default:
+      NOTREACHED();
+  }
+  switch (params.network_requirements) {
+    case SchedulingParams::NetworkRequirements::NONE:
+      result.meets_network_requirement =
+          network_status != NetworkStatus::DISCONNECTED;
+      break;
+    case SchedulingParams::NetworkRequirements::OPTIMISTIC:
+    case SchedulingParams::NetworkRequirements::UNMETERED:
+      result.meets_network_requirement =
+          network_status == NetworkStatus::UNMETERED;
+      break;
+    default:
+      NOTREACHED();
+  }
+  return result;
+}
+
+Criteria::Criteria()
+    : requires_battery_charging(true), requires_unmetered_network(true) {}
+
+bool Criteria::operator==(const Criteria& other) const {
+  return requires_battery_charging == other.requires_battery_charging &&
+         requires_unmetered_network == other.requires_unmetered_network;
+}
+
+}  // namespace download
diff --git a/components/download/internal/scheduler/device_status.h b/components/download/internal/scheduler/device_status.h
new file mode 100644
index 0000000..b7a35d2
--- /dev/null
+++ b/components/download/internal/scheduler/device_status.h
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_DEVICE_STATUS_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_DEVICE_STATUS_H_
+
+#include "components/download/public/download_params.h"
+
+namespace download {
+
+// Battery status used in download service.
+enum class BatteryStatus {
+  CHARGING = 0,
+  NOT_CHARGING = 1,
+};
+
+// NetworkStatus should mostly one to one map to
+// SchedulingParams::NetworkRequirements. Has coarser granularity than
+// network connection type.
+enum class NetworkStatus {
+  DISCONNECTED = 0,
+  UNMETERED = 1,  // WIFI or Ethernet.
+  METERED = 2,    // Mobile networks.
+};
+
+// Contains battery and network status.
+struct DeviceStatus {
+  DeviceStatus();
+  struct Result {
+    Result();
+    bool MeetsRequirements() const;
+    bool meets_battery_requirement;
+    bool meets_network_requirement;
+  };
+
+  BatteryStatus battery_status;
+  NetworkStatus network_status;
+
+  bool operator==(const DeviceStatus& rhs);
+
+  // Returns if the current device status meets all the conditions defined in
+  // the scheduling parameters.
+  Result MeetsCondition(const SchedulingParams& params) const;
+};
+
+// The criteria when the background download task should start.
+struct Criteria {
+  Criteria();
+  bool operator==(const Criteria& other) const;
+  bool requires_battery_charging;
+  bool requires_unmetered_network;
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_DEVICE_STATUS_H_
diff --git a/components/download/internal/scheduler/network_listener.cc b/components/download/internal/scheduler/network_listener.cc
index d09d527..3840e4f 100644
--- a/components/download/internal/scheduler/network_listener.cc
+++ b/components/download/internal/scheduler/network_listener.cc
@@ -8,24 +8,23 @@
 
 namespace {
 
-// Converts a ConnectionType to NetworkListener::NetworkStatus.
-NetworkListener::NetworkStatus ToNetworkStatus(
-    net::NetworkChangeNotifier::ConnectionType type) {
+// Converts a ConnectionType to NetworkStatus.
+NetworkStatus ToNetworkStatus(net::NetworkChangeNotifier::ConnectionType type) {
   switch (type) {
     case net::NetworkChangeNotifier::CONNECTION_ETHERNET:
     case net::NetworkChangeNotifier::CONNECTION_WIFI:
-      return NetworkListener::NetworkStatus::UNMETERED;
+      return NetworkStatus::UNMETERED;
     case net::NetworkChangeNotifier::CONNECTION_2G:
     case net::NetworkChangeNotifier::CONNECTION_3G:
     case net::NetworkChangeNotifier::CONNECTION_4G:
-      return NetworkListener::NetworkStatus::METERED;
+      return NetworkStatus::METERED;
     case net::NetworkChangeNotifier::CONNECTION_UNKNOWN:
     case net::NetworkChangeNotifier::CONNECTION_NONE:
     case net::NetworkChangeNotifier::CONNECTION_BLUETOOTH:
-      return NetworkListener::NetworkStatus::DISCONNECTED;
+      return NetworkStatus::DISCONNECTED;
   }
   NOTREACHED();
-  return NetworkListener::NetworkStatus::DISCONNECTED;
+  return NetworkStatus::DISCONNECTED;
 }
 
 }  // namespace
@@ -37,7 +36,7 @@
   Stop();
 }
 
-NetworkListener::NetworkStatus NetworkListener::CurrentNetworkStatus() const {
+NetworkStatus NetworkListener::CurrentNetworkStatus() const {
   return ToNetworkStatus(net::NetworkChangeNotifier::GetConnectionType());
 }
 
diff --git a/components/download/internal/scheduler/network_listener.h b/components/download/internal/scheduler/network_listener.h
index 6b05ffb1..b827909 100644
--- a/components/download/internal/scheduler/network_listener.h
+++ b/components/download/internal/scheduler/network_listener.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_NETWORK_LISTENER_H_
 #define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_NETWORK_LISTENER_H_
 
+#include "components/download/internal/scheduler/device_status.h"
 #include "net/base/network_change_notifier.h"
 
 namespace download {
@@ -13,15 +14,6 @@
 class NetworkListener
     : public net::NetworkChangeNotifier::ConnectionTypeObserver {
  public:
-  // NetworkStatus should mostly one to one map to
-  // SchedulingParams::NetworkRequirements. Has coarser granularity than
-  // network connection type.
-  enum class NetworkStatus {
-    DISCONNECTED = 0,
-    UNMETERED = 1,  // WIFI or Ethernet.
-    METERED = 2,    // Mobile networks.
-  };
-
   class Observer {
    public:
     // Called when network status is changed.
diff --git a/components/download/internal/scheduler/network_listener_unittest.cc b/components/download/internal/scheduler/network_listener_unittest.cc
index 4b8165dce..c4fadac 100644
--- a/components/download/internal/scheduler/network_listener_unittest.cc
+++ b/components/download/internal/scheduler/network_listener_unittest.cc
@@ -45,7 +45,7 @@
 
 class MockObserver : public NetworkListener::Observer {
  public:
-  MOCK_METHOD1(OnNetworkChange, void(NetworkListener::NetworkStatus));
+  MOCK_METHOD1(OnNetworkChange, void(NetworkStatus));
 };
 
 class NetworkListenerTest : public testing::Test {
@@ -69,26 +69,23 @@
   network_listener_.AddObserver(&mock_observer_);
 
   // Initial states check.
-  EXPECT_EQ(NetworkListener::NetworkStatus::DISCONNECTED,
+  EXPECT_EQ(NetworkStatus::DISCONNECTED,
             network_listener_.CurrentNetworkStatus());
 
   // Network switch between mobile networks, the observer should be notified
   // only once.
-  EXPECT_CALL(mock_observer_,
-              OnNetworkChange(NetworkListener::NetworkStatus::METERED))
+  EXPECT_CALL(mock_observer_, OnNetworkChange(NetworkStatus::METERED))
       .Times(1)
       .RetiresOnSaturation();
 
   ChangeNetworkType(ConnectionType::CONNECTION_4G);
   ChangeNetworkType(ConnectionType::CONNECTION_3G);
   ChangeNetworkType(ConnectionType::CONNECTION_2G);
-  EXPECT_EQ(NetworkListener::NetworkStatus::METERED,
-            network_listener_.CurrentNetworkStatus());
+  EXPECT_EQ(NetworkStatus::METERED, network_listener_.CurrentNetworkStatus());
 
   // Network is switched between wifi and ethernet, the observer should be
   // notified only once.
-  EXPECT_CALL(mock_observer_,
-              OnNetworkChange(NetworkListener::NetworkStatus::UNMETERED))
+  EXPECT_CALL(mock_observer_, OnNetworkChange(NetworkStatus::UNMETERED))
       .Times(1)
       .RetiresOnSaturation();
 
diff --git a/components/download/internal/scheduler/scheduler.h b/components/download/internal/scheduler/scheduler.h
new file mode 100644
index 0000000..3cb0034
--- /dev/null
+++ b/components/download/internal/scheduler/scheduler.h
@@ -0,0 +1,41 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_SCHEDULER_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_SCHEDULER_H_
+
+#include "components/download/internal/model.h"
+#include "components/download/internal/scheduler/device_status.h"
+#include "components/download/public/download_params.h"
+
+namespace download {
+
+struct DeviceStatus;
+
+// The interface that talks to download service to schedule platform background
+// download tasks.
+class Scheduler {
+ public:
+  // Reschedule another background platform task. Called when downloads are
+  // added or removed or the criteria has changed.
+  virtual void Reschedule(const Model::EntryList& entries) = 0;
+
+  // Returns the next download that should be processed based on scheduling
+  // parameters, may return nullptr if no download meets the criteria.
+  // The sequence of polling on entries with exactly same states is undefined.
+  virtual Entry* Next(const Model::EntryList& entries,
+                      const DeviceStatus& device_status) = 0;
+};
+
+// Interface to schedule platform dependent background tasks that can run after
+// browser being closed.
+class PlatformTaskScheduler {
+ public:
+  virtual void ScheduleDownloadTask(const Criteria& criteria) = 0;
+  virtual void CancelDownloadTask() = 0;
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_CORE_SCHEDULER_H_
diff --git a/components/download/internal/scheduler/scheduler_impl.cc b/components/download/internal/scheduler/scheduler_impl.cc
new file mode 100644
index 0000000..1233dda
--- /dev/null
+++ b/components/download/internal/scheduler/scheduler_impl.cc
@@ -0,0 +1,109 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/download/internal/scheduler/scheduler_impl.h"
+
+#include "components/download/internal/entry_utils.h"
+#include "components/download/internal/scheduler/device_status.h"
+#include "components/download/public/download_params.h"
+
+namespace download {
+
+SchedulerImpl::SchedulerImpl(PlatformTaskScheduler* platform_scheduler,
+                             const std::vector<DownloadClient>& clients)
+    : platform_scheduler_(platform_scheduler),
+      download_clients_(clients),
+      current_client_index_(0) {
+  DCHECK(!clients.empty());
+}
+
+SchedulerImpl::~SchedulerImpl() = default;
+
+void SchedulerImpl::Reschedule(const Model::EntryList& entries) {
+  if (entries.empty()) {
+    if (platform_scheduler_)
+      platform_scheduler_->CancelDownloadTask();
+    return;
+  }
+
+  // TODO(xingliu): Figure out if we need to pass the time window to platform
+  // scheduler, and support NetworkRequirements::OPTIMISTIC.
+  if (platform_scheduler_) {
+    platform_scheduler_->CancelDownloadTask();
+    platform_scheduler_->ScheduleDownloadTask(
+        util::GetSchedulingCriteria(entries));
+  }
+}
+
+Entry* SchedulerImpl::Next(const Model::EntryList& entries,
+                           const DeviceStatus& device_status) {
+  std::map<DownloadClient, Entry*> candidates =
+      FindCandidates(entries, device_status);
+
+  Entry* entry = nullptr;
+  size_t index = current_client_index_;
+
+  // Finds the next entry to download.
+  for (size_t i = 0; i < download_clients_.size(); ++i) {
+    DownloadClient client =
+        download_clients_[(index + i) % download_clients_.size()];
+    Entry* candidate = candidates[client];
+
+    // Some clients may have no entries, continue to check other clients.
+    if (!candidate)
+      continue;
+
+    bool ui_priority =
+        candidate->scheduling_params.priority == SchedulingParams::Priority::UI;
+
+    // Records the first available candidate. Keep iterating to see if there
+    // are UI priority entries for other clients.
+    if (!entry || ui_priority) {
+      entry = candidate;
+      DCHECK(entry);
+
+      // Load balancing between clients.
+      current_client_index_ = (index + i + 1) % download_clients_.size();
+
+      // UI priority entry will be processed immediately.
+      if (ui_priority)
+        break;
+    }
+  }
+  return entry;
+}
+
+std::map<DownloadClient, Entry*> SchedulerImpl::FindCandidates(
+    const Model::EntryList& entries,
+    const DeviceStatus& device_status) {
+  std::map<DownloadClient, Entry*> candidates;
+
+  if (entries.empty())
+    return candidates;
+
+  for (auto* const entry : entries) {
+    DCHECK(entry);
+    const SchedulingParams& current_params = entry->scheduling_params;
+
+    // Every download needs to pass the state and device status check.
+    if (entry->state != Entry::State::AVAILABLE ||
+        !device_status.MeetsCondition(current_params).MeetsRequirements()) {
+      continue;
+    }
+
+    // Find the most appropriate download based on priority and cancel time.
+    Entry* candidate = candidates[entry->client];
+    if (!candidate ||
+        (current_params.priority > candidate->scheduling_params.priority ||
+         (current_params.priority == candidate->scheduling_params.priority &&
+          entry->scheduling_params.cancel_time <
+              candidate->scheduling_params.cancel_time))) {
+      candidates[entry->client] = entry;
+    }
+  }
+
+  return candidates;
+}
+
+}  // namespace download
diff --git a/components/download/internal/scheduler/scheduler_impl.h b/components/download/internal/scheduler/scheduler_impl.h
new file mode 100644
index 0000000..30c2969
--- /dev/null
+++ b/components/download/internal/scheduler/scheduler_impl.h
@@ -0,0 +1,61 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_SCHEDULER_IMPL_H_
+#define COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_SCHEDULER_IMPL_H_
+
+#include "components/download/internal/scheduler/scheduler.h"
+
+#include <map>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/download/internal/entry.h"
+
+namespace download {
+
+// Scheduler implementation that
+// 1. Creates platform background task based on the states of download entries.
+// 2. Polls the next entry to be processed by the service mainly according to
+// scheduling parameters and current device status.
+//
+// Provides load balancing between download clients using the service.
+class SchedulerImpl : public Scheduler {
+ public:
+  SchedulerImpl(PlatformTaskScheduler* platform_scheduler,
+                const std::vector<DownloadClient>& clients);
+  ~SchedulerImpl();
+
+  // Scheduler implementation.
+  void Reschedule(const Model::EntryList& entries) override;
+  Entry* Next(const Model::EntryList& entries,
+              const DeviceStatus& device_status) override;
+
+ private:
+  // Finds a candidate for each download client to be processed next by the
+  // service.
+  // The candidates are selected based on scheduling parameters and current
+  // device status.
+  std::map<DownloadClient, Entry*> FindCandidates(
+      const Model::EntryList& entries,
+      const DeviceStatus& device_status);
+
+  // Used to create platform dependent background tasks.
+  PlatformTaskScheduler* platform_scheduler_;
+
+  // List of all download client id, used in round robin load balancing.
+  // Downloads will be delivered to clients with incremental order based on
+  // the index of this list.
+  const std::vector<DownloadClient> download_clients_;
+
+  // The index of the current client.
+  // See |download_clients_|.
+  size_t current_client_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(SchedulerImpl);
+};
+
+}  // namespace download
+
+#endif  // COMPONENTS_DOWNLOAD_INTERNAL_SCHEDULER_SCHEDULER_IMPL_H_
diff --git a/components/download/internal/scheduler/scheduler_impl_unittest.cc b/components/download/internal/scheduler/scheduler_impl_unittest.cc
new file mode 100644
index 0000000..7730af5a2
--- /dev/null
+++ b/components/download/internal/scheduler/scheduler_impl_unittest.cc
@@ -0,0 +1,413 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/download/internal/scheduler/scheduler_impl.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/download/internal/entry.h"
+#include "components/download/internal/scheduler/device_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::InSequence;
+
+namespace download {
+namespace {
+
+class MockPlatformTaskScheduler : public PlatformTaskScheduler {
+ public:
+  MOCK_METHOD1(ScheduleDownloadTask, void(const Criteria&));
+  MOCK_METHOD0(CancelDownloadTask, void());
+};
+
+class DownloadSchedulerImplTest : public testing::Test {
+ public:
+  DownloadSchedulerImplTest() {}
+  ~DownloadSchedulerImplTest() override = default;
+
+  void TearDown() override { DestroyScheduler(); }
+
+  void BuildScheduler(const std::vector<DownloadClient> clients) {
+    scheduler_ =
+        base::MakeUnique<SchedulerImpl>(&platform_task_scheduler_, clients);
+  }
+  void DestroyScheduler() { scheduler_.reset(); }
+
+  // Helper function to create a list of entries for the scheduler to query the
+  // next entry.
+  void BuildDataEntries(size_t size) {
+    entries_ = std::vector<Entry>(size, Entry());
+    for (size_t i = 0; i < size; ++i) {
+      entries_[i].guid = base::IntToString(i);
+      entries_[i].scheduling_params.battery_requirements =
+          SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+      entries_[i].scheduling_params.network_requirements =
+          SchedulingParams::NetworkRequirements::UNMETERED;
+      entries_[i].state = Entry::State::AVAILABLE;
+    }
+  }
+
+  // Returns list of entry pointers to feed to the scheduler.
+  Model::EntryList entries() {
+    Model::EntryList entry_list;
+    for (auto& entry : entries_) {
+      entry_list.emplace_back(&entry);
+    }
+    return entry_list;
+  }
+
+  // Simulates the entry has been processed by the download service and the
+  // state has changed.
+  void MakeEntryActive(Entry* entry) {
+    if (entry)
+      entry->state = Entry::State::ACTIVE;
+  }
+
+  // Reverts the states of entry so that the scheduler can poll it again.
+  void MakeEntryAvailable(Entry* entry) {
+    entry->state = Entry::State::AVAILABLE;
+  }
+
+  // Helper function to build a device status.
+  DeviceStatus BuildDeviceStatus(BatteryStatus battery, NetworkStatus network) {
+    DeviceStatus device_status;
+    device_status.battery_status = battery;
+    device_status.network_status = network;
+    return device_status;
+  }
+
+ protected:
+  std::unique_ptr<SchedulerImpl> scheduler_;
+  MockPlatformTaskScheduler platform_task_scheduler_;
+
+  // Entries owned by the test fixture.
+  std::vector<Entry> entries_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DownloadSchedulerImplTest);
+};
+
+// Ensures normal polling logic is correct.
+TEST_F(DownloadSchedulerImplTest, BasicPolling) {
+  BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST_2,
+                                             DownloadClient::TEST});
+
+  // Client TEST: entry 0.
+  // Client TEST_2: entry 1.
+  // Poll sequence: 1 -> 0.
+  BuildDataEntries(2);
+  entries_[0].client = DownloadClient::TEST;
+  entries_[1].client = DownloadClient::TEST_2;
+
+  // First download belongs to first client.
+  Entry* next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(next, &entries_[1]);
+  MakeEntryActive(next);
+
+  // If the first one is processed, the next should be the other entry.
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(next, &entries_[0]);
+  MakeEntryActive(next);
+}
+
+// Tests the load balancing and polling downloads based on cancel time.
+TEST_F(DownloadSchedulerImplTest, BasicLoadBalancing) {
+  BuildScheduler(std::vector<DownloadClient>{
+      DownloadClient::TEST, DownloadClient::TEST_2, DownloadClient::TEST_3});
+
+  // Client TEST: entry 0, entry 1 (earlier cancel time).
+  // Client TEST_2: entry 2.
+  // Client TEST_3: No entries.
+  // Poll sequence: 1 -> 2 -> 0.
+  BuildDataEntries(3);
+  entries_[0].client = DownloadClient::TEST;
+  entries_[0].scheduling_params.cancel_time = base::Time::FromInternalValue(20);
+  entries_[1].client = DownloadClient::TEST;
+  entries_[1].scheduling_params.cancel_time = base::Time::FromInternalValue(10);
+  entries_[2].client = DownloadClient::TEST_2;
+  entries_[2].scheduling_params.cancel_time = base::Time::FromInternalValue(30);
+
+  // There are 2 downloads for client 0, the one with earlier create time will
+  // be the next download.
+  Entry* next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[1], next);
+  MakeEntryActive(next);
+
+  // The second download should belongs to client 1, because of the round robin
+  // load balancing.
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[2], next);
+  MakeEntryActive(next);
+
+  // Only one entry left, which will be the next.
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[0], next);
+  MakeEntryActive(next);
+
+  // Keep polling twice, since no available downloads, both will return nullptr.
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(nullptr, next);
+  MakeEntryActive(next);
+
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(nullptr, next);
+  MakeEntryActive(next);
+}
+
+// Ensures downloads are polled based on scheduling parameters and device
+// status.
+TEST_F(DownloadSchedulerImplTest, SchedulingParams) {
+  BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST});
+  BuildDataEntries(1);
+  entries_[0].client = DownloadClient::TEST;
+  entries_[0].scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+  entries_[0].scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::UNMETERED;
+
+  Entry* next = nullptr;
+
+  // Tests network scheduling parameter.
+  // No downloads can be polled when network disconnected.
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::DISCONNECTED));
+  EXPECT_EQ(nullptr, next);
+
+  // If the network is metered, and scheduling parameter requires unmetered
+  // network, the download should not be polled.
+  next = scheduler_->Next(entries(), BuildDeviceStatus(BatteryStatus::CHARGING,
+                                                       NetworkStatus::METERED));
+  EXPECT_EQ(nullptr, next);
+
+  // If the network requirement is none, the download can happen under metered
+  // network. However, download won't happen when network is disconnected.
+  entries_[0].scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::NONE;
+  next = scheduler_->Next(entries(), BuildDeviceStatus(BatteryStatus::CHARGING,
+                                                       NetworkStatus::METERED));
+  EXPECT_EQ(&entries_[0], next);
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::DISCONNECTED));
+  EXPECT_EQ(nullptr, next);
+  MakeEntryActive(next);
+
+  // Tests battery sensitive scheduling parameter.
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[0], next);
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::NOT_CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(nullptr, next);
+  MakeEntryActive(next);
+
+  entries_[0].scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::NOT_CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[0], next);
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[0], next);
+  MakeEntryActive(next);
+}
+
+// Ensures higher priority will be scheduled first.
+TEST_F(DownloadSchedulerImplTest, Priority) {
+  BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST});
+
+  // The second entry has higher priority but is created later than the first
+  // entry. This ensures priority is checked before the create time.
+  BuildDataEntries(2);
+  entries_[0].client = DownloadClient::TEST;
+  entries_[0].scheduling_params.priority = SchedulingParams::Priority::LOW;
+  entries_[0].scheduling_params.cancel_time = base::Time::FromInternalValue(20);
+  entries_[1].client = DownloadClient::TEST;
+  entries_[1].scheduling_params.priority = SchedulingParams::Priority::HIGH;
+  entries_[1].scheduling_params.cancel_time = base::Time::FromInternalValue(40);
+
+  // Download with higher priority should be polled first, even if there is
+  // another download created earlier.
+  Entry* next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[1], next);
+
+  // Download with non UI priority should be subject to network and battery
+  // scheduling parameters. The higher priority one will be ignored because of
+  // mismatching battery condition.
+  entries_[1].scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+  entries_[0].scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::NOT_CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[0], next);
+  MakeEntryActive(next);
+}
+
+// Ensures UI priority entries are subject to device status check.
+TEST_F(DownloadSchedulerImplTest, UIPrioritySubjectToDeviceStatus) {
+  BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST,
+                                             DownloadClient::TEST_2});
+
+  // Client TEST: entry 0.
+  // Client TEST_2: entry 1 (UI priority, cancel later).
+  BuildDataEntries(2);
+  entries_[0].client = DownloadClient::TEST;
+  entries_[0].scheduling_params.priority = SchedulingParams::Priority::LOW;
+  entries_[1].client = DownloadClient::TEST_2;
+  entries_[1].scheduling_params.priority = SchedulingParams::Priority::UI;
+
+  // UI priority is also subject to device status validation.
+  Entry* next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::NOT_CHARGING, NetworkStatus::METERED));
+  EXPECT_EQ(nullptr, next);
+  MakeEntryActive(next);
+}
+
+// UI priority entries will be processed first even if they doesn't belong to
+// the current client in load balancing.
+TEST_F(DownloadSchedulerImplTest, UIPriorityLoadBalancing) {
+  BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST,
+                                             DownloadClient::TEST_2});
+
+  // Client TEST: entry 0(Low priority).
+  // Client TEST_2: entry 1(UI priority).
+  BuildDataEntries(2);
+  entries_[0].client = DownloadClient::TEST;
+  entries_[0].scheduling_params.priority = SchedulingParams::Priority::LOW;
+  entries_[1].client = DownloadClient::TEST_2;
+  entries_[1].scheduling_params.priority = SchedulingParams::Priority::UI;
+
+  Entry* next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[1], next);
+  MakeEntryActive(next);
+}
+
+// When multiple UI priority entries exist, the next entry is selected based on
+// cancel time and load balancing.
+TEST_F(DownloadSchedulerImplTest, MultipleUIPriorityEntries) {
+  BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST,
+                                             DownloadClient::TEST_2});
+  BuildDataEntries(4);
+
+  // Client TEST: entry 0(UI priority), entry 1(UI priority, early cancel time).
+  // Client TEST_2: entry 2(UI priority), entry 3(high priority, early cancel
+  // time). Poll sequence: 1 -> 2 -> 0 -> 3.
+  for (auto& entry : entries_) {
+    entry.scheduling_params.priority = SchedulingParams::Priority::UI;
+  }
+  entries_[0].client = DownloadClient::TEST;
+  entries_[0].scheduling_params.cancel_time = base::Time::FromInternalValue(40);
+  entries_[1].client = DownloadClient::TEST;
+  entries_[1].scheduling_params.cancel_time = base::Time::FromInternalValue(20);
+  entries_[2].client = DownloadClient::TEST_2;
+  entries_[2].scheduling_params.cancel_time = base::Time::FromInternalValue(50);
+  entries_[3].client = DownloadClient::TEST_2;
+  entries_[3].scheduling_params.cancel_time = base::Time::FromInternalValue(20);
+  entries_[3].scheduling_params.priority = SchedulingParams::Priority::HIGH;
+
+  // When device conditions are meet, UI priority entry with the earliest cancel
+  // time will be processed first.
+  Entry* next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[1], next);
+  MakeEntryActive(next);
+
+  // Next entry will be UI priority entry from another client.
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[2], next);
+  MakeEntryActive(next);
+
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[0], next);
+  MakeEntryActive(next);
+
+  next = scheduler_->Next(
+      entries(),
+      BuildDeviceStatus(BatteryStatus::CHARGING, NetworkStatus::UNMETERED));
+  EXPECT_EQ(&entries_[3], next);
+  MakeEntryActive(next);
+}
+
+// Ensures the reschedule logic works correctly, and we can pass the correct
+// criteria to platform task scheduler.
+TEST_F(DownloadSchedulerImplTest, Reschedule) {
+  InSequence s;
+
+  BuildScheduler(std::vector<DownloadClient>{DownloadClient::TEST});
+  BuildDataEntries(2);
+  entries_[0].client = DownloadClient::TEST;
+  entries_[1].client = DownloadClient::TEST;
+
+  entries_[0].scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+  entries_[0].scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::UNMETERED;
+  entries_[1].scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_SENSITIVE;
+  entries_[1].scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::UNMETERED;
+
+  Criteria criteria;
+  EXPECT_CALL(platform_task_scheduler_, CancelDownloadTask())
+      .RetiresOnSaturation();
+  EXPECT_CALL(platform_task_scheduler_, ScheduleDownloadTask(criteria))
+      .RetiresOnSaturation();
+  scheduler_->Reschedule(entries());
+
+  entries_[0].scheduling_params.battery_requirements =
+      SchedulingParams::BatteryRequirements::BATTERY_INSENSITIVE;
+  criteria.requires_battery_charging = false;
+  EXPECT_CALL(platform_task_scheduler_, CancelDownloadTask())
+      .RetiresOnSaturation();
+  EXPECT_CALL(platform_task_scheduler_, ScheduleDownloadTask(criteria))
+      .RetiresOnSaturation();
+  scheduler_->Reschedule(entries());
+
+  entries_[0].scheduling_params.network_requirements =
+      SchedulingParams::NetworkRequirements::NONE;
+  criteria.requires_unmetered_network = false;
+  EXPECT_CALL(platform_task_scheduler_, CancelDownloadTask())
+      .RetiresOnSaturation();
+  EXPECT_CALL(platform_task_scheduler_, ScheduleDownloadTask(criteria))
+      .RetiresOnSaturation();
+  scheduler_->Reschedule(entries());
+}
+
+}  // namespace
+}  // namespace download
diff --git a/components/download/public/clients.h b/components/download/public/clients.h
index 36665b6..3939aea 100644
--- a/components/download/public/clients.h
+++ b/components/download/public/clients.h
@@ -20,17 +20,19 @@
 // but also to make sure the underlying database properly associates each
 // download with the right client.
 enum class DownloadClient {
-  // Represents an uninitialized DownloadClient variable.
-  INVALID = 0,
-
   // Test client values.  Meant to be used by the testing framework and not
   // production code.  Callers will be unable to access the DownloadService with
   // these test APIs.
-  TEST = 1,
+  TEST = -1,
+  TEST_2 = -2,
+  TEST_3 = -3,
 
-  OFFLINE_PAGE_PREFETCH = 2,
+  // Represents an uninitialized DownloadClient variable.
+  INVALID = 0,
 
-  BOUNDARY = 3,
+  OFFLINE_PAGE_PREFETCH = 1,
+
+  BOUNDARY = 2,
 };
 
 using DownloadClientMap = std::map<DownloadClient, std::unique_ptr<Client>>;
diff --git a/components/feature_engagement_tracker/README.md b/components/feature_engagement_tracker/README.md
index e912703..d357494c99 100644
--- a/components/feature_engagement_tracker/README.md
+++ b/components/feature_engagement_tracker/README.md
@@ -13,21 +13,6 @@
 The backend is feature agnostic and have no special logic for any specific
 features, but instead provides a generic API.
 
-## Compiling for platforms other than Android
-
-For now the code for the Feature Engagement Tracker is only compiled in
-for Android, but only a shim layer is really dependent on Android to provide a
-JNI bridge. The goal is to keep all the business logic in the cross-platform
-part of the code.
-
-For local development, it is therefore possible to compile and run tests for
-the core of the tracker on other platforms. To do this, simply add the
-following line to the `//components:components_unittests` target:
-
-```python
-deps += [ "//components/feature_engagement_tracker:unit_tests" ]
-```
-
 ## Testing
 
 To compile and run tests, assuming the product out directory is `out/Debug`,
diff --git a/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc b/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
index 9d92884..b2d9374a 100644
--- a/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
+++ b/components/feature_engagement_tracker/internal/feature_engagement_tracker_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/feature_list.h"
+#include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/user_metrics.h"
@@ -35,8 +36,10 @@
 namespace feature_engagement_tracker {
 
 namespace {
-const char kEventDBStorageDir[] = "EventDB";
-const char kAvailabilityDBStorageDir[] = "AvailabilityDB";
+const base::FilePath::CharType kEventDBStorageDir[] =
+    FILE_PATH_LITERAL("EventDB");
+const base::FilePath::CharType kAvailabilityDBStorageDir[] =
+    FILE_PATH_LITERAL("AvailabilityDB");
 
 // Creates a FeatureEngagementTrackerImpl that is usable for a demo mode.
 std::unique_ptr<FeatureEngagementTracker>
diff --git a/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc b/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc
index 6bcf53c..e9d08c0 100644
--- a/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc
+++ b/components/feature_engagement_tracker/internal/system_time_provider_unittest.cc
@@ -17,6 +17,7 @@
   exploded_time.year = year;
   exploded_time.month = month;
   exploded_time.day_of_month = day;
+  exploded_time.day_of_week = 0;
   exploded_time.hour = 0;
   exploded_time.minute = 0;
   exploded_time.second = 0;
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn
index e60183b..1675bea 100644
--- a/components/history/core/browser/BUILD.gn
+++ b/components/history/core/browser/BUILD.gn
@@ -200,6 +200,7 @@
     "top_sites_cache_unittest.cc",
     "top_sites_database_unittest.cc",
     "top_sites_impl_unittest.cc",
+    "typed_url_sync_bridge_unittest.cc",
     "typed_url_sync_metadata_database_unittest.cc",
     "typed_url_syncable_service_unittest.cc",
     "url_database_unittest.cc",
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index bb21144..2a9e668d 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -41,7 +41,6 @@
 #include "components/history/core/browser/in_memory_history_backend.h"
 #include "components/history/core/browser/keyword_search_term.h"
 #include "components/history/core/browser/page_usage_data.h"
-#include "components/history/core/browser/typed_url_sync_bridge.h"
 #include "components/history/core/browser/typed_url_syncable_service.h"
 #include "components/history/core/browser/url_utils.h"
 #include "components/sync/driver/sync_driver_switches.h"
@@ -224,6 +223,7 @@
             &ModelTypeChangeProcessor::Create,
             // TODO(gangwu): use ReportUnrecoverableError before launch.
             base::BindRepeating(base::IgnoreResult(&DumpWithoutCrashing))));
+    typed_url_sync_bridge_->Init();
   } else {
     typed_url_syncable_service_ =
         base::MakeUnique<TypedUrlSyncableService>(this);
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h
index 59c5838..cae7870 100644
--- a/components/history/core/browser/history_backend.h
+++ b/components/history/core/browser/history_backend.h
@@ -32,6 +32,7 @@
 #include "components/history/core/browser/history_types.h"
 #include "components/history/core/browser/keyword_id.h"
 #include "components/history/core/browser/thumbnail_database.h"
+#include "components/history/core/browser/typed_url_sync_bridge.h"
 #include "components/history/core/browser/visit_tracker.h"
 #include "sql/init_status.h"
 
@@ -53,7 +54,6 @@
 class HistoryDBTask;
 class InMemoryHistoryBackend;
 class TypedUrlSyncableService;
-class TypedURLSyncBridge;
 class HistoryBackendHelper;
 class URLDatabase;
 
@@ -476,6 +476,11 @@
   HistoryDatabase* db() const { return db_.get(); }
 
   ExpireHistoryBackend* expire_backend() { return &expirer_; }
+
+  void SetTypedURLSyncBridgeForTest(
+      std::unique_ptr<TypedURLSyncBridge> bridge) {
+    typed_url_sync_bridge_ = std::move(bridge);
+  }
 #endif
 
   // Returns true if the passed visit time is already expired (used by the sync
diff --git a/components/history/core/browser/typed_url_sync_bridge.cc b/components/history/core/browser/typed_url_sync_bridge.cc
index 40d57d6..48edb4e 100644
--- a/components/history/core/browser/typed_url_sync_bridge.cc
+++ b/components/history/core/browser/typed_url_sync_bridge.cc
@@ -4,22 +4,67 @@
 
 #include "components/history/core/browser/typed_url_sync_bridge.h"
 
+#include "base/big_endian.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/history/core/browser/history_backend.h"
+#include "components/sync/model/mutable_data_batch.h"
 #include "components/sync/model_impl/sync_metadata_store_change_list.h"
 
+using syncer::EntityData;
+using sync_pb::TypedUrlSpecifics;
+using syncer::MutableDataBatch;
+
 namespace history {
 
+namespace {
+
+// The server backend can't handle arbitrarily large node sizes, so to keep
+// the size under control we limit the visit array.
+static const int kMaxTypedUrlVisits = 100;
+
+// There's no limit on how many visits the history DB could have for a given
+// typed URL, so we limit how many we fetch from the DB to avoid crashes due to
+// running out of memory (http://crbug.com/89793). This value is different
+// from kMaxTypedUrlVisits, as some of the visits fetched from the DB may be
+// RELOAD visits, which will be stripped.
+static const int kMaxVisitsToFetch = 1000;
+
+// Enforce oldest to newest visit order.
+static bool CheckVisitOrdering(const VisitVector& visits) {
+  int64_t previous_visit_time = 0;
+  for (VisitVector::const_iterator visit = visits.begin();
+       visit != visits.end(); ++visit) {
+    if (visit != visits.begin() &&
+        previous_visit_time > visit->visit_time.ToInternalValue())
+      return false;
+
+    previous_visit_time = visit->visit_time.ToInternalValue();
+  }
+  return true;
+}
+
+std::string GetStorageKeyFromURLRow(const URLRow& row) {
+  std::string storage_key(sizeof(row.id()), 0);
+  base::WriteBigEndian<URLID>(&storage_key[0], row.id());
+  return storage_key;
+}
+
+}  // namespace
+
 TypedURLSyncBridge::TypedURLSyncBridge(
     HistoryBackend* history_backend,
-    syncer::SyncMetadataStore* sync_metadata_store,
+    TypedURLSyncMetadataDatabase* sync_metadata_database,
     const ChangeProcessorFactory& change_processor_factory)
     : ModelTypeSyncBridge(change_processor_factory, syncer::TYPED_URLS),
       history_backend_(history_backend),
-      sync_metadata_store_(sync_metadata_store) {
+      sync_metadata_database_(sync_metadata_database),
+      num_db_accesses_(0),
+      num_db_errors_(0),
+      history_backend_observer_(this) {
   DCHECK(history_backend_);
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  DCHECK(sync_metadata_store_);
-  NOTIMPLEMENTED();
+  DCHECK(sync_metadata_database_);
 }
 
 TypedURLSyncBridge::~TypedURLSyncBridge() {
@@ -31,7 +76,7 @@
 TypedURLSyncBridge::CreateMetadataChangeList() {
   DCHECK(sequence_checker_.CalledOnValidSequence());
   return base::MakeUnique<syncer::SyncMetadataStoreChangeList>(
-      sync_metadata_store_, syncer::TYPED_URLS);
+      sync_metadata_database_, syncer::TYPED_URLS);
 }
 
 base::Optional<syncer::ModelError> TypedURLSyncBridge::MergeSyncData(
@@ -58,15 +103,31 @@
 
 void TypedURLSyncBridge::GetAllData(DataCallback callback) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  NOTIMPLEMENTED();
+
+  history::URLRows typed_urls;
+  ++num_db_accesses_;
+  if (!history_backend_->GetAllTypedURLs(&typed_urls)) {
+    ++num_db_errors_;
+    change_processor()->ReportError(FROM_HERE,
+                                    "Could not get the typed_url entries.");
+    return;
+  }
+
+  auto batch = base::MakeUnique<MutableDataBatch>();
+  for (history::URLRow& url : typed_urls) {
+    VisitVector visits_vector;
+    FixupURLAndGetVisits(&url, &visits_vector);
+    batch->Put(GetStorageKeyFromURLRow(url),
+               CreateEntityData(url, visits_vector));
+  }
+  callback.Run(std::move(batch));
 }
 
 // Must be exactly the value of GURL::spec() for backwards comparability with
 // the previous (Directory + SyncableService) iteration of sync integration.
 // This can be large but it is assumed that this is not held in memory at steady
 // state.
-std::string TypedURLSyncBridge::GetClientTag(
-    const syncer::EntityData& entity_data) {
+std::string TypedURLSyncBridge::GetClientTag(const EntityData& entity_data) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
   DCHECK(entity_data.specifics.has_typed_url())
       << "EntityData does not have typed urls specifics.";
@@ -76,11 +137,25 @@
 
 // Prefer to use URLRow::id() to uniquely identify entities when coordinating
 // with sync because it has a significantly low memory cost than a URL.
-std::string TypedURLSyncBridge::GetStorageKey(
-    const syncer::EntityData& entity_data) {
+std::string TypedURLSyncBridge::GetStorageKey(const EntityData& entity_data) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
-  NOTIMPLEMENTED();
-  return std::string();
+  DCHECK(history_backend_);
+  DCHECK(entity_data.specifics.has_typed_url())
+      << "EntityData does not have typed urls specifics.";
+
+  const TypedUrlSpecifics& typed_url(entity_data.specifics.typed_url());
+  URLRow existing_url;
+  ++num_db_accesses_;
+  bool is_existing_url =
+      history_backend_->GetURL(GURL(typed_url.url()), &existing_url);
+
+  if (!is_existing_url) {
+    // The typed url did not save to local history database yet, so return URL
+    // for now.
+    return entity_data.specifics.typed_url().url();
+  }
+
+  return GetStorageKeyFromURLRow(existing_url);
 }
 
 void TypedURLSyncBridge::OnURLVisited(history::HistoryBackend* history_backend,
@@ -108,4 +183,209 @@
   NOTIMPLEMENTED();
 }
 
+void TypedURLSyncBridge::Init() {
+  DCHECK(sequence_checker_.CalledOnValidSequence());
+
+  history_backend_observer_.Add(history_backend_);
+  LoadMetadata();
+}
+
+int TypedURLSyncBridge::GetErrorPercentage() const {
+  return num_db_accesses_ ? (100 * num_db_errors_ / num_db_accesses_) : 0;
+}
+
+bool TypedURLSyncBridge::WriteToTypedUrlSpecifics(
+    const URLRow& url,
+    const VisitVector& visits,
+    TypedUrlSpecifics* typed_url) {
+  DCHECK(!url.last_visit().is_null());
+  DCHECK(!visits.empty());
+  DCHECK_EQ(url.last_visit().ToInternalValue(),
+            visits.back().visit_time.ToInternalValue());
+
+  typed_url->set_url(url.url().spec());
+  typed_url->set_title(base::UTF16ToUTF8(url.title()));
+  typed_url->set_hidden(url.hidden());
+
+  DCHECK(CheckVisitOrdering(visits));
+
+  bool only_typed = false;
+  int skip_count = 0;
+
+  if (std::find_if(visits.begin(), visits.end(),
+                   [](const history::VisitRow& visit) {
+                     return ui::PageTransitionCoreTypeIs(
+                         visit.transition, ui::PAGE_TRANSITION_TYPED);
+                   }) == visits.end()) {
+    // This URL has no TYPED visits, don't sync it
+    return false;
+  }
+
+  if (visits.size() > static_cast<size_t>(kMaxTypedUrlVisits)) {
+    int typed_count = 0;
+    int total = 0;
+    // Walk the passed-in visit vector and count the # of typed visits.
+    for (VisitRow visit : visits) {
+      // We ignore reload visits.
+      if (PageTransitionCoreTypeIs(visit.transition,
+                                   ui::PAGE_TRANSITION_RELOAD)) {
+        continue;
+      }
+      ++total;
+      if (PageTransitionCoreTypeIs(visit.transition,
+                                   ui::PAGE_TRANSITION_TYPED)) {
+        ++typed_count;
+      }
+    }
+
+    // We should have at least one typed visit. This can sometimes happen if
+    // the history DB has an inaccurate count for some reason (there's been
+    // bugs in the history code in the past which has left users in the wild
+    // with incorrect counts - http://crbug.com/84258).
+    DCHECK(typed_count > 0);
+
+    if (typed_count > kMaxTypedUrlVisits) {
+      only_typed = true;
+      skip_count = typed_count - kMaxTypedUrlVisits;
+    } else if (total > kMaxTypedUrlVisits) {
+      skip_count = total - kMaxTypedUrlVisits;
+    }
+  }
+
+  for (const auto& visit : visits) {
+    // Skip reload visits.
+    if (PageTransitionCoreTypeIs(visit.transition, ui::PAGE_TRANSITION_RELOAD))
+      continue;
+
+    // If we only have room for typed visits, then only add typed visits.
+    if (only_typed && !PageTransitionCoreTypeIs(visit.transition,
+                                                ui::PAGE_TRANSITION_TYPED)) {
+      continue;
+    }
+
+    if (skip_count > 0) {
+      // We have too many entries to fit, so we need to skip the oldest ones.
+      // Only skip typed URLs if there are too many typed URLs to fit.
+      if (only_typed || !PageTransitionCoreTypeIs(visit.transition,
+                                                  ui::PAGE_TRANSITION_TYPED)) {
+        --skip_count;
+        continue;
+      }
+    }
+    typed_url->add_visits(visit.visit_time.ToInternalValue());
+    typed_url->add_visit_transitions(visit.transition);
+  }
+  DCHECK_EQ(skip_count, 0);
+
+  CHECK_GT(typed_url->visits_size(), 0);
+  CHECK_LE(typed_url->visits_size(), kMaxTypedUrlVisits);
+  CHECK_EQ(typed_url->visits_size(), typed_url->visit_transitions_size());
+
+  return true;
+}
+
+void TypedURLSyncBridge::LoadMetadata() {
+  if (!history_backend_ || !sync_metadata_database_) {
+    change_processor()->ReportError(
+        FROM_HERE, "Failed to load TypedURLSyncMetadataDatabase.");
+    return;
+  }
+
+  auto batch = base::MakeUnique<syncer::MetadataBatch>();
+  if (!sync_metadata_database_->GetAllSyncMetadata(batch.get())) {
+    change_processor()->ReportError(
+        FROM_HERE,
+        "Failed reading typed url metadata from TypedURLSyncMetadataDatabase.");
+    return;
+  }
+  change_processor()->ModelReadyToSync(std::move(batch));
+}
+
+void TypedURLSyncBridge::ClearErrorStats() {
+  num_db_accesses_ = 0;
+  num_db_errors_ = 0;
+}
+
+bool TypedURLSyncBridge::FixupURLAndGetVisits(URLRow* url,
+                                              VisitVector* visits) {
+  ++num_db_accesses_;
+  if (!history_backend_->GetMostRecentVisitsForURL(url->id(), kMaxVisitsToFetch,
+                                                   visits)) {
+    ++num_db_errors_;
+    // Couldn't load the visits for this URL due to some kind of DB error.
+    // Don't bother writing this URL to the history DB (if we ignore the
+    // error and continue, we might end up duplicating existing visits).
+    DLOG(ERROR) << "Could not load visits for url: " << url->url();
+    return false;
+  }
+
+  // Sometimes (due to a bug elsewhere in the history or sync code, or due to
+  // a crash between adding a URL to the history database and updating the
+  // visit DB) the visit vector for a URL can be empty. If this happens, just
+  // create a new visit whose timestamp is the same as the last_visit time.
+  // This is a workaround for http://crbug.com/84258.
+  if (visits->empty()) {
+    DVLOG(1) << "Found empty visits for URL: " << url->url();
+    if (url->last_visit().is_null()) {
+      // If modified URL is bookmarked, history backend treats it as modified
+      // even if all its visits are deleted. Return false to stop further
+      // processing because sync expects valid visit time for modified entry.
+      return false;
+    }
+
+    VisitRow visit(url->id(), url->last_visit(), 0, ui::PAGE_TRANSITION_TYPED,
+                   0);
+    visits->push_back(visit);
+  }
+
+  // GetMostRecentVisitsForURL() returns the data in the opposite order that
+  // we need it, so reverse it.
+  std::reverse(visits->begin(), visits->end());
+
+  // Sometimes, the last_visit field in the URL doesn't match the timestamp of
+  // the last visit in our visit array (they come from different tables, so
+  // crashes/bugs can cause them to mismatch), so just set it here.
+  url->set_last_visit(visits->back().visit_time);
+  DCHECK(CheckVisitOrdering(*visits));
+
+  // Removes all visits that are older than the current expiration time. Visits
+  // are in ascending order now, so we can check from beginning to check how
+  // many expired visits.
+  size_t num_expired_visits = 0;
+  for (auto& visit : *visits) {
+    base::Time time = visit.visit_time;
+    if (history_backend_->IsExpiredVisitTime(time)) {
+      ++num_expired_visits;
+    } else {
+      break;
+    }
+  }
+  if (num_expired_visits != 0) {
+    if (num_expired_visits == visits->size()) {
+      DVLOG(1) << "All visits are expired for url: " << url->url();
+      visits->clear();
+      return false;
+    }
+    visits->erase(visits->begin(), visits->begin() + num_expired_visits);
+  }
+  DCHECK(CheckVisitOrdering(*visits));
+
+  return true;
+}
+
+std::unique_ptr<EntityData> TypedURLSyncBridge::CreateEntityData(
+    const URLRow& row,
+    const VisitVector& visits) {
+  auto entity_data = base::MakeUnique<EntityData>();
+  TypedUrlSpecifics* specifics = entity_data->specifics.mutable_typed_url();
+
+  if (!WriteToTypedUrlSpecifics(row, visits, specifics)) {
+    // Cannot write to specifics, ex. no TYPED visits.
+    return base::MakeUnique<EntityData>();
+  }
+  entity_data->non_unique_name = row.url().spec();
+
+  return entity_data;
+}
+
 }  // namespace history
diff --git a/components/history/core/browser/typed_url_sync_bridge.h b/components/history/core/browser/typed_url_sync_bridge.h
index 59eb075..281e673a 100644
--- a/components/history/core/browser/typed_url_sync_bridge.h
+++ b/components/history/core/browser/typed_url_sync_bridge.h
@@ -5,15 +5,13 @@
 #ifndef COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_SYNC_BRIDGE_H_
 #define COMPONENTS_HISTORY_CORE_BROWSER_TYPED_URL_SYNC_BRIDGE_H_
 
+#include "base/scoped_observer.h"
 #include "components/history/core/browser/history_backend_observer.h"
+#include "components/history/core/browser/typed_url_sync_metadata_database.h"
 #include "components/sync/model/metadata_change_list.h"
 #include "components/sync/model/model_type_sync_bridge.h"
 #include "components/sync/model/sync_error.h"
 
-namespace syncer {
-class SyncMetadataStore;
-}
-
 namespace history {
 
 class TypedURLSyncBridge : public syncer::ModelTypeSyncBridge,
@@ -22,7 +20,7 @@
   // |sync_metadata_store| is owned by |history_backend|, and must outlive
   // TypedURLSyncBridge.
   TypedURLSyncBridge(HistoryBackend* history_backend,
-                     syncer::SyncMetadataStore* sync_metadata_store,
+                     TypedURLSyncMetadataDatabase* sync_metadata_store,
                      const ChangeProcessorFactory& change_processor_factory);
   ~TypedURLSyncBridge() override;
 
@@ -54,19 +52,69 @@
                      const history::URLRows& deleted_rows,
                      const std::set<GURL>& favicon_urls) override;
 
+  // Must be called after creation and before any operations.
+  void Init();
+
+  // Returns the percentage of DB accesses that have resulted in an error.
+  int GetErrorPercentage() const;
+
+  // Return true if this function successfully converts the passed URL
+  // information to a TypedUrlSpecifics structure for writing to the sync DB.
+  static bool WriteToTypedUrlSpecifics(const URLRow& url,
+                                       const VisitVector& visits,
+                                       sync_pb::TypedUrlSpecifics* specifics)
+      WARN_UNUSED_RESULT;
+
  private:
+  friend class TypedURLSyncBridgeTest;
+
+  // Synchronously load sync metadata from the TypedURLSyncMetadataDatabase and
+  // pass it to the processor so that it can start tracking changes.
+  void LoadMetadata();
+
+  // Helper function that clears our error counters (used to reset stats after
+  // merge so we can track merge errors separately).
+  void ClearErrorStats();
+
+  // Fetches visits from the history DB corresponding to the passed URL. This
+  // function compensates for the fact that the history DB has rather poor data
+  // integrity (duplicate visits, visit timestamps that don't match the
+  // last_visit timestamp, huge data sets that exhaust memory when fetched,
+  // expired visits that are not deleted by |ExpireHistoryBackend|, etc) by
+  // modifying the passed |url| object and |visits| vector. The order of
+  // |visits| will be from the oldest to the newest order.
+  // Returns false in two cases.
+  // 1. we could not fetch the visits for the passed URL, DB error.
+  // 2. No visits for the passed url, or all the visits are expired.
+  bool FixupURLAndGetVisits(URLRow* url, VisitVector* visits);
+
+  // Create an EntityData by URL |row| and its visits |visits|.
+  std::unique_ptr<syncer::EntityData> CreateEntityData(
+      const URLRow& row,
+      const VisitVector& visits);
+
   // A non-owning pointer to the backend, which we're syncing local changes from
   // and sync changes to.
   HistoryBackend* const history_backend_;
 
   // A non-owning pointer to the database, which is for storing typed urls sync
   // metadata and state.
-  syncer::SyncMetadataStore* const sync_metadata_store_;
+  TypedURLSyncMetadataDatabase* const sync_metadata_database_;
+
+  // Statistics for the purposes of tracking the percentage of DB accesses that
+  // fail for each client via UMA.
+  int num_db_accesses_;
+  int num_db_errors_;
 
   // Since HistoryBackend use SequencedTaskRunner, so should use SequenceChecker
   // here.
   base::SequenceChecker sequence_checker_;
 
+  // Tracks observed history backend, for receiving updates from history
+  // backend.
+  ScopedObserver<history::HistoryBackend, history::HistoryBackendObserver>
+      history_backend_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(TypedURLSyncBridge);
 };
 
diff --git a/components/history/core/browser/typed_url_sync_bridge_unittest.cc b/components/history/core/browser/typed_url_sync_bridge_unittest.cc
new file mode 100644
index 0000000..2453270
--- /dev/null
+++ b/components/history/core/browser/typed_url_sync_bridge_unittest.cc
@@ -0,0 +1,260 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/history/core/browser/typed_url_sync_bridge.h"
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/history/core/browser/history_backend.h"
+#include "components/history/core/browser/history_backend_client.h"
+#include "components/history/core/browser/history_database_params.h"
+#include "components/history/core/browser/in_memory_history_backend.h"
+#include "components/history/core/test/test_history_database.h"
+#include "components/sync/model/data_batch.h"
+#include "components/sync/model/recording_model_type_change_processor.h"
+#include "components/sync/model/sync_metadata_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sync_pb::TypedUrlSpecifics;
+using syncer::DataBatch;
+using syncer::EntityData;
+using syncer::EntityDataPtr;
+using syncer::KeyAndData;
+using syncer::RecordingModelTypeChangeProcessor;
+
+namespace history {
+
+namespace {
+
+// Visits with this timestamp are treated as expired.
+const int kExpiredVisit = -1;
+
+// Helper constants for tests.
+const char kTitle[] = "pie";
+const char kTitle2[] = "cookie";
+const char kURL[] = "http://pie.com/";
+const char kURL2[] = "http://cookie.com/";
+
+// Create a new row object and the typed visit çorresponding with the time at
+// |last_visit| in the |visits| vector.
+URLRow MakeTypedUrlRow(const std::string& url,
+                       const std::string& title,
+                       int typed_count,
+                       int64_t last_visit,
+                       bool hidden,
+                       VisitVector* visits) {
+  // Give each URL a unique ID, to mimic the behavior of the real database.
+  GURL gurl(url);
+  URLRow history_url(gurl);
+  history_url.set_title(base::UTF8ToUTF16(title));
+  history_url.set_typed_count(typed_count);
+  history_url.set_hidden(hidden);
+
+  base::Time last_visit_time = base::Time::FromInternalValue(last_visit);
+  history_url.set_last_visit(last_visit_time);
+
+  if (typed_count > 0) {
+    // Add a typed visit for time |last_visit|.
+    visits->push_back(VisitRow(history_url.id(), last_visit_time, 0,
+                               ui::PAGE_TRANSITION_TYPED, 0));
+  } else {
+    // Add a non-typed visit for time |last_visit|.
+    visits->push_back(VisitRow(history_url.id(), last_visit_time, 0,
+                               ui::PAGE_TRANSITION_RELOAD, 0));
+  }
+
+  history_url.set_visit_count(visits->size());
+  return history_url;
+}
+
+void VerifyEqual(const TypedUrlSpecifics& s1, const TypedUrlSpecifics& s2) {
+  // Instead of just comparing serialized strings, manually check fields to show
+  // differences on failure.
+  EXPECT_EQ(s1.url(), s2.url());
+  EXPECT_EQ(s1.title(), s2.title());
+  EXPECT_EQ(s1.hidden(), s2.hidden());
+  EXPECT_EQ(s1.visits_size(), s2.visits_size());
+  EXPECT_EQ(s1.visit_transitions_size(), s2.visit_transitions_size());
+  EXPECT_EQ(s1.visits_size(), s1.visit_transitions_size());
+  int size = s1.visits_size();
+  for (int i = 0; i < size; ++i) {
+    EXPECT_EQ(s1.visits(i), s2.visits(i)) << "visits differ at index " << i;
+    EXPECT_EQ(s1.visit_transitions(i), s2.visit_transitions(i))
+        << "visit_transitions differ at index " << i;
+  }
+}
+
+void VerifyDataBatch(std::map<std::string, TypedUrlSpecifics> expected,
+                     std::unique_ptr<DataBatch> batch) {
+  while (batch->HasNext()) {
+    const KeyAndData& pair = batch->Next();
+    auto iter = expected.find(pair.first);
+    ASSERT_NE(iter, expected.end());
+    VerifyEqual(iter->second, pair.second->specifics.typed_url());
+    // Removing allows us to verify we don't see the same item multiple times,
+    // and that we saw everything we expected.
+    expected.erase(iter);
+  }
+  EXPECT_TRUE(expected.empty());
+}
+
+class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
+ public:
+  TestHistoryBackendDelegate() {}
+
+  void NotifyProfileError(sql::InitStatus init_status,
+                          const std::string& diagnostics) override {}
+  void SetInMemoryBackend(
+      std::unique_ptr<InMemoryHistoryBackend> backend) override{};
+  void NotifyFaviconsChanged(const std::set<GURL>& page_urls,
+                             const GURL& icon_url) override{};
+  void NotifyURLVisited(ui::PageTransition transition,
+                        const URLRow& row,
+                        const RedirectList& redirects,
+                        base::Time visit_time) override{};
+  void NotifyURLsModified(const URLRows& changed_urls) override{};
+  void NotifyURLsDeleted(bool all_history,
+                         bool expired,
+                         const URLRows& deleted_rows,
+                         const std::set<GURL>& favicon_urls) override{};
+  void NotifyKeywordSearchTermUpdated(const URLRow& row,
+                                      KeywordID keyword_id,
+                                      const base::string16& term) override{};
+  void NotifyKeywordSearchTermDeleted(URLID url_id) override{};
+  void DBLoaded() override{};
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestHistoryBackendDelegate);
+};
+
+class TestHistoryBackend : public HistoryBackend {
+ public:
+  TestHistoryBackend()
+      : HistoryBackend(new TestHistoryBackendDelegate(),
+                       nullptr,
+                       base::ThreadTaskRunnerHandle::Get()) {}
+
+  bool IsExpiredVisitTime(const base::Time& time) override {
+    return time.ToInternalValue() == kExpiredVisit;
+  }
+
+  URLID GetIdByUrl(const GURL& gurl) {
+    return db()->GetRowForURL(gurl, nullptr);
+  }
+
+  void SetVisitsForUrl(URLRow& new_url, const VisitVector visits) {
+    std::vector<history::VisitInfo> added_visits;
+    URLRows new_urls;
+    DeleteURL(new_url.url());
+    for (const auto& visit : visits) {
+      added_visits.push_back(
+          history::VisitInfo(visit.visit_time, visit.transition));
+    }
+    new_urls.push_back(new_url);
+    AddPagesWithDetails(new_urls, history::SOURCE_SYNCED);
+    AddVisits(new_url.url(), added_visits, history::SOURCE_SYNCED);
+    new_url.set_id(GetIdByUrl(new_url.url()));
+  }
+
+ private:
+  ~TestHistoryBackend() override {}
+};
+
+}  // namespace
+
+class TypedURLSyncBridgeTest : public testing::Test {
+ public:
+  TypedURLSyncBridgeTest() : typed_url_sync_bridge_(NULL) {}
+  ~TypedURLSyncBridgeTest() override {}
+
+  void SetUp() override {
+    fake_history_backend_ = new TestHistoryBackend();
+    ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
+    fake_history_backend_->Init(
+        false, TestHistoryDatabaseParamsForPath(test_dir_.GetPath()));
+    std::unique_ptr<TypedURLSyncBridge> bridge =
+        base::MakeUnique<TypedURLSyncBridge>(
+            fake_history_backend_.get(), fake_history_backend_->db(),
+            RecordingModelTypeChangeProcessor::FactoryForBridgeTest(&processor_,
+                                                                    false));
+    typed_url_sync_bridge_ = bridge.get();
+    typed_url_sync_bridge_->Init();
+    fake_history_backend_->SetTypedURLSyncBridgeForTest(std::move(bridge));
+  }
+
+  void TearDown() override { fake_history_backend_->Closing(); }
+
+  // Fills |specifics| with the sync data for |url| and |visits|.
+  static bool WriteToTypedUrlSpecifics(const URLRow& url,
+                                       const VisitVector& visits,
+                                       sync_pb::TypedUrlSpecifics* specifics) {
+    return TypedURLSyncBridge::WriteToTypedUrlSpecifics(url, visits, specifics);
+  }
+
+  std::string GetStorageKey(const TypedUrlSpecifics& specifics) {
+    std::string key =
+        bridge()->GetStorageKey(SpecificsToEntity(specifics).value());
+    EXPECT_FALSE(key.empty());
+    return key;
+  }
+
+  EntityDataPtr SpecificsToEntity(const TypedUrlSpecifics& specifics) {
+    EntityData data;
+    data.client_tag_hash = "ignored";
+    *data.specifics.mutable_typed_url() = specifics;
+    return data.PassToPtr();
+  }
+
+  std::map<std::string, TypedUrlSpecifics> ExpectedMap(
+      const std::vector<TypedUrlSpecifics>& specifics_vector) {
+    std::map<std::string, TypedUrlSpecifics> map;
+    for (const auto& specifics : specifics_vector) {
+      map[GetStorageKey(specifics)] = specifics;
+    }
+    return map;
+  }
+
+  void VerifyLocalHistoryData(const std::vector<TypedUrlSpecifics>& expected) {
+    bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
+  }
+
+  TypedURLSyncBridge* bridge() { return typed_url_sync_bridge_; }
+
+  TypedURLSyncMetadataDatabase* metadata_store() {
+    return bridge()->sync_metadata_database_;
+  }
+
+  const RecordingModelTypeChangeProcessor& processor() { return *processor_; }
+
+ protected:
+  base::MessageLoop message_loop_;
+  base::ScopedTempDir test_dir_;
+  scoped_refptr<TestHistoryBackend> fake_history_backend_;
+  TypedURLSyncBridge* typed_url_sync_bridge_;
+  // A non-owning pointer to the processor given to the bridge. Will be null
+  // before being given to the bridge, to make ownership easier.
+  RecordingModelTypeChangeProcessor* processor_ = nullptr;
+};
+
+// Add two typed urls locally and verify bridge can get them from GetAllData.
+TEST_F(TypedURLSyncBridgeTest, GetAllData) {
+  // Add two urls to backend.
+  VisitVector visits1, visits2;
+  URLRow row1 = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits1);
+  URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, 2, 4, false, &visits2);
+  fake_history_backend_->SetVisitsForUrl(row1, visits1);
+  fake_history_backend_->SetVisitsForUrl(row2, visits2);
+
+  // Create the same data in sync.
+  sync_pb::TypedUrlSpecifics typed_url1, typed_url2;
+  WriteToTypedUrlSpecifics(row1, visits1, &typed_url1);
+  WriteToTypedUrlSpecifics(row2, visits2, &typed_url2);
+
+  // Check that the local cache is still correct.
+  VerifyLocalHistoryData({typed_url1, typed_url2});
+}
+
+}  // namespace history
diff --git a/components/history/core/browser/typed_url_sync_metadata_database.cc b/components/history/core/browser/typed_url_sync_metadata_database.cc
index f0b2ce6..81e9da0 100644
--- a/components/history/core/browser/typed_url_sync_metadata_database.cc
+++ b/components/history/core/browser/typed_url_sync_metadata_database.cc
@@ -4,8 +4,9 @@
 
 #include "components/history/core/browser/typed_url_sync_metadata_database.h"
 
+#include "base/big_endian.h"
 #include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
+#include "components/history/core/browser/url_row.h"
 #include "sql/statement.h"
 
 namespace history {
@@ -55,9 +56,11 @@
       << "Only the TYPED_URLS model type is supported";
 
   int64_t storage_key_int = 0;
-  if (!base::StringToInt64(storage_key, &storage_key_int)) {
-    return false;
-  }
+  DCHECK_EQ(storage_key.size(), sizeof(storage_key_int));
+  base::ReadBigEndian(storage_key.data(), &storage_key_int);
+  // Make sure storage_key_int is set.
+  DCHECK_NE(storage_key_int, 0);
+
   sql::Statement s(GetDB().GetUniqueStatement(
       "INSERT OR REPLACE INTO typed_url_sync_metadata "
       "(storage_key, value) VALUES(?, ?)"));
@@ -74,9 +77,11 @@
       << "Only the TYPED_URLS model type is supported";
 
   int64_t storage_key_int = 0;
-  if (!base::StringToInt64(storage_key, &storage_key_int)) {
-    return false;
-  }
+  DCHECK_EQ(storage_key.size(), sizeof(storage_key_int));
+  base::ReadBigEndian(storage_key.data(), &storage_key_int);
+  // Make sure storage_key_int is set.
+  DCHECK_NE(storage_key_int, 0);
+
   sql::Statement s(GetDB().GetUniqueStatement(
       "DELETE FROM typed_url_sync_metadata WHERE storage_key=?"));
   s.BindInt64(0, storage_key_int);
@@ -122,7 +127,8 @@
       "SELECT storage_key, value FROM typed_url_sync_metadata"));
 
   while (s.Step()) {
-    std::string storage_key = base::Int64ToString(s.ColumnInt64(0));
+    std::string storage_key(sizeof(URLID), 0);
+    base::WriteBigEndian<URLID>(&storage_key[0], s.ColumnInt64(0));
     std::string serialized_metadata = s.ColumnString(1);
     sync_pb::EntityMetadata entity_metadata;
     if (entity_metadata.ParseFromString(serialized_metadata)) {
diff --git a/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc b/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc
index c37bf3ee..ec0f22f 100644
--- a/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc
+++ b/components/history/core/browser/typed_url_sync_metadata_database_unittest.cc
@@ -4,8 +4,10 @@
 
 #include "components/history/core/browser/typed_url_sync_metadata_database.h"
 
+#include "base/big_endian.h"
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
+#include "components/history/core/browser/url_row.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 #include "sql/statement.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -17,6 +19,16 @@
 
 namespace history {
 
+namespace {
+
+std::string IntToStorageKey(int i) {
+  std::string storage_key(sizeof(URLID), 0);
+  base::WriteBigEndian<URLID>(&storage_key[0], i);
+  return storage_key;
+}
+
+}  // namespace
+
 class TypedURLSyncMetadataDatabaseTest : public testing::Test,
                                          public TypedURLSyncMetadataDatabase {
  public:
@@ -60,8 +72,8 @@
 
 TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLGetAllSyncMetadata) {
   EntityMetadata metadata;
-  std::string storage_key = "1";
-  std::string storage_key2 = "2";
+  std::string storage_key = IntToStorageKey(1);
+  std::string storage_key2 = IntToStorageKey(2);
   metadata.set_sequence_number(1);
 
   EXPECT_TRUE(UpdateSyncMetadata(syncer::TYPED_URLS, storage_key, metadata));
@@ -96,7 +108,7 @@
 TEST_F(TypedURLSyncMetadataDatabaseTest, TypedURLWriteThenDeleteSyncMetadata) {
   EntityMetadata metadata;
   MetadataBatch metadata_batch;
-  std::string storage_key = "1";
+  std::string storage_key = IntToStorageKey(1);
   ModelTypeState model_type_state;
 
   model_type_state.set_initial_sync_done(true);
diff --git a/components/safe_browsing_db/v4_database_unittest.cc b/components/safe_browsing_db/v4_database_unittest.cc
index de42e1a3..66db6fa5 100644
--- a/components/safe_browsing_db/v4_database_unittest.cc
+++ b/components/safe_browsing_db/v4_database_unittest.cc
@@ -24,7 +24,7 @@
               const bool hash_prefix_matches)
       : V4Store(
             task_runner,
-            base::FilePath(store_path.value() + FILE_PATH_LITERAL(".store"))),
+            base::FilePath(store_path.value() + FILE_PATH_LITERAL(".fake"))),
         hash_prefix_should_match_(hash_prefix_matches) {}
 
   HashPrefix GetMatchingHashPrefix(const FullHash& full_hash) override {
@@ -103,13 +103,13 @@
                              SB_THREAT_TYPE_URL_MALWARE);
     expected_identifiers_.push_back(win_malware_id_);
     expected_store_paths_.push_back(
-        database_dirname_.AppendASCII("win_url_malware.store"));
+        database_dirname_.AppendASCII("win_url_malware.fake"));
 
     list_infos_.emplace_back(true, "linux_url_malware", linux_malware_id_,
                              SB_THREAT_TYPE_URL_MALWARE);
     expected_identifiers_.push_back(linux_malware_id_);
     expected_store_paths_.push_back(
-        database_dirname_.AppendASCII("linux_url_malware.store"));
+        database_dirname_.AppendASCII("linux_url_malware.fake"));
   }
 
   void DatabaseUpdated() {}
diff --git a/components/safe_browsing_db/v4_local_database_manager.cc b/components/safe_browsing_db/v4_local_database_manager.cc
index fbbc1b94..c17dbde9 100644
--- a/components/safe_browsing_db/v4_local_database_manager.cc
+++ b/components/safe_browsing_db/v4_local_database_manager.cc
@@ -11,12 +11,10 @@
 
 #include "base/bind_helpers.h"
 #include "base/callback.h"
-#include "base/files/file_util.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/task_scheduler/post_task.h"
+#include "base/threading/sequenced_worker_pool.h"
 #include "components/safe_browsing_db/v4_feature_list.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
 #include "content/public/browser/browser_thread.h"
@@ -32,14 +30,11 @@
 const ThreatSeverity kLeastSeverity =
     std::numeric_limits<ThreatSeverity>::max();
 
-// The list of the name of any store files that are no longer used and can be
-// safely deleted from the disk. There's no overlap allowed between the files
-// on this list and the list returned by GetListInfos().
-const char* const kStoreFileNamesToDelete[] = {"AnyIpMalware.store"};
-
 ListInfos GetListInfos() {
   // NOTE(vakh): When adding a store here, add the corresponding store-specific
   // histograms also.
+  // NOTE(vakh): Delete file "AnyIpMalware.store". It has been renamed to
+  // "IpMalware.store". If it exists, it should be 75 bytes long.
   // The first argument to ListInfo specifies whether to sync hash prefixes for
   // that list. This can be false for two reasons:
   // - The server doesn't support that list yet. Once the server adds support
@@ -161,14 +156,10 @@
     : base_path_(base_path),
       extended_reporting_level_callback_(extended_reporting_level_callback),
       list_infos_(GetListInfos()),
-      task_runner_(base::CreateSequencedTaskRunnerWithTraits(
-          {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
       weak_factory_(this) {
   DCHECK(!base_path_.empty());
   DCHECK(!list_infos_.empty());
 
-  DeleteUnusedStoreFiles();
-
   DVLOG(1) << "V4LocalDatabaseManager::V4LocalDatabaseManager: "
            << "base_path_: " << base_path_.AsUTF8Unsafe();
 }
@@ -505,32 +496,6 @@
   }
 }
 
-void V4LocalDatabaseManager::DeleteUnusedStoreFiles() {
-  for (auto* const store_filename_to_delete : kStoreFileNamesToDelete) {
-    // Is the file marked for deletion also being used for a valid V4Store?
-    auto it = std::find_if(std::begin(list_infos_), std::end(list_infos_),
-                           [&store_filename_to_delete](ListInfo const& li) {
-                             return li.filename() == store_filename_to_delete;
-                           });
-    if (list_infos_.end() == it) {
-      const auto& store_path = base_path_.AppendASCII(store_filename_to_delete);
-      bool path_exists = base::PathExists(store_path);
-      base::UmaHistogramBoolean("SafeBrowsing.V4UnusedStoreFileExists" +
-                                    GetUmaSuffixForStore(store_path),
-                                path_exists);
-      if (!path_exists) {
-        continue;
-      }
-      task_runner_->PostTask(FROM_HERE,
-                             base::Bind(base::IgnoreResult(&base::DeleteFile),
-                                        store_path, false /* recursive */));
-    } else {
-      NOTREACHED() << "Trying to delete a store file that's in use: "
-                   << store_filename_to_delete;
-    }
-  }
-}
-
 bool V4LocalDatabaseManager::GetPrefixMatches(
     const std::unique_ptr<PendingCheck>& check,
     FullHashToStoreAndHashPrefixesMap* full_hash_to_store_and_hash_prefixes) {
@@ -824,6 +789,14 @@
   DCHECK(!list_infos_.empty());
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
+  // Only get a new task runner if there isn't one already. If the service has
+  // previously been started and stopped, a task runner could already exist.
+  if (!task_runner_) {
+    base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
+    task_runner_ = pool->GetSequencedTaskRunnerWithShutdownBehavior(
+        pool->GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
+  }
+
   // Do not create the database on the IO thread since this may be an expensive
   // operation. Instead, do that on the task_runner and when the new database
   // has been created, swap it out on the IO thread.
diff --git a/components/safe_browsing_db/v4_local_database_manager.h b/components/safe_browsing_db/v4_local_database_manager.h
index b82d9a8..0c154325 100644
--- a/components/safe_browsing_db/v4_local_database_manager.h
+++ b/components/safe_browsing_db/v4_local_database_manager.h
@@ -198,9 +198,6 @@
   // Called when the database has been updated and schedules the next update.
   void DatabaseUpdated();
 
-  // Delete any *.store files from disk that are no longer used.
-  void DeleteUnusedStoreFiles();
-
   // Identifies the prefixes and the store they matched in, for a given |check|.
   // Returns true if one or more hash prefix matches are found; false otherwise.
   bool GetPrefixMatches(
diff --git a/components/safe_browsing_db/v4_local_database_manager_unittest.cc b/components/safe_browsing_db/v4_local_database_manager_unittest.cc
index 9d2e633..5e20cb7 100644
--- a/components/safe_browsing_db/v4_local_database_manager_unittest.cc
+++ b/components/safe_browsing_db/v4_local_database_manager_unittest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "components/safe_browsing_db/v4_local_database_manager.h"
-#include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
@@ -1102,45 +1101,4 @@
   EXPECT_TRUE(client.on_check_download_urls_result_called_);
 }
 
-TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileDoesNotExist) {
-  auto store_file_path = base_dir_.GetPath().AppendASCII("AnyIpMalware.store");
-  ASSERT_FALSE(base::PathExists(store_file_path));
-
-  // Reset the database manager so that DeleteUnusedStoreFiles is called.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-  ASSERT_FALSE(base::PathExists(store_file_path));
-}
-
-TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileSuccess) {
-  auto store_file_path = base_dir_.GetPath().AppendASCII("AnyIpMalware.store");
-  ASSERT_FALSE(base::PathExists(store_file_path));
-
-  // Now write an empty file.
-  base::WriteFile(store_file_path, "", 0);
-  ASSERT_TRUE(base::PathExists(store_file_path));
-
-  // Reset the database manager so that DeleteUnusedStoreFiles is called.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-  ASSERT_FALSE(base::PathExists(store_file_path));
-}
-
-TEST_F(V4LocalDatabaseManagerTest, DeleteUnusedStoreFileRandomFileNotDeleted) {
-  auto random_store_file_path = base_dir_.GetPath().AppendASCII("random.store");
-  ASSERT_FALSE(base::PathExists(random_store_file_path));
-
-  // Now write an empty file.
-  base::WriteFile(random_store_file_path, "", 0);
-  ASSERT_TRUE(base::PathExists(random_store_file_path));
-
-  // Reset the database manager so that DeleteUnusedStoreFiles is called.
-  ResetLocalDatabaseManager();
-  WaitForTasksOnTaskRunner();
-  ASSERT_TRUE(base::PathExists(random_store_file_path));
-
-  // Cleanup
-  base::DeleteFile(random_store_file_path, false /* recursive */);
-}
-
 }  // namespace safe_browsing
diff --git a/components/safe_browsing_db/v4_protocol_manager_util.cc b/components/safe_browsing_db/v4_protocol_manager_util.cc
index 022374a..44aeee98 100644
--- a/components/safe_browsing_db/v4_protocol_manager_util.cc
+++ b/components/safe_browsing_db/v4_protocol_manager_util.cc
@@ -21,10 +21,6 @@
 using base::TimeDelta;
 
 namespace safe_browsing {
-const base::FilePath::CharType kStoreSuffix[] = FILE_PATH_LITERAL(".store");
-
-// The Safe Browsing V4 server URL prefix.
-const char kSbV4UrlPrefix[] = "https://safebrowsing.googleapis.com/v4";
 
 namespace {
 
@@ -145,11 +141,8 @@
   return ListIdentifier(GetCurrentPlatformType(), URL, UNWANTED_SOFTWARE);
 }
 
-std::string GetUmaSuffixForStore(const base::FilePath& file_path) {
-  DCHECK_EQ(kStoreSuffix, file_path.BaseName().Extension());
-  return base::StringPrintf(
-      ".%" PRIsFP, file_path.BaseName().RemoveExtension().value().c_str());
-}
+// The Safe Browsing V4 server URL prefix.
+const char kSbV4UrlPrefix[] = "https://safebrowsing.googleapis.com/v4";
 
 StoreAndHashPrefix::StoreAndHashPrefix(ListIdentifier list_id,
                                        const HashPrefix& hash_prefix)
diff --git a/components/safe_browsing_db/v4_protocol_manager_util.h b/components/safe_browsing_db/v4_protocol_manager_util.h
index 434b94b..54b5225 100644
--- a/components/safe_browsing_db/v4_protocol_manager_util.h
+++ b/components/safe_browsing_db/v4_protocol_manager_util.h
@@ -169,9 +169,6 @@
 ListIdentifier GetUrlSubresourceFilterId();
 ListIdentifier GetUrlUwsId();
 
-// Returns the basename of the store file, without the ".store" extension.
-std::string GetUmaSuffixForStore(const base::FilePath& file_path);
-
 // Represents the state of each store.
 using StoreStateMap = std::unordered_map<ListIdentifier, std::string>;
 
diff --git a/components/safe_browsing_db/v4_store.cc b/components/safe_browsing_db/v4_store.cc
index 5871922..fe3220a1 100644
--- a/components/safe_browsing_db/v4_store.cc
+++ b/components/safe_browsing_db/v4_store.cc
@@ -46,6 +46,11 @@
 const uint32_t kFileMagic = 0x600D71FE;
 const uint32_t kFileVersion = 9;
 
+std::string GetUmaSuffixForStore(const base::FilePath& file_path) {
+  return base::StringPrintf(
+      ".%" PRIsFP, file_path.BaseName().RemoveExtension().value().c_str());
+}
+
 void RecordTimeWithAndWithoutSuffix(const std::string& metric,
                                     base::TimeDelta time,
                                     const base::FilePath& file_path) {
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2.css b/components/security_interstitials/core/browser/resources/interstitial_v2.css
index 5af309a..fdda65d 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_v2.css
+++ b/components/security_interstitials/core/browser/resources/interstitial_v2.css
@@ -65,7 +65,7 @@
 
 #details {
   color: #696969;
-  margin: 45px 0 50px;
+  margin: 0 0 50px;
 }
 
 #details p:not(:first-of-type) {
@@ -315,7 +315,7 @@
 @media (min-width: 240px) and (max-width: 420px) and
        (min-height: 401px),
        (min-width: 421px) and (min-height: 240px) and
-       (max-height: 736px) {
+       (max-height: 560px) {
   body .nav-wrapper {
     background: #f7f7f7;
     bottom: 0;
@@ -348,7 +348,7 @@
 }
 
 @media (max-width: 420px) and (orientation: portrait),
-       (max-height: 736px) {
+       (max-height: 560px) {
   body {
     margin: 0 auto;
   }
@@ -435,7 +435,7 @@
   }
 }
 
-@media (min-width: 421px) and (min-height: 500px) and (max-height: 736px) {
+@media (min-width: 421px) and (min-height: 500px) and (max-height: 560px) {
   .interstitial-wrapper {
     margin-top: 10vh;
   }
diff --git a/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js b/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js
index 8980099..655d91f 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js
+++ b/components/security_interstitials/core/browser/resources/interstitial_v2_mobile.js
@@ -13,7 +13,7 @@
   var mainContent = document.querySelector('#main-content');
   var mediaQuery = '(min-width: 240px) and (max-width: 420px) and ' +
       '(min-height: 401px), ' +
-      '(max-height: 736px) and (min-height: 240px) and ' +
+      '(max-height: 560px) and (min-height: 240px) and ' +
       '(min-width: 421px)';
 
   var detailsHidden = helpOuterBox.classList.contains('hidden');
diff --git a/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css b/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css
index 80a87648c..12719e8 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css
+++ b/components/security_interstitials/core/browser/resources/interstitial_webview_quiet.css
@@ -34,7 +34,9 @@
 
 h1 {
   color: rgba(0,0,0,.38);
+  font-family: Roboto-Regular;
   font-size: 1.037037em;
+  font-weight: normal;
   line-height: 1.4em;
   margin: 8px 0 8px;
 }
@@ -55,10 +57,11 @@
 
 .icon {
   background-image: url(images/blocked.svg);
+  background-position: center;
   height: 20vh;
   margin: 0 auto;
-  max-height: 36px;
-  max-width: 36px;
+  max-height: 24px;
+  max-width: 24px;
   min-height: 18px;
   min-width: 18px;
   opacity: .54;
@@ -96,8 +99,8 @@
 @media (min-height:25em) and (min-width:37.5em),
        (min-height:37.5em) and (min-width:25em) {
   .icon {
-    height: 36px;
-    width: 36px;
+    max-height: 36px;
+    max-width: 36px;
   }
 }
 
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn
index dd59a01..cfd824e 100644
--- a/components/subresource_filter/content/browser/BUILD.gn
+++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -59,6 +59,8 @@
     "async_document_subresource_filter_test_utils.h",
     "fake_safe_browsing_database_manager.cc",
     "fake_safe_browsing_database_manager.h",
+    "subresource_filter_observer_test_utils.cc",
+    "subresource_filter_observer_test_utils.h",
   ]
   deps = [
     ":browser",
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter.h b/components/subresource_filter/content/browser/async_document_subresource_filter.h
index b96e827..92c80ac 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter.h
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter.h
@@ -16,6 +16,7 @@
 #include "components/subresource_filter/core/common/activation_level.h"
 #include "components/subresource_filter/core/common/activation_state.h"
 #include "components/subresource_filter/core/common/document_subresource_filter.h"
+#include "components/subresource_filter/core/common/load_policy.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
diff --git a/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc b/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
index 88a0862..424f872 100644
--- a/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
+++ b/components/subresource_filter/content/browser/async_document_subresource_filter_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter_test_utils.h"
+#include "components/subresource_filter/core/common/load_policy.h"
 #include "components/subresource_filter/core/common/proto/rules.pb.h"
 #include "components/subresource_filter/core/common/test_ruleset_creator.h"
 #include "components/subresource_filter/core/common/test_ruleset_utils.h"
diff --git a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
index 8254f53..877a4d3 100644
--- a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
+++ b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "components/subresource_filter/core/common/time_measurements.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/common/browser_side_navigation_policy.h"
@@ -24,16 +25,21 @@
 }
 
 SubframeNavigationFilteringThrottle::~SubframeNavigationFilteringThrottle() {
-  if (disallowed_) {
-    UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
-        "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed",
-        total_defer_time_, base::TimeDelta::FromMicroseconds(1),
-        base::TimeDelta::FromSeconds(10), 50);
-  } else {
-    UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
-        "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Allowed",
-        total_defer_time_, base::TimeDelta::FromMicroseconds(1),
-        base::TimeDelta::FromSeconds(10), 50);
+  switch (load_policy_) {
+    case LoadPolicy::ALLOW:
+      UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+          "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Allowed",
+          total_defer_time_, base::TimeDelta::FromMicroseconds(1),
+          base::TimeDelta::FromSeconds(10), 50);
+      break;
+    case LoadPolicy::WOULD_DISALLOW:
+    // fall through
+    case LoadPolicy::DISALLOW:
+      UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
+          "SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed",
+          total_defer_time_, base::TimeDelta::FromMicroseconds(1),
+          base::TimeDelta::FromSeconds(10), 50);
+      break;
   }
 }
 
@@ -47,6 +53,13 @@
   return DeferToCalculateLoadPolicy(ThrottlingStage::WillRedirectRequest);
 }
 
+content::NavigationThrottle::ThrottleCheckResult
+SubframeNavigationFilteringThrottle::WillProcessResponse() {
+  DCHECK_NE(load_policy_, LoadPolicy::DISALLOW);
+  NotifyLoadPolicy();
+  return content::NavigationThrottle::ThrottleCheckResult::PROCEED;
+}
+
 const char* SubframeNavigationFilteringThrottle::GetNameForLogging() {
   return "SubframeNavigationFilteringThrottle";
 }
@@ -54,6 +67,9 @@
 content::NavigationThrottle::ThrottleCheckResult
 SubframeNavigationFilteringThrottle::DeferToCalculateLoadPolicy(
     ThrottlingStage stage) {
+  DCHECK_NE(load_policy_, LoadPolicy::DISALLOW);
+  if (load_policy_ == LoadPolicy::WOULD_DISALLOW)
+    return content::NavigationThrottle::ThrottleCheckResult::PROCEED;
   parent_frame_filter_->GetLoadPolicyForSubdocument(
       navigation_handle()->GetURL(),
       base::Bind(&SubframeNavigationFilteringThrottle::OnCalculatedLoadPolicy,
@@ -66,12 +82,13 @@
     ThrottlingStage stage,
     LoadPolicy policy) {
   DCHECK(!last_defer_timestamp_.is_null());
+  load_policy_ = policy;
   total_defer_time_ += base::TimeTicks::Now() - last_defer_timestamp_;
-  // TODO(csharrison): Support WouldDisallow pattern and expose the policy for
-  // metrics.
+
   if (policy == LoadPolicy::DISALLOW) {
-    disallowed_ = true;
     parent_frame_filter_->ReportDisallowedLoad();
+    // Other load policies will be reported in WillProcessResponse.
+    NotifyLoadPolicy();
 
     const bool block_and_collapse_is_supported =
         content::IsBrowserSideNavigationEnabled() ||
@@ -85,4 +102,13 @@
   }
 }
 
+void SubframeNavigationFilteringThrottle::NotifyLoadPolicy() const {
+  if (auto* observer_manager =
+          SubresourceFilterObserverManager::FromWebContents(
+              navigation_handle()->GetWebContents())) {
+    observer_manager->NotifySubframeNavigationEvaluated(navigation_handle(),
+                                                        load_policy_);
+  }
+}
+
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
index e4dbfe12..74d8921 100644
--- a/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
+++ b/components/subresource_filter/content/browser/subframe_navigation_filtering_throttle.h
@@ -9,6 +9,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
+#include "components/subresource_filter/core/common/load_policy.h"
 #include "content/public/browser/navigation_throttle.h"
 
 namespace content {
@@ -38,6 +39,8 @@
   content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
   content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
       override;
+  content::NavigationThrottle::ThrottleCheckResult WillProcessResponse()
+      override;
   const char* GetNameForLogging() override;
 
  private:
@@ -47,12 +50,14 @@
       ThrottlingStage stage);
   void OnCalculatedLoadPolicy(ThrottlingStage stage, LoadPolicy policy);
 
+  void NotifyLoadPolicy() const;
+
   // Must outlive this class.
   AsyncDocumentSubresourceFilter* parent_frame_filter_;
 
   base::TimeTicks last_defer_timestamp_;
   base::TimeDelta total_defer_time_;
-  bool disallowed_ = false;
+  LoadPolicy load_policy_ = LoadPolicy::ALLOW;
 
   base::WeakPtrFactory<SubframeNavigationFilteringThrottle> weak_ptr_factory_;
 
diff --git a/components/subresource_filter/content/browser/subresource_filter_observer.h b/components/subresource_filter/content/browser/subresource_filter_observer.h
index 7daa78a6..8946095 100644
--- a/components/subresource_filter/content/browser/subresource_filter_observer.h
+++ b/components/subresource_filter/content/browser/subresource_filter_observer.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_H_
 
 #include "components/subresource_filter/core/common/activation_decision.h"
+#include "components/subresource_filter/core/common/load_policy.h"
 
 namespace content {
 class NavigationHandle;
@@ -27,6 +28,12 @@
       content::NavigationHandle* navigation_handle,
       ActivationDecision activation_decision,
       const ActivationState& activation_state) {}
+
+  // Called before navigation commit, either at the WillStartRequest stage or
+  // WillRedirectRequest stage.
+  virtual void OnSubframeNavigationEvaluated(
+      content::NavigationHandle* navigation_handle,
+      LoadPolicy load_policy) {}
 };
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc b/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc
index f3b891c..a0d02d1 100644
--- a/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_observer_manager.cc
@@ -39,4 +39,11 @@
   }
 }
 
+void SubresourceFilterObserverManager::NotifySubframeNavigationEvaluated(
+    content::NavigationHandle* navigation_handle,
+    LoadPolicy load_policy) {
+  for (auto& observer : observers_)
+    observer.OnSubframeNavigationEvaluated(navigation_handle, load_policy);
+}
+
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subresource_filter_observer_manager.h b/components/subresource_filter/content/browser/subresource_filter_observer_manager.h
index 489fb21..978b1a3b 100644
--- a/components/subresource_filter/content/browser/subresource_filter_observer_manager.h
+++ b/components/subresource_filter/content/browser/subresource_filter_observer_manager.h
@@ -9,6 +9,7 @@
 #include "base/observer_list.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer.h"
 #include "components/subresource_filter/core/common/activation_decision.h"
+#include "components/subresource_filter/core/common/load_policy.h"
 #include "content/public/browser/web_contents_user_data.h"
 
 namespace content {
@@ -39,6 +40,10 @@
       ActivationDecision activation_decision,
       const ActivationState& activation_state);
 
+  void NotifySubframeNavigationEvaluated(
+      content::NavigationHandle* navigation_handle,
+      LoadPolicy load_policy);
+
  private:
   base::ObserverList<SubresourceFilterObserver> observers_;
   DISALLOW_COPY_AND_ASSIGN(SubresourceFilterObserverManager);
diff --git a/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc b/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc
new file mode 100644
index 0000000..24cf9480
--- /dev/null
+++ b/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
+
+#include "content/public/browser/navigation_handle.h"
+
+namespace subresource_filter {
+
+TestSubresourceFilterObserver::TestSubresourceFilterObserver(
+    content::WebContents* web_contents)
+    : scoped_observer_(this) {
+  scoped_observer_.Add(
+      SubresourceFilterObserverManager::FromWebContents(web_contents));
+}
+
+TestSubresourceFilterObserver::~TestSubresourceFilterObserver() {}
+
+void TestSubresourceFilterObserver::OnSubresourceFilterGoingAway() {
+  scoped_observer_.RemoveAll();
+}
+
+void TestSubresourceFilterObserver::OnPageActivationComputed(
+    content::NavigationHandle* navigation_handle,
+    ActivationDecision activation_decision,
+    const ActivationState& activation_state) {
+  page_activations_[navigation_handle->GetURL()] = activation_decision;
+}
+
+void TestSubresourceFilterObserver::OnSubframeNavigationEvaluated(
+    content::NavigationHandle* navigation_handle,
+    LoadPolicy load_policy) {
+  subframe_load_evaluations_[navigation_handle->GetURL()] = load_policy;
+}
+
+base::Optional<ActivationDecision>
+TestSubresourceFilterObserver::GetPageActivation(const GURL& url) {
+  auto it = page_activations_.find(url);
+  if (it != page_activations_.end())
+    return it->second;
+  return base::Optional<ActivationDecision>();
+}
+
+base::Optional<LoadPolicy> TestSubresourceFilterObserver::GetSubframeLoadPolicy(
+    const GURL& url) {
+  auto it = subframe_load_evaluations_.find(url);
+  if (it != subframe_load_evaluations_.end())
+    return it->second;
+  return base::Optional<LoadPolicy>();
+}
+
+}  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h b/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
new file mode 100644
index 0000000..b76f203bd
--- /dev/null
+++ b/components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h
@@ -0,0 +1,57 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_TEST_UTILS_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_TEST_UTILS_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/scoped_observer.h"
+#include "components/subresource_filter/content/browser/subresource_filter_observer.h"
+#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
+#include "components/subresource_filter/core/common/activation_decision.h"
+#include "components/subresource_filter/core/common/load_policy.h"
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace subresource_filter {
+
+// This class can be used to observe subresource filtering events associated
+// with a particular web contents. Particular events can be expected by using
+// the Get* methods.
+class TestSubresourceFilterObserver : public SubresourceFilterObserver {
+ public:
+  TestSubresourceFilterObserver(content::WebContents* web_contents);
+  ~TestSubresourceFilterObserver() override;
+
+  // SubresourceFilterObserver:
+  void OnSubresourceFilterGoingAway() override;
+  void OnPageActivationComputed(
+      content::NavigationHandle* navigation_handle,
+      ActivationDecision activation_decision,
+      const ActivationState& activation_state) override;
+  void OnSubframeNavigationEvaluated(
+      content::NavigationHandle* navigation_handle,
+      LoadPolicy load_policy) override;
+
+  base::Optional<ActivationDecision> GetPageActivation(const GURL& url);
+  base::Optional<LoadPolicy> GetSubframeLoadPolicy(const GURL& url);
+
+ private:
+  std::map<GURL, LoadPolicy> subframe_load_evaluations_;
+  std::map<GURL, ActivationDecision> page_activations_;
+
+  ScopedObserver<SubresourceFilterObserverManager, SubresourceFilterObserver>
+      scoped_observer_;
+  DISALLOW_COPY_AND_ASSIGN(TestSubresourceFilterObserver);
+};
+
+}  // namespace subresource_filter
+
+#endif  // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_OBSERVER_TEST_UTILS_H_
diff --git a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
index de17ed65..efb514b 100644
--- a/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
+++ b/components/subresource_filter/content/renderer/web_document_subresource_filter_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "components/subresource_filter/core/common/activation_state.h"
+#include "components/subresource_filter/core/common/load_policy.h"
 #include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
diff --git a/components/subresource_filter/core/common/BUILD.gn b/components/subresource_filter/core/common/BUILD.gn
index 61964f8d..ea52296 100644
--- a/components/subresource_filter/core/common/BUILD.gn
+++ b/components/subresource_filter/core/common/BUILD.gn
@@ -25,6 +25,7 @@
     "fuzzy_pattern_matching.h",
     "indexed_ruleset.cc",
     "indexed_ruleset.h",
+    "load_policy.h",
     "memory_mapped_ruleset.cc",
     "memory_mapped_ruleset.h",
     "ngram_extractor.h",
diff --git a/components/subresource_filter/core/common/document_subresource_filter.h b/components/subresource_filter/core/common/document_subresource_filter.h
index 611707d..0a8ac037 100644
--- a/components/subresource_filter/core/common/document_subresource_filter.h
+++ b/components/subresource_filter/core/common/document_subresource_filter.h
@@ -16,6 +16,7 @@
 #include "components/subresource_filter/core/common/activation_state.h"
 #include "components/subresource_filter/core/common/document_load_statistics.h"
 #include "components/subresource_filter/core/common/indexed_ruleset.h"
+#include "components/subresource_filter/core/common/load_policy.h"
 #include "components/subresource_filter/core/common/proto/rules.pb.h"
 
 class GURL;
@@ -29,12 +30,6 @@
 class FirstPartyOrigin;
 class MemoryMappedRuleset;
 
-enum class LoadPolicy {
-  ALLOW,
-  DISALLOW,
-  WOULD_DISALLOW,
-};
-
 // Computes whether/how subresource filtering should be activated while loading
 // |document_url| in a frame, based on the parent document's |activation_state|,
 // the |parent_document_origin|, as well as any applicable deactivation rules in
diff --git a/components/subresource_filter/core/common/load_policy.h b/components/subresource_filter/core/common/load_policy.h
new file mode 100644
index 0000000..0a235a7e
--- /dev/null
+++ b/components/subresource_filter/core/common/load_policy.h
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_LOAD_POLICY_H_
+#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_LOAD_POLICY_H_
+
+namespace subresource_filter {
+
+// Represents the value returned by the DocumentSubresourceFilter corresponding
+// to a resource load.
+enum class LoadPolicy {
+  ALLOW,
+  DISALLOW,
+  WOULD_DISALLOW,
+};
+
+}  // namespace subresource_filter
+
+#endif  // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_LOAD_POLICY_H_
diff --git a/components/viz/client/BUILD.gn b/components/viz/client/BUILD.gn
index 881aac5..1e73b925 100644
--- a/components/viz/client/BUILD.gn
+++ b/components/viz/client/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "client_compositor_frame_sink.cc",
     "client_compositor_frame_sink.h",
+    "local_surface_id_provider.cc",
+    "local_surface_id_provider.h",
   ]
 
   public_deps = [
diff --git a/components/viz/client/DEPS b/components/viz/client/DEPS
index 1d0b8872..86ba24d 100644
--- a/components/viz/client/DEPS
+++ b/components/viz/client/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+cc",
+  "+ui/gfx/geometry",
 ]
diff --git a/components/viz/client/client_compositor_frame_sink.cc b/components/viz/client/client_compositor_frame_sink.cc
index e363f8c..3fe17a6 100644
--- a/components/viz/client/client_compositor_frame_sink.cc
+++ b/components/viz/client/client_compositor_frame_sink.cc
@@ -9,6 +9,7 @@
 #include "cc/output/begin_frame_args.h"
 #include "cc/output/compositor_frame.h"
 #include "cc/output/compositor_frame_sink_client.h"
+#include "components/viz/client/local_surface_id_provider.h"
 
 namespace viz {
 
@@ -20,11 +21,13 @@
     std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source,
     cc::mojom::MojoCompositorFrameSinkPtrInfo compositor_frame_sink_info,
     cc::mojom::MojoCompositorFrameSinkClientRequest client_request,
+    std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider,
     bool enable_surface_synchronization)
     : cc::CompositorFrameSink(std::move(context_provider),
                               std::move(worker_context_provider),
                               gpu_memory_buffer_manager,
                               shared_bitmap_manager),
+      local_surface_id_provider_(std::move(local_surface_id_provider)),
       synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)),
       compositor_frame_sink_info_(std::move(compositor_frame_sink_info)),
       client_request_(std::move(client_request)),
@@ -38,8 +41,10 @@
     std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source,
     cc::mojom::MojoCompositorFrameSinkPtrInfo compositor_frame_sink_info,
     cc::mojom::MojoCompositorFrameSinkClientRequest client_request,
+    std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider,
     bool enable_surface_synchronization)
     : cc::CompositorFrameSink(std::move(vulkan_context_provider)),
+      local_surface_id_provider_(std::move(local_surface_id_provider)),
       synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)),
       compositor_frame_sink_info_(std::move(compositor_frame_sink_info)),
       client_request_(std::move(client_request)),
@@ -94,13 +99,10 @@
   DCHECK_LE(cc::BeginFrameArgs::kStartingFrameNumber,
             frame.metadata.begin_frame_ack.sequence_number);
 
-  if (!enable_surface_synchronization_ &&
-      (!local_surface_id_.is_valid() || ShouldAllocateNewLocalSurfaceId(frame)))
-    local_surface_id_ = id_allocator_.GenerateId();
-
-  surface_size_ = frame.render_pass_list.back()->output_rect.size();
-  device_scale_factor_ = frame.metadata.device_scale_factor;
-
+  if (!enable_surface_synchronization_) {
+    local_surface_id_ =
+        local_surface_id_provider_->GetLocalSurfaceIdForFrame(frame);
+  }
   compositor_frame_sink_->SubmitCompositorFrame(local_surface_id_,
                                                 std::move(frame));
 }
@@ -131,13 +133,6 @@
   client_->ReclaimResources(resources);
 }
 
-bool ClientCompositorFrameSink::ShouldAllocateNewLocalSurfaceId(
-    const cc::CompositorFrame& frame) {
-  gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size();
-  return frame_size != surface_size_ ||
-         device_scale_factor_ != frame.metadata.device_scale_factor;
-}
-
 void ClientCompositorFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) {
   compositor_frame_sink_->SetNeedsBeginFrame(needs_begin_frames);
 }
diff --git a/components/viz/client/client_compositor_frame_sink.h b/components/viz/client/client_compositor_frame_sink.h
index 008b730..236767c 100644
--- a/components/viz/client/client_compositor_frame_sink.h
+++ b/components/viz/client/client_compositor_frame_sink.h
@@ -16,6 +16,8 @@
 
 namespace viz {
 
+class LocalSurfaceIdProvider;
+
 class ClientCompositorFrameSink
     : public cc::CompositorFrameSink,
       public cc::mojom::MojoCompositorFrameSinkClient,
@@ -30,6 +32,7 @@
           synthetic_begin_frame_source,
       cc::mojom::MojoCompositorFrameSinkPtrInfo compositor_frame_sink_info,
       cc::mojom::MojoCompositorFrameSinkClientRequest client_request,
+      std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider,
       bool enable_surface_synchronization);
 
   ClientCompositorFrameSink(
@@ -38,6 +41,7 @@
           synthetic_begin_frame_source,
       cc::mojom::MojoCompositorFrameSinkPtrInfo compositor_frame_sink_info,
       cc::mojom::MojoCompositorFrameSinkClientRequest client_request,
+      std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider,
       bool enable_surface_synchronization);
 
   ~ClientCompositorFrameSink() override;
@@ -50,9 +54,6 @@
   void DidNotProduceFrame(const cc::BeginFrameAck& ack) override;
 
  private:
-  virtual bool ShouldAllocateNewLocalSurfaceId(
-      const cc::CompositorFrame& frame);
-
   // cc::mojom::MojoCompositorFrameSinkClient implementation:
   void DidReceiveCompositorFrameAck(
       const cc::ReturnedResourceArray& resources) override;
@@ -62,11 +63,8 @@
   // cc::ExternalBeginFrameSourceClient implementation.
   void OnNeedsBeginFrames(bool needs_begin_frames) override;
 
-  gfx::Size surface_size_;
-  float device_scale_factor_ = 0.f;
-
   cc::LocalSurfaceId local_surface_id_;
-  cc::LocalSurfaceIdAllocator id_allocator_;
+  std::unique_ptr<LocalSurfaceIdProvider> local_surface_id_provider_;
   std::unique_ptr<cc::ExternalBeginFrameSource> begin_frame_source_;
   std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source_;
   cc::mojom::MojoCompositorFrameSinkPtrInfo compositor_frame_sink_info_;
diff --git a/components/viz/client/local_surface_id_provider.cc b/components/viz/client/local_surface_id_provider.cc
new file mode 100644
index 0000000..caf3ca9
--- /dev/null
+++ b/components/viz/client/local_surface_id_provider.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/viz/client/local_surface_id_provider.h"
+#include "cc/output/compositor_frame.h"
+
+namespace viz {
+
+LocalSurfaceIdProvider::LocalSurfaceIdProvider() = default;
+
+LocalSurfaceIdProvider::~LocalSurfaceIdProvider() = default;
+
+DefaultLocalSurfaceIdProvider::DefaultLocalSurfaceIdProvider() = default;
+
+const cc::LocalSurfaceId&
+DefaultLocalSurfaceIdProvider::GetLocalSurfaceIdForFrame(
+    const cc::CompositorFrame& frame) {
+  gfx::Size frame_size = frame.render_pass_list.back()->output_rect.size();
+  if (!local_surface_id_.is_valid() || surface_size_ != frame_size ||
+      frame.metadata.device_scale_factor != device_scale_factor_) {
+    local_surface_id_ = local_surface_id_allocator_.GenerateId();
+  }
+  surface_size_ = frame_size;
+  device_scale_factor_ = frame.metadata.device_scale_factor;
+  return local_surface_id_;
+}
+
+}  // namespace viz
diff --git a/components/viz/client/local_surface_id_provider.h b/components/viz/client/local_surface_id_provider.h
new file mode 100644
index 0000000..50f4dd2e
--- /dev/null
+++ b/components/viz/client/local_surface_id_provider.h
@@ -0,0 +1,48 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VIZ_CLIENT_LOCAL_SURFACE_ID_PROVIDER_H_
+#define COMPONENTS_VIZ_CLIENT_LOCAL_SURFACE_ID_PROVIDER_H_
+
+#include "cc/surfaces/local_surface_id.h"
+#include "cc/surfaces/local_surface_id_allocator.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+class CompositorFrame;
+}
+
+namespace viz {
+
+class LocalSurfaceIdProvider {
+ public:
+  LocalSurfaceIdProvider();
+  virtual ~LocalSurfaceIdProvider();
+
+  virtual const cc::LocalSurfaceId& GetLocalSurfaceIdForFrame(
+      const cc::CompositorFrame& frame) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(LocalSurfaceIdProvider);
+};
+
+class DefaultLocalSurfaceIdProvider : public LocalSurfaceIdProvider {
+ public:
+  DefaultLocalSurfaceIdProvider();
+
+  const cc::LocalSurfaceId& GetLocalSurfaceIdForFrame(
+      const cc::CompositorFrame& frame) override;
+
+ private:
+  cc::LocalSurfaceId local_surface_id_;
+  gfx::Size surface_size_;
+  float device_scale_factor_ = 0;
+  cc::LocalSurfaceIdAllocator local_surface_id_allocator_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultLocalSurfaceIdProvider);
+};
+
+}  //  namespace viz
+
+#endif  // COMPONENTS_VIZ_CLIENT_LOCAL_SURFACE_ID_PROVIDER_H_
diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc
index 28a6209..0026338 100644
--- a/content/browser/download/download_item_impl.cc
+++ b/content/browser/download/download_item_impl.cc
@@ -132,6 +132,40 @@
 // The maximum number of attempts we will make to resume automatically.
 const int DownloadItemImpl::kMaxAutoResumeAttempts = 5;
 
+DownloadItemImpl::RequestInfo::RequestInfo(
+    const std::vector<GURL>& url_chain,
+    const GURL& referrer_url,
+    const GURL& site_url,
+    const GURL& tab_url,
+    const GURL& tab_referrer_url,
+    const std::string& suggested_filename,
+    const base::FilePath& forced_file_path,
+    ui::PageTransition transition_type,
+    bool has_user_gesture,
+    const std::string& remote_address,
+    base::Time start_time)
+    : url_chain(url_chain),
+      referrer_url(referrer_url),
+      site_url(site_url),
+      tab_url(tab_url),
+      tab_referrer_url(tab_referrer_url),
+      suggested_filename(suggested_filename),
+      forced_file_path(forced_file_path),
+      transition_type(transition_type),
+      has_user_gesture(has_user_gesture),
+      remote_address(remote_address),
+      start_time(start_time) {}
+
+DownloadItemImpl::RequestInfo::RequestInfo(const GURL& url)
+    : url_chain(std::vector<GURL>(1, url)), start_time(base::Time::Now()) {}
+
+DownloadItemImpl::RequestInfo::RequestInfo() = default;
+
+DownloadItemImpl::RequestInfo::RequestInfo(
+    const DownloadItemImpl::RequestInfo& other) = default;
+
+DownloadItemImpl::RequestInfo::~RequestInfo() = default;
+
 // Constructor for reading from the history service.
 DownloadItemImpl::DownloadItemImpl(
     DownloadItemImplDelegate* delegate,
@@ -161,14 +195,20 @@
     bool transient,
     const std::vector<DownloadItem::ReceivedSlice>& received_slices,
     const net::NetLogWithSource& net_log)
-    : guid_(base::ToUpperASCII(guid)),
+    : request_info_(url_chain,
+                    referrer_url,
+                    site_url,
+                    tab_url,
+                    tab_refererr_url,
+                    std::string(),
+                    base::FilePath(),
+                    ui::PAGE_TRANSITION_LINK,
+                    false,
+                    std::string(),
+                    start_time),
+      guid_(base::ToUpperASCII(guid)),
       download_id_(download_id),
       target_path_(target_path),
-      url_chain_(url_chain),
-      referrer_url_(referrer_url),
-      site_url_(site_url),
-      tab_url_(tab_url),
-      tab_referrer_url_(tab_refererr_url),
       mime_type_(mime_type),
       original_mime_type_(original_mime_type),
       total_bytes_(total_bytes),
@@ -176,7 +216,6 @@
       start_tick_(base::TimeTicks()),
       state_(ExternalToInternalState(state)),
       danger_type_(danger_type),
-      start_time_(start_time),
       end_time_(end_time),
       delegate_(delegate),
       opened_(opened),
@@ -203,32 +242,32 @@
                                    uint32_t download_id,
                                    const DownloadCreateInfo& info,
                                    const net::NetLogWithSource& net_log)
-    : guid_(info.guid.empty() ? base::ToUpperASCII(base::GenerateGUID())
+    : request_info_(info.url_chain,
+                    info.referrer_url,
+                    info.site_url,
+                    info.tab_url,
+                    info.tab_referrer_url,
+                    base::UTF16ToUTF8(info.save_info->suggested_name),
+                    info.save_info->file_path,
+                    info.transition_type ? info.transition_type.value()
+                                         : ui::PAGE_TRANSITION_LINK,
+                    info.has_user_gesture,
+                    info.remote_address,
+                    info.start_time),
+      guid_(info.guid.empty() ? base::ToUpperASCII(base::GenerateGUID())
                               : info.guid),
       download_id_(download_id),
       target_disposition_((info.save_info->prompt_for_save_location)
                               ? TARGET_DISPOSITION_PROMPT
                               : TARGET_DISPOSITION_OVERWRITE),
-      url_chain_(info.url_chain),
-      referrer_url_(info.referrer_url),
-      site_url_(info.site_url),
-      tab_url_(info.tab_url),
-      tab_referrer_url_(info.tab_referrer_url),
-      suggested_filename_(base::UTF16ToUTF8(info.save_info->suggested_name)),
-      forced_file_path_(info.save_info->file_path),
-      transition_type_(info.transition_type ? info.transition_type.value()
-                                            : ui::PAGE_TRANSITION_LINK),
-      has_user_gesture_(info.has_user_gesture),
       response_headers_(info.response_headers),
       content_disposition_(info.content_disposition),
       mime_type_(info.mime_type),
       original_mime_type_(info.original_mime_type),
-      remote_address_(info.remote_address),
       total_bytes_(info.total_bytes),
       last_reason_(info.result),
       start_tick_(base::TimeTicks::Now()),
       state_(INITIAL_INTERNAL),
-      start_time_(info.start_time),
       delegate_(delegate),
       is_temporary_(!info.transient && !info.save_info->file_path.empty()),
       transient_(info.transient),
@@ -258,16 +297,15 @@
     const std::string& mime_type,
     std::unique_ptr<DownloadRequestHandleInterface> request_handle,
     const net::NetLogWithSource& net_log)
-    : is_save_package_download_(true),
+    : request_info_(url),
+      is_save_package_download_(true),
       guid_(base::ToUpperASCII(base::GenerateGUID())),
       download_id_(download_id),
       target_path_(path),
-      url_chain_(1, url),
       mime_type_(mime_type),
       original_mime_type_(mime_type),
       start_tick_(base::TimeTicks::Now()),
       state_(IN_PROGRESS_INTERNAL),
-      start_time_(base::Time::Now()),
       delegate_(delegate),
       current_path_(path),
       net_log_(net_log),
@@ -576,37 +614,39 @@
 }
 
 const GURL& DownloadItemImpl::GetURL() const {
-  return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.back();
+  return request_info_.url_chain.empty() ? GURL::EmptyGURL()
+                                         : request_info_.url_chain.back();
 }
 
 const std::vector<GURL>& DownloadItemImpl::GetUrlChain() const {
-  return url_chain_;
+  return request_info_.url_chain;
 }
 
 const GURL& DownloadItemImpl::GetOriginalUrl() const {
   // Be careful about taking the front() of possibly-empty vectors!
   // http://crbug.com/190096
-  return url_chain_.empty() ? GURL::EmptyGURL() : url_chain_.front();
+  return request_info_.url_chain.empty() ? GURL::EmptyGURL()
+                                         : request_info_.url_chain.front();
 }
 
 const GURL& DownloadItemImpl::GetReferrerUrl() const {
-  return referrer_url_;
+  return request_info_.referrer_url;
 }
 
 const GURL& DownloadItemImpl::GetSiteUrl() const {
-  return site_url_;
+  return request_info_.site_url;
 }
 
 const GURL& DownloadItemImpl::GetTabUrl() const {
-  return tab_url_;
+  return request_info_.tab_url;
 }
 
 const GURL& DownloadItemImpl::GetTabReferrerUrl() const {
-  return tab_referrer_url_;
+  return request_info_.tab_referrer_url;
 }
 
 std::string DownloadItemImpl::GetSuggestedFilename() const {
-  return suggested_filename_;
+  return request_info_.suggested_filename;
 }
 
 const scoped_refptr<const net::HttpResponseHeaders>&
@@ -627,16 +667,16 @@
 }
 
 std::string DownloadItemImpl::GetRemoteAddress() const {
-  return remote_address_;
+  return request_info_.remote_address;
 }
 
 bool DownloadItemImpl::HasUserGesture() const {
-  return has_user_gesture_;
-};
+  return request_info_.has_user_gesture;
+}
 
 ui::PageTransition DownloadItemImpl::GetTransitionType() const {
-  return transition_type_;
-};
+  return request_info_.transition_type;
+}
 
 const std::string& DownloadItemImpl::GetLastModifiedTime() const {
   return last_modified_time_;
@@ -661,7 +701,7 @@
 const base::FilePath& DownloadItemImpl::GetForcedFilePath() const {
   // TODO(asanka): Get rid of GetForcedFilePath(). We should instead just
   // require that clients respect GetTargetFilePath() if it is already set.
-  return forced_file_path_;
+  return request_info_.forced_file_path;
 }
 
 base::FilePath DownloadItemImpl::GetFileNameToReportUser() const {
@@ -766,7 +806,7 @@
 }
 
 base::Time DownloadItemImpl::GetStartTime() const {
-  return start_time_;
+  return request_info_.start_time;
 }
 
 base::Time DownloadItemImpl::GetEndTime() const {
@@ -869,9 +909,9 @@
 
   // Construct a string of the URL chain.
   std::string url_list("<none>");
-  if (!url_chain_.empty()) {
-    std::vector<GURL>::const_iterator iter = url_chain_.begin();
-    std::vector<GURL>::const_iterator last = url_chain_.end();
+  if (!request_info_.url_chain.empty()) {
+    std::vector<GURL>::const_iterator iter = request_info_.url_chain.begin();
+    std::vector<GURL>::const_iterator last = request_info_.url_chain.end();
     url_list = (*iter).is_valid() ? (*iter).spec() : "<invalid>";
     ++iter;
     for ( ; verbose && (iter != last); ++iter) {
@@ -1029,7 +1069,7 @@
   //   download since the initial request, in order.
   std::vector<GURL>::const_iterator chain_iter =
       new_create_info.url_chain.begin();
-  if (*chain_iter == url_chain_.back())
+  if (*chain_iter == request_info_.url_chain.back())
     ++chain_iter;
 
   // Record some stats. If the precondition failed (the server returned
@@ -1049,8 +1089,8 @@
     origin_state |= ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED;
   RecordOriginStateOnResumption(is_partial, origin_state);
 
-  url_chain_.insert(
-      url_chain_.end(), chain_iter, new_create_info.url_chain.end());
+  request_info_.url_chain.insert(request_info_.url_chain.end(), chain_iter,
+                                 new_create_info.url_chain.end());
   etag_ = new_create_info.etag;
   last_modified_time_ = new_create_info.last_modified;
   response_headers_ = new_create_info.response_headers;
@@ -1203,10 +1243,10 @@
     file_name = target_path_.AsUTF8Unsafe();
   } else {
     // See if it's set programmatically.
-    file_name = forced_file_path_.AsUTF8Unsafe();
+    file_name = GetForcedFilePath().AsUTF8Unsafe();
     // Possibly has a 'download' attribute for the anchor.
     if (file_name.empty())
-      file_name = suggested_filename_;
+      file_name = GetSuggestedFilename();
     // From the URL file name.
     if (file_name.empty())
       file_name = GetURL().ExtractFileName();
@@ -2078,7 +2118,7 @@
 
   StoragePartition* storage_partition =
       BrowserContext::GetStoragePartitionForSite(GetBrowserContext(),
-                                                 site_url_);
+                                                 request_info_.site_url);
 
   // Avoid using the WebContents even if it's still around. Resumption requests
   // are consistently routed through the no-renderer code paths so that the
diff --git a/content/browser/download/download_item_impl.h b/content/browser/download/download_item_impl.h
index 42a4ebaa..9980ad00 100644
--- a/content/browser/download/download_item_impl.h
+++ b/content/browser/download/download_item_impl.h
@@ -43,6 +43,65 @@
     RESUME_MODE_USER_RESTART
   };
 
+  // Information about the initial request that triggers the download. Most of
+  // the fields are immutable after the DownloadItem is successfully created.
+  // However, it is possible that the url chain is changed when resuming an
+  // interrupted download. In that case, the download will restart from the
+  // beginning.
+  struct CONTENT_EXPORT RequestInfo {
+    RequestInfo(const std::vector<GURL>& url_chain,
+                const GURL& referrer_url,
+                const GURL& site_url,
+                const GURL& tab_url,
+                const GURL& tab_referrer_url,
+                const std::string& suggested_filename,
+                const base::FilePath& forced_file_path,
+                ui::PageTransition transition_type,
+                bool has_user_gesture,
+                const std::string& remote_address,
+                base::Time start_time);
+    RequestInfo();
+    RequestInfo(const RequestInfo& other);
+    RequestInfo(const GURL& url);
+    ~RequestInfo();
+
+    // The chain of redirects that leading up to and including the final URL.
+    std::vector<GURL> url_chain;
+
+    // The URL of the page that initiated the download.
+    GURL referrer_url;
+
+    // Site URL for the site instance that initiated this download.
+    GURL site_url;
+
+    // The URL of the tab that initiated the download.
+    GURL tab_url;
+
+    // The URL of the referrer of the tab that initiated the download.
+    GURL tab_referrer_url;
+
+    // Filename suggestion from DownloadSaveInfo. It could, among others, be the
+    // suggested filename in 'download' attribute of an anchor. Details:
+    // http://www.whatwg.org/specs/web-apps/current-work/#downloading-hyperlinks
+    std::string suggested_filename;
+
+    // If non-empty, contains an externally supplied path that should be used as
+    // the target path.
+    base::FilePath forced_file_path;
+
+    // Page transition that triggerred the download.
+    ui::PageTransition transition_type = ui::PAGE_TRANSITION_LINK;
+
+    // Whether the download was triggered with a user gesture.
+    bool has_user_gesture = false;
+
+    // The remote IP address where the download was fetched from.
+    std::string remote_address;
+
+    // Time the download was started.
+    base::Time start_time;
+  };
+
   // The maximum number of attempts we will make to resume automatically.
   static const int kMaxAutoResumeAttempts;
 
@@ -517,6 +576,8 @@
   static bool IsValidStateTransition(DownloadInternalState from,
                                      DownloadInternalState to);
 
+  RequestInfo request_info_;
+
   // Will be false for save package downloads retrieved from the history.
   // TODO(rdsmith): Replace with a generalized enum for "download source".
   const bool is_save_package_download_ = false;
@@ -537,36 +598,6 @@
   // Whether the target should be overwritten, uniquified or prompted for.
   TargetDisposition target_disposition_ = TARGET_DISPOSITION_OVERWRITE;
 
-  // The chain of redirects that leading up to and including the final URL.
-  std::vector<GURL> url_chain_;
-
-  // The URL of the page that initiated the download.
-  GURL referrer_url_;
-
-  // Site URL for the site instance that initiated this download.
-  GURL site_url_;
-
-  // The URL of the tab that initiated the download.
-  GURL tab_url_;
-
-  // The URL of the referrer of the tab that initiated the download.
-  GURL tab_referrer_url_;
-
-  // Filename suggestion from DownloadSaveInfo. It could, among others, be the
-  // suggested filename in 'download' attribute of an anchor. Details:
-  // http://www.whatwg.org/specs/web-apps/current-work/#downloading-hyperlinks
-  std::string suggested_filename_;
-
-  // If non-empty, contains an externally supplied path that should be used as
-  // the target path.
-  base::FilePath forced_file_path_;
-
-  // Page transition that triggerred the download.
-  ui::PageTransition transition_type_ = ui::PAGE_TRANSITION_LINK;
-
-  // Whether the download was triggered with a user gesture.
-  bool has_user_gesture_ = false;
-
   // Information from the response.
 
   // The HTTP response headers. This contains a nullptr when the response has
@@ -584,10 +615,6 @@
   // which may look at the file extension and first few bytes of the file.
   std::string original_mime_type_;
 
-  // The remote IP address where the download was fetched from.  Copied from
-  // DownloadCreateInfo::remote_address.
-  std::string remote_address_;
-
   // Total bytes expected.
   int64_t total_bytes_ = 0;
 
@@ -606,9 +633,6 @@
   // The views of this item in the download shelf and download contents.
   base::ObserverList<Observer> observers_;
 
-  // Time the download was started.
-  base::Time start_time_;
-
   // Time the download completed.
   base::Time end_time_;
 
diff --git a/content/common/feature_policy/feature_policy.cc b/content/common/feature_policy/feature_policy.cc
index 76ad7698..1211672a 100644
--- a/content/common/feature_policy/feature_policy.cc
+++ b/content/common/feature_policy/feature_policy.cc
@@ -170,7 +170,7 @@
     } else {
       new_policy->inherited_policies_[feature.first] = false;
     }
-    if (!container_policy.empty())
+    if (parent_policy && !container_policy.empty())
       new_policy->AddContainerPolicy(container_policy, parent_policy);
   }
   return new_policy;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 7681cd24..8f8da66f 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -347,10 +347,6 @@
 const base::Feature kAndroidAutofillAccessibility{
     "AndroidAutofillAccessibility", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// FeatureList definition for the Seccomp field trial.
-const base::Feature kSeccompSandboxAndroid{"SeccompSandboxAndroid",
-                                           base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Service worker based payment apps as defined by w3c here:
 // https://w3c.github.io/webpayments-payment-apps-api/
 const base::Feature kServiceWorkerPaymentApps{
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index f426387..b2138af 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -89,7 +89,6 @@
 #if defined(OS_ANDROID)
 CONTENT_EXPORT extern const base::Feature kAndroidAutofillAccessibility;
 CONTENT_EXPORT extern const base::Feature kImeThread;
-CONTENT_EXPORT extern const base::Feature kSeccompSandboxAndroid;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerPaymentApps;
 CONTENT_EXPORT extern const base::Feature kWebNfc;
 #endif  // defined(OS_ANDROID)
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 4b534b9..66be081 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -137,8 +137,6 @@
     "gpu/render_widget_compositor.cc",
     "gpu/render_widget_compositor.h",
     "gpu/render_widget_compositor_delegate.h",
-    "gpu/renderer_compositor_frame_sink.cc",
-    "gpu/renderer_compositor_frame_sink.h",
     "gpu/stream_texture_host_android.cc",
     "gpu/stream_texture_host_android.h",
     "history_entry.cc",
diff --git a/content/renderer/gpu/renderer_compositor_frame_sink.cc b/content/renderer/gpu/renderer_compositor_frame_sink.cc
deleted file mode 100644
index 90a0846..0000000
--- a/content/renderer/gpu/renderer_compositor_frame_sink.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2012 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 "content/renderer/gpu/renderer_compositor_frame_sink.h"
-
-#include <utility>
-
-#include "base/command_line.h"
-#include "base/location.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "cc/output/compositor_frame.h"
-#include "cc/output/compositor_frame_sink_client.h"
-#include "cc/output/managed_memory_policy.h"
-#include "content/common/view_messages.h"
-#include "content/public/common/content_switches.h"
-#include "content/renderer/render_thread_impl.h"
-#include "gpu/command_buffer/client/context_support.h"
-#include "gpu/command_buffer/client/gles2_interface.h"
-#include "gpu/ipc/client/command_buffer_proxy_impl.h"
-#include "ipc/ipc_sync_channel.h"
-#include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
-
-namespace content {
-
-RendererCompositorFrameSink::RendererCompositorFrameSink(
-    int32_t routing_id,
-    std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source,
-    scoped_refptr<cc::ContextProvider> context_provider,
-    scoped_refptr<cc::ContextProvider> worker_context_provider,
-    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-    cc::SharedBitmapManager* shared_bitmap_manager,
-    cc::mojom::MojoCompositorFrameSinkPtrInfo sink_info,
-    cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request)
-    : ClientCompositorFrameSink(std::move(context_provider),
-                                std::move(worker_context_provider),
-                                gpu_memory_buffer_manager,
-                                shared_bitmap_manager,
-                                std::move(synthetic_begin_frame_source),
-                                std::move(sink_info),
-                                std::move(sink_client_request),
-                                false /* enable_surface_synchronization */),
-      compositor_frame_sink_filter_(
-          RenderThreadImpl::current()->compositor_message_filter()),
-      message_sender_(RenderThreadImpl::current()->sync_message_filter()),
-      routing_id_(routing_id) {
-  DCHECK(compositor_frame_sink_filter_);
-  DCHECK(message_sender_);
-}
-
-RendererCompositorFrameSink::RendererCompositorFrameSink(
-    int32_t routing_id,
-    std::unique_ptr<cc::SyntheticBeginFrameSource> synthetic_begin_frame_source,
-    scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider,
-    cc::mojom::MojoCompositorFrameSinkPtrInfo sink_info,
-    cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request)
-    : ClientCompositorFrameSink(std::move(vulkan_context_provider),
-                                std::move(synthetic_begin_frame_source),
-                                std::move(sink_info),
-                                std::move(sink_client_request),
-                                false /* enable_surface_synchronization */),
-      compositor_frame_sink_filter_(
-          RenderThreadImpl::current()->compositor_message_filter()),
-      message_sender_(RenderThreadImpl::current()->sync_message_filter()),
-      routing_id_(routing_id) {
-  DCHECK(compositor_frame_sink_filter_);
-  DCHECK(message_sender_);
-}
-
-RendererCompositorFrameSink::~RendererCompositorFrameSink() = default;
-
-bool RendererCompositorFrameSink::BindToClient(
-    cc::CompositorFrameSinkClient* client) {
-  if (!ClientCompositorFrameSink::BindToClient(client))
-    return false;
-
-  compositor_frame_sink_proxy_ = new RendererCompositorFrameSinkProxy(this);
-  compositor_frame_sink_filter_handler_ =
-      base::Bind(&RendererCompositorFrameSinkProxy::OnMessageReceived,
-                 compositor_frame_sink_proxy_);
-  compositor_frame_sink_filter_->AddHandlerOnCompositorThread(
-      routing_id_, compositor_frame_sink_filter_handler_);
-
-  return true;
-}
-
-void RendererCompositorFrameSink::DetachFromClient() {
-  compositor_frame_sink_proxy_->ClearCompositorFrameSink();
-  compositor_frame_sink_filter_->RemoveHandlerOnCompositorThread(
-      routing_id_, compositor_frame_sink_filter_handler_);
-  ClientCompositorFrameSink::DetachFromClient();
-}
-
-void RendererCompositorFrameSink::SubmitCompositorFrame(
-    cc::CompositorFrame frame) {
-  auto new_surface_properties =
-      RenderWidgetSurfaceProperties::FromCompositorFrame(frame);
-  ClientCompositorFrameSink::SubmitCompositorFrame(std::move(frame));
-  current_surface_properties_ = new_surface_properties;
-}
-
-bool RendererCompositorFrameSink::ShouldAllocateNewLocalSurfaceId(
-    const cc::CompositorFrame& frame) {
-  return current_surface_properties_ !=
-         RenderWidgetSurfaceProperties::FromCompositorFrame(frame);
-}
-
-}  // namespace content
diff --git a/content/renderer/gpu/renderer_compositor_frame_sink.h b/content/renderer/gpu/renderer_compositor_frame_sink.h
deleted file mode 100644
index 6db24f0b..0000000
--- a/content/renderer/gpu/renderer_compositor_frame_sink.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (c) 2012 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 CONTENT_RENDERER_GPU_RENDERER_COMPOSITOR_FRAME_SINK_H_
-#define CONTENT_RENDERER_GPU_RENDERER_COMPOSITOR_FRAME_SINK_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/non_thread_safe.h"
-#include "base/threading/platform_thread.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "cc/input/selection.h"
-#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
-#include "cc/output/begin_frame_args.h"
-#include "cc/output/compositor_frame_sink.h"
-#include "cc/scheduler/begin_frame_source.h"
-#include "cc/surfaces/local_surface_id.h"
-#include "cc/surfaces/local_surface_id_allocator.h"
-#include "components/viz/client/client_compositor_frame_sink.h"
-#include "content/common/render_widget_surface_properties.h"
-#include "content/renderer/gpu/compositor_forwarding_message_filter.h"
-#include "ipc/ipc_sync_message_filter.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "ui/gfx/selection_bound.h"
-
-namespace IPC {
-class Message;
-}
-
-namespace cc {
-class CompositorFrame;
-class ContextProvider;
-}
-
-namespace content {
-
-// This class can be created only on the main thread, but then becomes pinned
-// to a fixed thread when BindToClient is called.
-class RendererCompositorFrameSink
-    : NON_EXPORTED_BASE(public viz::ClientCompositorFrameSink) {
- public:
-  RendererCompositorFrameSink(
-      int32_t routing_id,
-      std::unique_ptr<cc::SyntheticBeginFrameSource>
-          synthetic_begin_frame_source,
-      scoped_refptr<cc::ContextProvider> context_provider,
-      scoped_refptr<cc::ContextProvider> worker_context_provider,
-      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
-      cc::SharedBitmapManager* shared_bitmap_manager,
-      cc::mojom::MojoCompositorFrameSinkPtrInfo sink_info,
-      cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request);
-  RendererCompositorFrameSink(
-      int32_t routing_id,
-      std::unique_ptr<cc::SyntheticBeginFrameSource>
-          synthetic_begin_frame_source,
-      scoped_refptr<cc::VulkanContextProvider> vulkan_context_provider,
-      cc::mojom::MojoCompositorFrameSinkPtrInfo sink_info,
-      cc::mojom::MojoCompositorFrameSinkClientRequest sink_client_request);
-  ~RendererCompositorFrameSink() override;
-
-  // Overriden from viz::ClientCompositorFrameSink.
-  bool BindToClient(cc::CompositorFrameSinkClient* client) override;
-  void DetachFromClient() override;
-  void SubmitCompositorFrame(cc::CompositorFrame frame) override;
-  bool ShouldAllocateNewLocalSurfaceId(
-      const cc::CompositorFrame& frame) override;
-
- private:
-  class RendererCompositorFrameSinkProxy
-      : public base::RefCountedThreadSafe<RendererCompositorFrameSinkProxy> {
-   public:
-    explicit RendererCompositorFrameSinkProxy(
-        RendererCompositorFrameSink* compositor_frame_sink)
-        : compositor_frame_sink_(compositor_frame_sink) {}
-    void ClearCompositorFrameSink() { compositor_frame_sink_ = NULL; }
-    void OnMessageReceived(const IPC::Message& message) {
-      if (compositor_frame_sink_)
-        compositor_frame_sink_->OnMessageReceived(message);
-    }
-
-   private:
-    friend class base::RefCountedThreadSafe<RendererCompositorFrameSinkProxy>;
-    ~RendererCompositorFrameSinkProxy() {}
-    RendererCompositorFrameSink* compositor_frame_sink_;
-
-    DISALLOW_COPY_AND_ASSIGN(RendererCompositorFrameSinkProxy);
-  };
-
-  void OnMessageReceived(const IPC::Message& message) {}
-
-  scoped_refptr<CompositorForwardingMessageFilter>
-      compositor_frame_sink_filter_;
-  CompositorForwardingMessageFilter::Handler
-      compositor_frame_sink_filter_handler_;
-  scoped_refptr<RendererCompositorFrameSinkProxy> compositor_frame_sink_proxy_;
-  scoped_refptr<IPC::SyncMessageFilter> message_sender_;
-  int routing_id_;
-
-  RenderWidgetSurfaceProperties current_surface_properties_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_GPU_RENDERER_COMPOSITOR_FRAME_SINK_H_
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc
index 20dc7d93..59b6a8b 100644
--- a/content/renderer/mus/renderer_window_tree_client.cc
+++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -10,6 +10,7 @@
 #include "base/lazy_instance.h"
 #include "cc/base/switches.h"
 #include "components/viz/client/client_compositor_frame_sink.h"
+#include "components/viz/client/local_surface_id_provider.h"
 
 namespace content {
 
@@ -87,7 +88,9 @@
       std::move(context_provider), nullptr /* worker_context_provider */,
       gpu_memory_buffer_manager, nullptr /* shared_bitmap_manager */,
       nullptr /* synthetic_begin_frame_source */, std::move(sink_info),
-      std::move(client_request), enable_surface_synchronization);
+      std::move(client_request),
+      base::MakeUnique<viz::DefaultLocalSurfaceIdProvider>(),
+      enable_surface_synchronization);
   tree_->AttachCompositorFrameSink(root_window_id_, std::move(sink_request),
                                    std::move(client));
   callback.Run(std::move(frame_sink));
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index bc63aab..fe54aff 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -55,6 +55,8 @@
 #include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
 #include "components/metrics/public/interfaces/single_sample_metrics.mojom.h"
 #include "components/metrics/single_sample_metrics.h"
+#include "components/viz/client/client_compositor_frame_sink.h"
+#include "components/viz/client/local_surface_id_provider.h"
 #include "content/child/appcache/appcache_dispatcher.h"
 #include "content/child/appcache/appcache_frontend_impl.h"
 #include "content/child/blob_storage/blob_message_filter.h"
@@ -106,7 +108,6 @@
 #include "content/renderer/gpu/compositor_external_begin_frame_source.h"
 #include "content/renderer/gpu/compositor_forwarding_message_filter.h"
 #include "content/renderer/gpu/frame_swap_message_queue.h"
-#include "content/renderer/gpu/renderer_compositor_frame_sink.h"
 #include "content/renderer/input/input_event_filter.h"
 #include "content/renderer/input/input_handler_manager.h"
 #include "content/renderer/input/main_thread_input_event_filter.h"
@@ -419,6 +420,26 @@
                  connector, base::Passed(&request)));
 }
 
+class RendererLocalSurfaceIdProvider : public viz::LocalSurfaceIdProvider {
+ public:
+  const cc::LocalSurfaceId& GetLocalSurfaceIdForFrame(
+      const cc::CompositorFrame& frame) override {
+    auto new_surface_properties =
+        RenderWidgetSurfaceProperties::FromCompositorFrame(frame);
+    if (!local_surface_id_.is_valid() ||
+        new_surface_properties != surface_properties_) {
+      local_surface_id_ = local_surface_id_allocator_.GenerateId();
+      surface_properties_ = new_surface_properties;
+    }
+    return local_surface_id_;
+  }
+
+ private:
+  cc::LocalSurfaceIdAllocator local_surface_id_allocator_;
+  cc::LocalSurfaceId local_surface_id_;
+  RenderWidgetSurfaceProperties surface_properties_;
+};
+
 }  // namespace
 
 RenderThreadImpl::HistogramCustomizer::HistogramCustomizer() {
@@ -1914,10 +1935,12 @@
       DCHECK(!layout_test_mode());
       frame_sink_provider_->CreateForWidget(routing_id, std::move(sink_request),
                                             std::move(client));
-      callback.Run(base::MakeUnique<RendererCompositorFrameSink>(
-          routing_id, std::move(synthetic_begin_frame_source),
-          std::move(vulkan_context_provider), std::move(sink_info),
-          std::move(client_request)));
+      callback.Run(base::MakeUnique<viz::ClientCompositorFrameSink>(
+          std::move(vulkan_context_provider),
+          std::move(synthetic_begin_frame_source), std::move(sink_info),
+          std::move(client_request),
+          base::MakeUnique<RendererLocalSurfaceIdProvider>(),
+          false /* enable_surface_synchroninzation */));
       return;
     }
   }
@@ -1942,10 +1965,12 @@
     DCHECK(!layout_test_mode());
     frame_sink_provider_->CreateForWidget(routing_id, std::move(sink_request),
                                           std::move(client));
-    callback.Run(base::MakeUnique<RendererCompositorFrameSink>(
-        routing_id, std::move(synthetic_begin_frame_source), nullptr, nullptr,
-        nullptr, shared_bitmap_manager(), std::move(sink_info),
-        std::move(client_request)));
+    callback.Run(base::MakeUnique<viz::ClientCompositorFrameSink>(
+        nullptr, nullptr, nullptr, shared_bitmap_manager(),
+        std::move(synthetic_begin_frame_source), std::move(sink_info),
+        std::move(client_request),
+        base::MakeUnique<RendererLocalSurfaceIdProvider>(),
+        false /* enable_surface_synchroninzation */));
     return;
   }
 
@@ -2014,11 +2039,13 @@
 #endif
   frame_sink_provider_->CreateForWidget(routing_id, std::move(sink_request),
                                         std::move(client));
-  callback.Run(base::WrapUnique(new RendererCompositorFrameSink(
-      routing_id, std::move(synthetic_begin_frame_source),
+  callback.Run(base::MakeUnique<viz::ClientCompositorFrameSink>(
       std::move(context_provider), std::move(worker_context_provider),
-      GetGpuMemoryBufferManager(), nullptr, std::move(sink_info),
-      std::move(client_request))));
+      GetGpuMemoryBufferManager(), nullptr,
+      std::move(synthetic_begin_frame_source), std::move(sink_info),
+      std::move(client_request),
+      base::MakeUnique<RendererLocalSurfaceIdProvider>(),
+      false /* enable_surface_synchroninzation */));
 }
 
 AssociatedInterfaceRegistry*
diff --git a/content/renderer/renderer_main_platform_delegate_android.cc b/content/renderer/renderer_main_platform_delegate_android.cc
index fc14746..fd6dde5 100644
--- a/content/renderer/renderer_main_platform_delegate_android.cc
+++ b/content/renderer/renderer_main_platform_delegate_android.cc
@@ -7,7 +7,6 @@
 #include <signal.h>
 
 #include "base/android/build_info.h"
-#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
@@ -16,7 +15,6 @@
 
 #if BUILDFLAG(USE_SECCOMP_BPF)
 #include "content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.h"
-#include "content/public/common/content_features.h"
 #include "content/renderer/seccomp_sandbox_status_android.h"
 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
 #endif
@@ -106,29 +104,22 @@
     return true;
   }
 
-  // Seccomp has been detected, check if the field trial experiment should run.
-  if (base::FeatureList::IsEnabled(features::kSeccompSandboxAndroid)) {
-    status_uma.set_status(SeccompSandboxStatus::FEATURE_ENABLED);
-
-    sig_t old_handler = signal(SIGSYS, SIG_DFL);
-    if (old_handler != SIG_DFL) {
-      // On Android O and later, the zygote applies a seccomp filter to all
-      // apps. It has its own SIGSYS handler that must be un-hooked so that
-      // the Chromium one can be used instead. If pre-O devices have a SIGSYS
-      // handler, then warn about that.
-      DLOG_IF(WARNING, info->sdk_int() < 26)
-          << "Un-hooking existing SIGSYS handler before starting "
-          << "Seccomp sandbox";
-    }
-
-    sandbox::SandboxBPF sandbox(new SandboxBPFBasePolicyAndroid());
-    CHECK(sandbox.StartSandbox(
-        sandbox::SandboxBPF::SeccompLevel::MULTI_THREADED));
-
-    status_uma.set_status(SeccompSandboxStatus::ENGAGED);
-  } else {
-    status_uma.set_status(SeccompSandboxStatus::FEATURE_DISABLED);
+  sig_t old_handler = signal(SIGSYS, SIG_DFL);
+  if (old_handler != SIG_DFL) {
+    // On Android O and later, the zygote applies a seccomp filter to all
+    // apps. It has its own SIGSYS handler that must be un-hooked so that
+    // the Chromium one can be used instead. If pre-O devices have a SIGSYS
+    // handler, then warn about that.
+    DLOG_IF(WARNING, info->sdk_int() < 26)
+        << "Un-hooking existing SIGSYS handler before starting "
+        << "Seccomp sandbox";
   }
+
+  sandbox::SandboxBPF sandbox(new SandboxBPFBasePolicyAndroid());
+  CHECK(
+      sandbox.StartSandbox(sandbox::SandboxBPF::SeccompLevel::MULTI_THREADED));
+
+  status_uma.set_status(SeccompSandboxStatus::ENGAGED);
 #endif
   return true;
 }
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 0212732e..c9d2fe4 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -111,8 +111,6 @@
     # Passthrough command decoder
     self.Fail('conformance/extensions/ext-sRGB.html',
         ['passthrough'], bug=679696)
-    self.Fail('conformance/extensions/get-extension.html',
-        ['passthrough'], bug=682745)
     self.Fail('conformance/extensions/webgl-draw-buffers.html',
         ['passthrough'], bug=1523) # angle bug ID
     self.Fail('conformance/glsl/misc/shaders-with-name-conflicts.html',
@@ -127,8 +125,6 @@
         ['passthrough'], bug=1635) # angle bug ID
     self.Fail('conformance/textures/misc/texture-mips.html',
         ['passthrough'], bug=665518)
-    self.Fail('conformance/extensions/webgl-compressed-texture-s3tc-srgb.html',
-        ['passthrough'], bug=2049) # angle bug ID
     self.Skip('conformance/textures/canvas/*',
         ['passthrough'], bug=1932) # angle bug ID
     self.Skip('conformance/textures/video/*',
diff --git a/docs/testing/layout_tests.md b/docs/testing/layout_tests.md
index c6d57e2..02294ff 100644
--- a/docs/testing/layout_tests.md
+++ b/docs/testing/layout_tests.md
@@ -153,8 +153,7 @@
 | `--nocheck-sys-deps`        | Don't check system dependencies; this allows faster iteration. |
 | `--verbose`                 |	Produce more verbose output, including a list of tests that pass. |
 | `--no-pixel-tests`          | Disable the pixel-to-pixel PNG comparisons and image checksums for tests that don't call `testRunner.dumpAsText()` |
-| `--reset-results`           |	Write all generated results directly into the given directory, overwriting what's there. |
-| `--new-baseline`            |	Write all generated results into the most specific platform directory, overwriting what's there. Equivalent to `--reset-results --add-platform-expectations` |
+| `--reset-results`           |	Overwrite the current baselines (`-expected.{png|txt|wav}` files) with actual results, or create new baselines if there are no existing baselines. |
 | `--renderer-startup-dialog` | Bring up a modal dialog before running the test, useful for attaching a debugger. |
 | `--fully-parallel`          | Run tests in parallel using as many child processes as the system has cores. |
 | `--driver-logging`          | Print C++ logs (LOG(WARNING), etc).  |
@@ -431,36 +430,29 @@
 
 *** promo
 To automatically re-baseline tests across all Chromium platforms, using the
-buildbot results, see the
-[Rebaselining keywords in TestExpectations](./layout_test_expectations.md)
-and the
-[Rebaselining Tool](https://trac.webkit.org/wiki/Rebaseline).
+buildbot results, see [How to rebaseline](./layout_test_expectations.md#How-to-rebaseline).
 Alternatively, to manually run and test and rebaseline it on your workstation,
 read on.
 ***
 
-By default, text-only tests (ones that call `testRunner.dumpAsText()`) produce
-only text results. Other tests produce both new text results and new image
-results (the image baseline comprises two files, `-expected.png` and
-  `-expected.checksum`). So you'll need either one or three `-expected.\*` files
-in your new baseline, depending on whether you have a text-only test or not. If
-you enable `--no-pixel-tests`, only new text results will be produced, even for
-tests that do image comparisons.
-
 ```bash
 cd src/third_party/WebKit
-Tools/Scripts/run-webkit-tests --new-baseline foo/bar/test.html
+Tools/Scripts/run-webkit-tests --reset-results foo/bar/test.html
 ```
 
-The above command will generate a new baseline for
-`LayoutTests/foo/bar/test.html` and put the output files in the right place,
-e.g.
-`LayoutTests/platform/chromium-win/LayoutTests/foo/bar/test-expected.{txt,png,checksum}`.
+If there are current expectation files for `LayoutTests/foo/bar/test.html`,
+the above command will overwrite the current baselines at their original
+locations with the actual results. The current baseline means the `-expected.*`
+file used to compare the actual result when the test is run locally, i.e. the
+first file found in the [baseline search path]
+(https://cs.chromium.org/search/?q=port/base.py+baseline_search_path).
+
+If there are no current baselines, the above command will create new baselines
+in the platform-independent directory, e.g.
+`LayoutTests/foo/bar/test-expected.{txt,png}`.
 
 When you rebaseline a test, make sure your commit description explains why the
-test is being re-baselined. If this is a special case (i.e., something we've
-decided to be different with upstream), please put a README file next to the new
-expected output explaining the difference.
+test is being re-baselined.
 
 ## web-platform-tests
 
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
index 29ee8e9..d2fe695 100644
--- a/gpu/command_buffer/service/feature_info.cc
+++ b/gpu/command_buffer/service/feature_info.cc
@@ -1410,22 +1410,23 @@
     // formats as GL_OES_texture_float(i.e.LUMINANCE_ALPHA,LUMINANCE and Alpha)
     if (extensions.Contains("GL_OES_texture_float")) {
       enable_texture_float = true;
-      if (extensions.Contains("GL_OES_texture_float_linear")) {
-        enable_texture_float_linear = true;
-      }
-
       if (enable_ext_color_buffer_float) {
         may_enable_chromium_color_buffer_float = true;
       }
     }
 
+    if (extensions.Contains("GL_OES_texture_float_linear")) {
+      enable_texture_float_linear = true;
+    }
+
     // TODO(dshwang): GLES3 supports half float by default but GL_HALF_FLOAT_OES
     // isn't equal to GL_HALF_FLOAT.
     if (extensions.Contains("GL_OES_texture_half_float")) {
       enable_texture_half_float = true;
-      if (extensions.Contains("GL_OES_texture_half_float_linear")) {
-        enable_texture_half_float_linear = true;
-      }
+    }
+
+    if (extensions.Contains("GL_OES_texture_half_float_linear")) {
+      enable_texture_half_float_linear = true;
     }
   }
 
@@ -1433,22 +1434,24 @@
     validators_.pixel_type.AddValue(GL_FLOAT);
     validators_.read_pixel_type.AddValue(GL_FLOAT);
     AddExtensionString("GL_OES_texture_float");
-    if (enable_texture_float_linear) {
-      oes_texture_float_linear_available_ = true;
-      if (!disallowed_features_.oes_texture_float_linear)
-        EnableOESTextureFloatLinear();
-    }
+  }
+
+  if (enable_texture_float_linear) {
+    oes_texture_float_linear_available_ = true;
+    if (!disallowed_features_.oes_texture_float_linear)
+      EnableOESTextureFloatLinear();
   }
 
   if (enable_texture_half_float) {
     validators_.pixel_type.AddValue(GL_HALF_FLOAT_OES);
     validators_.read_pixel_type.AddValue(GL_HALF_FLOAT_OES);
     AddExtensionString("GL_OES_texture_half_float");
-    if (enable_texture_half_float_linear) {
-      oes_texture_half_float_linear_available_ = true;
-      if (!disallowed_features_.oes_texture_half_float_linear)
-        EnableOESTextureHalfFloatLinear();
-    }
+  }
+
+  if (enable_texture_half_float_linear) {
+    oes_texture_half_float_linear_available_ = true;
+    if (!disallowed_features_.oes_texture_half_float_linear)
+      EnableOESTextureHalfFloatLinear();
   }
 
   bool had_native_chromium_color_buffer_float_ext = false;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index a3bf1c2..d9adaf81 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -170,11 +170,10 @@
   }
 
   // Each context initializes its own feature info because some extensions may
-  // be enabled dynamically
-  DisallowedFeatures adjusted_disallowed_features =
-      AdjustDisallowedFeatures(attrib_helper.context_type, disallowed_features);
+  // be enabled dynamically.  Don't disallow any features, leave it up to ANGLE
+  // to dynamically enable extensions.
   if (!feature_info_->Initialize(attrib_helper.context_type,
-                                 adjusted_disallowed_features)) {
+                                 DisallowedFeatures())) {
     Destroy(true);
     return false;
   }
diff --git a/media/gpu/dxva_video_decode_accelerator_win.cc b/media/gpu/dxva_video_decode_accelerator_win.cc
index c1f525db..c2c4629 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.cc
+++ b/media/gpu/dxva_video_decode_accelerator_win.cc
@@ -1235,13 +1235,11 @@
         (supported_profile <= VP9PROFILE_MAX)) {
       continue;
     }
-    std::pair<int, int> min_resolution = GetMinResolution(supported_profile);
-    std::pair<int, int> max_resolution = GetMaxResolution(supported_profile);
 
     SupportedProfile profile;
     profile.profile = supported_profile;
-    profile.min_resolution.SetSize(min_resolution.first, min_resolution.second);
-    profile.max_resolution.SetSize(max_resolution.first, max_resolution.second);
+    profile.min_resolution = GetMinResolution(supported_profile);
+    profile.max_resolution = GetMaxResolution(supported_profile);
     profiles.push_back(profile);
   }
   return profiles;
@@ -1264,95 +1262,74 @@
 }
 
 // static
-std::pair<int, int> DXVAVideoDecodeAccelerator::GetMinResolution(
+gfx::Size DXVAVideoDecodeAccelerator::GetMinResolution(
     VideoCodecProfile profile) {
   TRACE_EVENT0("gpu,startup", "DXVAVideoDecodeAccelerator::GetMinResolution");
-  std::pair<int, int> min_resolution;
+
+  // TODO(dalecurtis): These values are too low. We should only be using
+  // hardware decode for videos above ~360p, see http://crbug.com/684792.
+
   if (profile >= H264PROFILE_BASELINE && profile <= H264PROFILE_HIGH) {
     // Windows Media Foundation H.264 decoding does not support decoding videos
     // with any dimension smaller than 48 pixels:
     // http://msdn.microsoft.com/en-us/library/windows/desktop/dd797815
-    min_resolution = std::make_pair(48, 48);
-  } else {
-    // TODO(ananta)
-    // Detect this properly for VP8/VP9 profiles.
-    min_resolution = std::make_pair(16, 16);
+    return gfx::Size(48, 48);
   }
-  return min_resolution;
+
+  // TODO(dalecurtis): Detect this properly for VP8/VP9 profiles.
+  return gfx::Size(16, 16);
 }
 
 // static
-std::pair<int, int> DXVAVideoDecodeAccelerator::GetMaxResolution(
-    const VideoCodecProfile profile) {
+gfx::Size DXVAVideoDecodeAccelerator::GetMaxResolution(
+    VideoCodecProfile profile) {
   TRACE_EVENT0("gpu,startup", "DXVAVideoDecodeAccelerator::GetMaxResolution");
-  std::pair<int, int> max_resolution;
+
+  // Computes and caches the maximum resolution since it's expensive to
+  // determine and this function is called for every profile in
+  // kSupportedProfiles.
+
   if (profile >= H264PROFILE_BASELINE && profile <= H264PROFILE_HIGH) {
-    max_resolution = GetMaxH264Resolution();
-  } else {
-    // TODO(ananta)
-    // Detect this properly for VP8/VP9 profiles.
-    max_resolution = std::make_pair(4096, 2160);
+    const gfx::Size kDefaultMax = gfx::Size(1920, 1088);
+
+    // On Windows 7 the maximum resolution supported by media foundation is
+    // 1920 x 1088. We use 1088 to account for 16x16 macroblocks.
+    if (base::win::GetVersion() == base::win::VERSION_WIN7)
+      return kDefaultMax;
+
+    static const gfx::Size kCachedH264Resolution = GetMaxResolutionForGUIDs(
+        kDefaultMax, {DXVA2_ModeH264_E, DXVA2_Intel_ModeH264_E},
+        {gfx::Size(2560, 1440), gfx::Size(3840, 2160), gfx::Size(4096, 2160),
+         gfx::Size(4096, 2304), gfx::Size(7680, 4320)});
+    return kCachedH264Resolution;
   }
-  return max_resolution;
+
+  // Despite the name this is the GUID for VP8/VP9.
+  static const gfx::Size kCachedVPXResolution = GetMaxResolutionForGUIDs(
+      gfx::Size(4096, 2160), {D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0},
+      {gfx::Size(4096, 2304), gfx::Size(7680, 4320)});
+  return kCachedVPXResolution;
 }
 
-std::pair<int, int> DXVAVideoDecodeAccelerator::GetMaxH264Resolution() {
+gfx::Size DXVAVideoDecodeAccelerator::GetMaxResolutionForGUIDs(
+    const gfx::Size& default_max,
+    const std::vector<GUID>& valid_guids,
+    const std::vector<gfx::Size>& resolutions_to_test) {
   TRACE_EVENT0("gpu,startup",
-               "DXVAVideoDecodeAccelerator::GetMaxH264Resolution");
-  // The H.264 resolution detection operation is expensive. This static flag
-  // allows us to run the detection once.
-  static bool resolution_detected = false;
-  // Use 1088 to account for 16x16 macroblocks.
-  static std::pair<int, int> max_resolution = std::make_pair(1920, 1088);
-  if (resolution_detected)
-    return max_resolution;
-
-  resolution_detected = true;
-
-  // On Windows 7 the maximum resolution supported by media foundation is
-  // 1920 x 1088.
-  if (base::win::GetVersion() == base::win::VERSION_WIN7)
-    return max_resolution;
+               "DXVAVideoDecodeAccelerator::GetMaxResolutionForGUIDs");
+  gfx::Size max_resolution = default_max;
 
   // To detect if a driver supports the desired resolutions, we try and create
   // a DXVA decoder instance for that resolution and profile. If that succeeds
   // we assume that the driver supports H/W H.264 decoding for that resolution.
   HRESULT hr = E_FAIL;
   base::win::ScopedComPtr<ID3D11Device> device;
-
   {
     TRACE_EVENT0("gpu,startup",
-                 "GetMaxH264Resolution. QueryDeviceObjectFromANGLE");
+                 "GetMaxResolutionForGUIDs. QueryDeviceObjectFromANGLE");
 
     device = gl::QueryD3D11DeviceObjectFromANGLE();
-    if (!device.Get())
-      return max_resolution;
-  }
-
-  base::win::ScopedComPtr<ID3D11VideoDevice> video_device;
-  hr = device.CopyTo(IID_PPV_ARGS(&video_device));
-  if (FAILED(hr))
-    return max_resolution;
-
-  GUID decoder_guid = {};
-
-  {
-    TRACE_EVENT0("gpu,startup",
-                 "GetMaxH264Resolution. H.264 guid search begin");
-    // Enumerate supported video profiles and look for the H264 profile.
-    bool found = false;
-    UINT profile_count = video_device->GetVideoDecoderProfileCount();
-    for (UINT profile_idx = 0; profile_idx < profile_count; profile_idx++) {
-      GUID profile_id = {};
-      hr = video_device->GetVideoDecoderProfile(profile_idx, &profile_id);
-      if (SUCCEEDED(hr) && (profile_id == DXVA2_ModeH264_E ||
-                            profile_id == DXVA2_Intel_ModeH264_E)) {
-        decoder_guid = profile_id;
-        found = true;
-        break;
-      }
-    }
-    if (!found)
+    if (!device)
       return max_resolution;
   }
 
@@ -1361,25 +1338,38 @@
   if (IsLegacyGPU(device.Get()))
     return max_resolution;
 
-  // We look for the following resolutions in the driver.
-  // TODO(ananta)
-  // Look into whether this list needs to be expanded.
-  static std::pair<int, int> resolution_array[] = {
-      // Use 1088 to account for 16x16 macroblocks.
-      std::make_pair(1920, 1088), std::make_pair(2560, 1440),
-      std::make_pair(3840, 2160), std::make_pair(4096, 2160),
-      std::make_pair(4096, 2304),
-  };
+  base::win::ScopedComPtr<ID3D11VideoDevice> video_device;
+  hr = device.CopyTo(IID_PPV_ARGS(&video_device));
+  if (FAILED(hr))
+    return max_resolution;
+
+  GUID decoder_guid = GUID_NULL;
+  {
+    TRACE_EVENT0("gpu,startup", "GetMaxResolutionForGUIDs. GUID search begin");
+    // Enumerate supported video profiles and look for the H264 profile.
+    UINT profile_count = video_device->GetVideoDecoderProfileCount();
+    for (UINT profile_idx = 0; profile_idx < profile_count; profile_idx++) {
+      GUID profile_id = {};
+      hr = video_device->GetVideoDecoderProfile(profile_idx, &profile_id);
+      if (SUCCEEDED(hr) && (std::find(valid_guids.begin(), valid_guids.end(),
+                                      profile_id) != valid_guids.end())) {
+        decoder_guid = profile_id;
+        break;
+      }
+    }
+    if (decoder_guid == GUID_NULL)
+      return max_resolution;
+  }
 
   {
     TRACE_EVENT0("gpu,startup",
-                 "GetMaxH264Resolution. Resolution search begin");
+                 "GetMaxResolutionForGUIDs. Resolution search begin");
 
-    for (size_t res_idx = 0; res_idx < arraysize(resolution_array); res_idx++) {
+    for (auto& res : resolutions_to_test) {
       D3D11_VIDEO_DECODER_DESC desc = {};
       desc.Guid = decoder_guid;
-      desc.SampleWidth = resolution_array[res_idx].first;
-      desc.SampleHeight = resolution_array[res_idx].second;
+      desc.SampleWidth = res.width();
+      desc.SampleHeight = res.height();
       desc.OutputFormat = DXGI_FORMAT_NV12;
       UINT config_count = 0;
       hr = video_device->GetVideoDecoderConfigCount(&desc, &config_count);
@@ -1394,12 +1384,13 @@
       base::win::ScopedComPtr<ID3D11VideoDecoder> video_decoder;
       hr = video_device->CreateVideoDecoder(&desc, &config,
                                             video_decoder.GetAddressOf());
-      if (!video_decoder.Get())
+      if (!video_decoder)
         return max_resolution;
 
-      max_resolution = resolution_array[res_idx];
+      max_resolution = res;
     }
   }
+
   return max_resolution;
 }
 
diff --git a/media/gpu/dxva_video_decode_accelerator_win.h b/media/gpu/dxva_video_decode_accelerator_win.h
index dc02f98..0d31de58b 100644
--- a/media/gpu/dxva_video_decode_accelerator_win.h
+++ b/media/gpu/dxva_video_decode_accelerator_win.h
@@ -169,13 +169,20 @@
   };
 
   // Returns the minimum resolution for the |profile| passed in.
-  static std::pair<int, int> GetMinResolution(const VideoCodecProfile profile);
+  static gfx::Size GetMinResolution(VideoCodecProfile profile);
 
   // Returns the maximum resolution for the |profile| passed in.
-  static std::pair<int, int> GetMaxResolution(const VideoCodecProfile profile);
+  static gfx::Size GetMaxResolution(VideoCodecProfile profile);
 
-  // Returns the maximum resolution for H264 video.
-  static std::pair<int, int> GetMaxH264Resolution();
+  // Returns the maximum resolution for by attempting to create a decoder for
+  // each of the resolutions in |resolutions_to_test| for the first decoder
+  // matching a GUID from |valid_guids|. |resolutions_to_test| should be ordered
+  // from smallest to largest resolution. |default_max_resolution| will be
+  // returned if any errors occur during the process.
+  static gfx::Size GetMaxResolutionForGUIDs(
+      const gfx::Size& default_max_resolution,
+      const std::vector<GUID>& valid_guids,
+      const std::vector<gfx::Size>& resolutions_to_test);
 
   // Certain AMD GPU drivers like R600, R700, Evergreen and Cayman and
   // some second generation Intel GPU drivers crash if we create a video
diff --git a/remoting/ios/app/client_connection_view_controller.h b/remoting/ios/app/client_connection_view_controller.h
index cb5638b..efc5b56 100644
--- a/remoting/ios/app/client_connection_view_controller.h
+++ b/remoting/ios/app/client_connection_view_controller.h
@@ -7,6 +7,8 @@
 
 #import <UIKit/UIKit.h>
 
+@class HostInfo;
+
 // This enumerated the differnt modes this Client Connection View can be in.
 typedef NS_ENUM(NSInteger, ClientConnectionViewState) {
   ClientViewConnecting,
@@ -15,18 +17,6 @@
   ClientViewClosed,
 };
 
-// The host connection view controller delegate provides feedback for state
-// changes on Host Connection that the calling view should respond to.
-@protocol ClientConnectionViewControllerDelegate<NSObject>
-
-// Notifies the delegate the client is connected to the host.
-- (void)clientConnected;
-
-// Gets the current host name the client is attempting to connect to.
-- (NSString*)getConnectingHostName;
-
-@end
-
 // This is the view that shows the user feedback while the client connection is
 // being established. If requested the view can also display the pin entry view.
 // State communication for this view is handled by NSNotifications, it listens
@@ -35,13 +25,11 @@
 // work the same way if state is set directly.
 @interface ClientConnectionViewController : UIViewController
 
+- (instancetype)initWithHostInfo:(HostInfo*)hostInfo;
+
 // Setting state will change the view
 @property(nonatomic, assign) ClientConnectionViewState state;
 
-// This delegate is used to ask for Host Name and to notify when the connection
-// has been established.
-@property(weak, nonatomic) id<ClientConnectionViewControllerDelegate> delegate;
-
 @end
 
 #endif  // REMOTING_IOS_APP_CLIENT_CONNECTION_VIEW_CONTROLLER_H_
diff --git a/remoting/ios/app/client_connection_view_controller.mm b/remoting/ios/app/client_connection_view_controller.mm
index 395f3c60..31ad6d8 100644
--- a/remoting/ios/app/client_connection_view_controller.mm
+++ b/remoting/ios/app/client_connection_view_controller.mm
@@ -8,11 +8,16 @@
 
 #import "remoting/ios/app/client_connection_view_controller.h"
 
+#import "base/mac/bind_objc_block.h"
 #import "ios/third_party/material_components_ios/src/components/ActivityIndicator/src/MDCActivityIndicator.h"
 #import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
 #import "ios/third_party/material_components_ios/src/components/NavigationBar/src/MaterialNavigationBar.h"
+#import "remoting/ios/app/host_view_controller.h"
 #import "remoting/ios/app/pin_entry_view.h"
 #import "remoting/ios/domain/client_session_details.h"
+#import "remoting/ios/domain/host_info.h"
+#import "remoting/ios/facade/remoting_authentication.h"
+#import "remoting/ios/facade/remoting_service.h"
 #import "remoting/ios/session/remoting_client.h"
 
 #include "base/strings/sys_string_conversions.h"
@@ -40,17 +45,32 @@
   MDCNavigationBar* _navBar;
   PinEntryView* _pinEntryView;
   NSString* _remoteHostName;
+  RemotingClient* _client;
 }
 @end
 
 @implementation ClientConnectionViewController
 
 @synthesize state = _state;
-@synthesize delegate = _delegate;
 
-- (id)init {
+- (instancetype)initWithHostInfo:(HostInfo*)hostInfo {
   self = [super init];
   if (self) {
+    _client = [[RemotingClient alloc] init];
+
+    __weak RemotingClient* weakClient = _client;
+    [[RemotingService SharedInstance].authentication
+        callbackWithAccessToken:base::BindBlockArc(^(
+                                    remoting::OAuthTokenGetter::Status status,
+                                    const std::string& user_email,
+                                    const std::string& access_token) {
+          [weakClient connectToHost:hostInfo
+                           username:base::SysUTF8ToNSString(user_email)
+                        accessToken:base::SysUTF8ToNSString(access_token)];
+        })];
+
+    _remoteHostName = hostInfo.hostName;
+
     // TODO(yuweih): This logic may be reused by other views.
     UIButton* cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
     [cancelButton setTitle:@"CANCEL" forState:UIControlStateNormal];
@@ -82,8 +102,6 @@
           constraintEqualToAnchor:[self.view trailingAnchor]],
       [[_navBar heightAnchor] constraintEqualToConstant:kBarHeight],
     ]];
-
-    _remoteHostName = @"";
   }
   return self;
 }
@@ -187,6 +205,10 @@
   [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
+- (BOOL)prefersStatusBarHidden {
+  return YES;
+}
+
 #pragma mark - Keyboard
 
 - (void)keyboardWillShow:(NSNotification*)notification {
@@ -240,15 +262,6 @@
   }
 }
 
-- (void)setDelegate:(id<ClientConnectionViewControllerDelegate>)delegate {
-  _delegate = delegate;
-  if (_delegate) {
-    _remoteHostName = [_delegate getConnectingHostName];
-    // To get the view to use the new remote host name.
-    [self setState:_state];
-  }
-}
-
 #pragma mark - Private
 
 - (void)showConnectingState {
@@ -282,9 +295,19 @@
   _activityIndicator.cycleColors = @[ [UIColor greenColor] ];
   [_activityIndicator startAnimating];
   _activityIndicator.progress = 1.0;
-  [self dismissViewControllerAnimated:YES
+
+  HostViewController* hostViewController =
+      [[HostViewController alloc] initWithClient:_client];
+  _client = nil;
+
+  __weak UIViewController* parentController = self.presentingViewController;
+
+  [self dismissViewControllerAnimated:NO
                            completion:^{
-                             [_delegate clientConnected];
+                             [parentController
+                                 presentViewController:hostViewController
+                                              animated:NO
+                                            completion:nil];
                            }];
 }
 
@@ -299,12 +322,12 @@
 }
 
 - (void)didTapCancel:(id)sender {
-  NSLog(@"%@ was tapped.", NSStringFromClass([sender class]));
-  // TODO(nicholss): Need to cancel the pending connection.
+  _client = nil;
   [self dismissViewControllerAnimated:YES completion:nil];
 }
 
 - (void)hostSessionStatusChanged:(NSNotification*)notification {
+  NSLog(@"hostSessionStatusChanged: %@", [notification userInfo]);
   ClientConnectionViewState state;
   ClientSessionDetails* sessionDetails =
       [[notification userInfo] objectForKey:kSessionDetails];
diff --git a/remoting/ios/app/host_collection_view_controller.mm b/remoting/ios/app/host_collection_view_controller.mm
index 3d415c8..0fabb03 100644
--- a/remoting/ios/app/host_collection_view_controller.mm
+++ b/remoting/ios/app/host_collection_view_controller.mm
@@ -17,15 +17,6 @@
     @"remotingHostCollectionViewControllerItem";
 
 static CGFloat kHostCollectionViewControllerCellHeight = 70.f;
-static CGFloat kHostCollectionViewControllerDefaultHeaderHeight = 100.f;
-static CGFloat kHostCollectionViewControllerSmallHeaderHeight = 60.f;
-static UIColor* kBackgroundColor =
-    [UIColor colorWithRed:0.f green:0.67f blue:0.55f alpha:1.f];
-
-@interface HostCollectionViewController () {
-  MDCInkTouchController* _inkTouchController;
-}
-@end
 
 @implementation HostCollectionViewController
 
@@ -36,7 +27,7 @@
 - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout*)layout {
   self = [super initWithCollectionViewLayout:layout];
   if (self) {
-    self.collectionView.backgroundColor = [UIColor whiteColor];
+    self.collectionView.backgroundColor = [UIColor clearColor];
     [self.collectionView registerClass:[HostCollectionViewCell class]
             forCellWithReuseIdentifier:NSStringFromClass(
                                            [HostCollectionViewCell class])];
@@ -48,7 +39,7 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-  self.styler.cellStyle = MDCCollectionViewCellStyleCard;
+  self.styler.cellStyle = MDCCollectionViewCellStyleGrouped;
   self.styler.cellLayoutType = MDCCollectionViewCellLayoutTypeList;
 }
 
@@ -111,32 +102,14 @@
 
 #pragma mark - Private
 
-- (UIView*)hostsHeaderView {
-  CGRect headerFrame =
-      _flexHeaderContainerViewController.headerViewController.headerView.bounds;
-  UIView* hostsHeaderView = [[UIView alloc] initWithFrame:headerFrame];
-  hostsHeaderView.backgroundColor = kBackgroundColor;
-  hostsHeaderView.layer.masksToBounds = YES;
-  hostsHeaderView.autoresizingMask =
-      (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
-
-  _inkTouchController =
-      [[MDCInkTouchController alloc] initWithView:hostsHeaderView];
-  [_inkTouchController addInkView];
-
-  return hostsHeaderView;
-}
-
-- (void)setflexHeaderContainerViewController:
+- (void)setFlexHeaderContainerViewController:
     (MDCFlexibleHeaderContainerViewController*)
         flexHeaderContainerViewController {
   _flexHeaderContainerViewController = flexHeaderContainerViewController;
   MDCFlexibleHeaderView* headerView =
       _flexHeaderContainerViewController.headerViewController.headerView;
   headerView.trackingScrollView = self.collectionView;
-  headerView.maximumHeight = kHostCollectionViewControllerDefaultHeaderHeight;
-  headerView.minimumHeight = kHostCollectionViewControllerSmallHeaderHeight;
-  [headerView addSubview:[self hostsHeaderView]];
+  headerView.backgroundColor = [UIColor clearColor];
 
   // Use a custom shadow under the flexible header.
   MDCShadowLayer* shadowLayer = [MDCShadowLayer layer];
diff --git a/remoting/ios/app/remoting_view_controller.mm b/remoting/ios/app/remoting_view_controller.mm
index 443311a..73da201 100644
--- a/remoting/ios/app/remoting_view_controller.mm
+++ b/remoting/ios/app/remoting_view_controller.mm
@@ -20,7 +20,6 @@
 #import "remoting/ios/domain/client_session_details.h"
 #import "remoting/ios/facade/remoting_authentication.h"
 #import "remoting/ios/facade/remoting_service.h"
-#import "remoting/ios/session/remoting_client.h"
 
 #include "base/strings/sys_string_conversions.h"
 #include "remoting/base/oauth_token_getter.h"
@@ -28,8 +27,10 @@
 
 static CGFloat kHostInset = 5.f;
 
+static UIColor* kChromotingBlueBackground =
+    [UIColor colorWithRed:0.11f green:0.23f blue:0.66f alpha:1.f];
+
 @interface RemotingViewController ()<HostCollectionViewControllerDelegate,
-                                     ClientConnectionViewControllerDelegate,
                                      UIViewControllerAnimatedTransitioning,
                                      UIViewControllerTransitioningDelegate> {
   bool _isAuthenticated;
@@ -37,7 +38,6 @@
   MDCAppBar* _appBar;
   HostCollectionViewController* _collectionViewController;
   RemotingService* _remotingService;
-  RemotingClient* _client;
 }
 @end
 
@@ -69,7 +69,7 @@
     [self addChildViewController:_appBar.headerViewController];
 
     _appBar.headerViewController.headerView.backgroundColor =
-        [UIColor clearColor];
+        kChromotingBlueBackground;
     _appBar.navigationBar.tintColor = [UIColor whiteColor];
 
     UIBarButtonItem* menuButton =
@@ -85,12 +85,6 @@
                                         target:self
                                         action:@selector(didSelectRefresh)];
     self.navigationItem.rightBarButtonItem = refreshButton;
-
-    [[NSNotificationCenter defaultCenter]
-        addObserver:self
-           selector:@selector(hostSessionStatusChanged:)
-               name:kHostSessionStatusChanged
-             object:nil];
   }
   return self;
 }
@@ -99,6 +93,20 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
+
+  UIImage* image = [UIImage imageNamed:@"Background"];
+  UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
+  [self.view addSubview:imageView];
+  [self.view sendSubviewToBack:imageView];
+
+  imageView.translatesAutoresizingMaskIntoConstraints = NO;
+  [NSLayoutConstraint activateConstraints:@[
+    [[imageView widthAnchor]
+        constraintGreaterThanOrEqualToAnchor:[self.view widthAnchor]],
+    [[imageView heightAnchor]
+        constraintGreaterThanOrEqualToAnchor:[self.view heightAnchor]],
+  ]];
+
   [_appBar addSubviewsToParent];
 
   [[NSNotificationCenter defaultCenter]
@@ -135,18 +143,8 @@
   }
 }
 
-- (void)viewDidLayoutSubviews {
-  [super viewDidLayoutSubviews];
-
-  // Adjust the collection view's position and size so that it doesn't get
-  // overlayed by the navigation bar.
-  CGFloat collectionOffsetY =
-      _appBar.headerViewController.headerView.frame.size.height;
-  CGFloat collectionHeight = self.view.bounds.size.height - collectionOffsetY;
-  CGRect oldFrame = _collectionViewController.collectionView.frame;
-  _collectionViewController.collectionView.frame =
-      CGRectMake(oldFrame.origin.x, collectionOffsetY, oldFrame.size.width,
-                 collectionHeight);
+- (UIStatusBarStyle)preferredStatusBarStyle {
+  return UIStatusBarStyleLightContent;
 }
 
 #pragma mark - Remoting Service Notifications
@@ -183,22 +181,6 @@
   [_collectionViewController.collectionView reloadData];
 }
 
-#pragma mark - ClientConnectionViewControllerDelegate
-
-- (void)clientConnected {
-  HostViewController* hostViewController =
-      [[HostViewController alloc] initWithClient:_client];
-  _client = nil;
-  [self presentViewController:hostViewController animated:YES completion:nil];
-}
-
-- (NSString*)getConnectingHostName {
-  if (_client) {
-    return _client.hostInfo.hostName;
-  }
-  return nil;
-}
-
 #pragma mark - HostCollectionViewControllerDelegate
 
 - (void)didSelectCell:(HostCollectionViewCell*)cell
@@ -210,23 +192,8 @@
     return;
   }
 
-  _client = [[RemotingClient alloc] init];
-
-  [_remotingService.authentication
-      callbackWithAccessToken:base::BindBlockArc(^(
-                                  remoting::OAuthTokenGetter::Status status,
-                                  const std::string& user_email,
-                                  const std::string& access_token) {
-        // TODO(nicholss): Check status.
-        HostInfo* hostInfo = cell.hostInfo;
-        [_client connectToHost:hostInfo
-                      username:base::SysUTF8ToNSString(user_email)
-                   accessToken:base::SysUTF8ToNSString(access_token)];
-      })];
-
   ClientConnectionViewController* clientConnectionViewController =
-      [[ClientConnectionViewController alloc] init];
-  clientConnectionViewController.delegate = self;
+      [[ClientConnectionViewController alloc] initWithHostInfo:cell.hostInfo];
   [self presentViewController:clientConnectionViewController
                      animated:YES
                    completion:nil];
@@ -269,10 +236,6 @@
 
 #pragma mark - Private
 
-- (void)hostSessionStatusChanged:(NSNotification*)notification {
-  NSLog(@"hostSessionStatusChanged: %@", [notification userInfo]);
-}
-
 - (void)closeViewController {
   [self dismissViewControllerAnimated:true completion:nil];
 }
diff --git a/remoting/ios/app/resources/Assets.xcassets/Background.imageset/Contents.json b/remoting/ios/app/resources/Assets.xcassets/Background.imageset/Contents.json
new file mode 100644
index 0000000..486721f
--- /dev/null
+++ b/remoting/ios/app/resources/Assets.xcassets/Background.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "bkg1.jpg",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "bkg1_2x.jpg",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/remoting/ios/app/resources/Assets.xcassets/Background.imageset/bkg1.jpg b/remoting/ios/app/resources/Assets.xcassets/Background.imageset/bkg1.jpg
new file mode 100644
index 0000000..63b8d14
--- /dev/null
+++ b/remoting/ios/app/resources/Assets.xcassets/Background.imageset/bkg1.jpg
Binary files differ
diff --git a/remoting/ios/app/resources/Assets.xcassets/Background.imageset/bkg1_2x.jpg b/remoting/ios/app/resources/Assets.xcassets/Background.imageset/bkg1_2x.jpg
new file mode 100644
index 0000000..354fcac
--- /dev/null
+++ b/remoting/ios/app/resources/Assets.xcassets/Background.imageset/bkg1_2x.jpg
Binary files differ
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index 5b2cca7..0e4de01 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -249,7 +249,6 @@
     "transient_windows_unittest.cc",
     "user_activity_monitor_unittest.cc",
     "user_display_manager_unittest.cc",
-    "window_coordinate_conversions_unittest.cc",
     "window_finder_unittest.cc",
     "window_manager_state_unittest.cc",
     "window_tree_client_unittest.cc",
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc
index 5634221..751e562 100644
--- a/services/ui/ws/event_dispatcher.cc
+++ b/services/ui/ws/event_dispatcher.cc
@@ -525,9 +525,7 @@
 void EventDispatcher::DispatchToClient(ServerWindow* window,
                                        ClientSpecificId client_id,
                                        const ui::LocatedEvent& event) {
-  gfx::Point location(event.location());
-  gfx::Transform transform(GetTransformToWindow(window));
-  transform.TransformPoint(&location);
+  gfx::Point location = ConvertPointFromRoot(window, event.location());
   std::unique_ptr<ui::Event> clone = ui::Event::Clone(event);
   clone->AsLocatedEvent()->set_location(location);
   // TODO(jonross): add post-target accelerator support once accelerators
diff --git a/services/ui/ws/event_dispatcher_unittest.cc b/services/ui/ws/event_dispatcher_unittest.cc
index be3217a..7888872 100644
--- a/services/ui/ws/event_dispatcher_unittest.cc
+++ b/services/ui/ws/event_dispatcher_unittest.cc
@@ -1619,7 +1619,7 @@
     std::unique_ptr<DispatchedEventDetails> details =
         test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
     ASSERT_TRUE(details) << " details is nullptr " << i;
-    EXPECT_EQ(kTouchData[i].expected_target, details->window);
+    EXPECT_EQ(kTouchData[i].expected_target, details->window) << i;
 
     // Release touch.
     event_dispatcher()->ProcessEvent(
@@ -1882,6 +1882,36 @@
   EXPECT_EQ(nullptr, event_dispatcher()->mouse_cursor_source_window());
 }
 
+TEST_F(EventDispatcherTest, LocationHonorsTransform) {
+  std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3));
+
+  gfx::Transform transform;
+  transform.Scale(SkIntToMScalar(2), SkIntToMScalar(2));
+  child->SetTransform(transform);
+
+  root_window()->SetBounds(gfx::Rect(0, 0, 100, 100));
+  child->SetBounds(gfx::Rect(10, 10, 20, 20));
+
+  // Send event that is over child.
+  const ui::PointerEvent ui_event(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, gfx::Point(20, 25), gfx::Point(20, 25),
+      base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  event_dispatcher()->ProcessEvent(ui_event, 0,
+                                   EventDispatcher::AcceleratorMatchPhase::ANY);
+
+  std::unique_ptr<DispatchedEventDetails> details =
+      test_event_dispatcher_delegate()->GetAndAdvanceDispatchedEventDetails();
+  ASSERT_TRUE(details);
+  ASSERT_EQ(child.get(), details->window);
+
+  ASSERT_TRUE(details->event);
+  ASSERT_TRUE(details->event->IsPointerEvent());
+
+  ui::PointerEvent* dispatched_event = details->event->AsPointerEvent();
+  EXPECT_EQ(gfx::Point(20, 25), dispatched_event->root_location());
+  EXPECT_EQ(gfx::Point(5, 7), dispatched_event->location());
+}
+
 }  // namespace test
 }  // namespace ws
 }  // namespace ui
diff --git a/services/ui/ws/window_coordinate_conversions.cc b/services/ui/ws/window_coordinate_conversions.cc
index 11430a8a..e6bab98f4 100644
--- a/services/ui/ws/window_coordinate_conversions.cc
+++ b/services/ui/ws/window_coordinate_conversions.cc
@@ -6,69 +6,41 @@
 
 #include "services/ui/ws/server_window.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/point_f.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/vector2d.h"
-#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/transform.h"
 
 namespace ui {
-
 namespace ws {
-
 namespace {
 
-gfx::Vector2dF CalculateOffsetToAncestor(const ServerWindow* window,
-                                         const ServerWindow* ancestor) {
-  DCHECK(ancestor->Contains(window));
-  gfx::Vector2d result;
-  for (const ServerWindow* v = window; v != ancestor; v = v->parent())
-    result += v->bounds().OffsetFromOrigin();
-  return gfx::Vector2dF(result.x(), result.y());
+gfx::Transform GetTransformToRoot(const ServerWindow* window) {
+  // This code should only be called when |window| is connected to a display.
+  const ServerWindow* root = window->GetRoot();
+  DCHECK(root);
+
+  gfx::Transform transform;
+  const ServerWindow* w = window;
+  for (; w && w != root; w = w->parent()) {
+    gfx::Transform translation;
+    translation.Translate(static_cast<float>(w->bounds().x()),
+                          static_cast<float>(w->bounds().y()));
+    if (!w->transform().IsIdentity())
+      transform.ConcatTransform(w->transform());
+    transform.ConcatTransform(translation);
+  }
+  return transform;
 }
 
 }  // namespace
 
-gfx::Point ConvertPointBetweenWindows(const ServerWindow* from,
-                                      const ServerWindow* to,
-                                      const gfx::Point& point) {
-  return gfx::ToFlooredPoint(
-      ConvertPointFBetweenWindows(from, to, gfx::PointF(point.x(), point.y())));
-}
-
-gfx::PointF ConvertPointFBetweenWindows(const ServerWindow* from,
-                                        const ServerWindow* to,
-                                        const gfx::PointF& point) {
-  DCHECK(from);
-  DCHECK(to);
-  if (from == to)
-    return point;
-
-  if (from->Contains(to)) {
-    const gfx::Vector2dF offset(CalculateOffsetToAncestor(to, from));
-    return point - offset;
-  }
-  DCHECK(to->Contains(from));
-  const gfx::Vector2dF offset(CalculateOffsetToAncestor(from, to));
-  return point + offset;
-}
-
-gfx::Rect ConvertRectBetweenWindows(const ServerWindow* from,
-                                    const ServerWindow* to,
-                                    const gfx::Rect& rect) {
-  DCHECK(from);
-  DCHECK(to);
-  if (from == to)
-    return rect;
-
-  const gfx::Point top_left(
-      ConvertPointBetweenWindows(from, to, rect.origin()));
-  const gfx::Point bottom_right(gfx::ToCeiledPoint(ConvertPointFBetweenWindows(
-      from, to, gfx::PointF(rect.right(), rect.bottom()))));
-  return gfx::Rect(top_left.x(), top_left.y(), bottom_right.x() - top_left.x(),
-                   bottom_right.y() - top_left.y());
+gfx::Point ConvertPointFromRoot(const ServerWindow* window,
+                                const gfx::Point& location_in_root) {
+  const gfx::Transform transform = GetTransformToRoot(window);
+  gfx::Point3F location_in_root3(gfx::PointF{location_in_root});
+  transform.TransformPointReverse(&location_in_root3);
+  return gfx::ToFlooredPoint(location_in_root3.AsPointF());
 }
 
 }  // namespace ws
-
 }  // namespace ui
diff --git a/services/ui/ws/window_coordinate_conversions.h b/services/ui/ws/window_coordinate_conversions.h
index 577cb99..1c687c1 100644
--- a/services/ui/ws/window_coordinate_conversions.h
+++ b/services/ui/ws/window_coordinate_conversions.h
@@ -7,33 +7,18 @@
 
 namespace gfx {
 class Point;
-class PointF;
-class Rect;
 }
 
 namespace ui {
-
 namespace ws {
 
 class ServerWindow;
 
-// Converts |point| from the coordinates of |from| to the coordinates of |to|.
-// |from| and |to| must be an ancestors or descendants of each other.
-gfx::Point ConvertPointBetweenWindows(const ServerWindow* from,
-                                      const ServerWindow* to,
-                                      const gfx::Point& point);
-gfx::PointF ConvertPointFBetweenWindows(const ServerWindow* from,
-                                        const ServerWindow* to,
-                                        const gfx::PointF& point);
-
-// Converts |rect| from the coordinates of |from| to the coordinates of |to|.
-// |from| and |to| must be an ancestors or descendants of each other.
-gfx::Rect ConvertRectBetweenWindows(const ServerWindow* from,
-                                    const ServerWindow* to,
-                                    const gfx::Rect& rect);
+// Converts |point|, in the coordinates of the root, to that of |window|.
+gfx::Point ConvertPointFromRoot(const ServerWindow* window,
+                                const gfx::Point& point);
 
 }  // namespace ws
-
 }  // namespace ui
 
 #endif  // SERVICES_UI_WS_WINDOW_COORDINATE_CONVERSIONS_H_
diff --git a/services/ui/ws/window_coordinate_conversions_unittest.cc b/services/ui/ws/window_coordinate_conversions_unittest.cc
deleted file mode 100644
index 6cdebff..0000000
--- a/services/ui/ws/window_coordinate_conversions_unittest.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/ui/ws/window_coordinate_conversions.h"
-
-#include "services/ui/ws/server_window.h"
-#include "services/ui/ws/server_window_delegate.h"
-#include "services/ui/ws/test_server_window_delegate.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/gfx/geometry/point_f.h"
-#include "ui/gfx/geometry/rect.h"
-
-namespace ui {
-
-namespace ws {
-
-using WindowCoordinateConversionsTest = testing::Test;
-
-TEST_F(WindowCoordinateConversionsTest, ConvertRectBetweenWindows) {
-  TestServerWindowDelegate d1, d2, d3;
-  ServerWindow v1(&d1, WindowId()), v2(&d2, WindowId()), v3(&d3, WindowId());
-  v1.SetBounds(gfx::Rect(1, 2, 100, 100));
-  v2.SetBounds(gfx::Rect(3, 4, 100, 100));
-  v3.SetBounds(gfx::Rect(5, 6, 100, 100));
-  v1.Add(&v2);
-  v2.Add(&v3);
-
-  EXPECT_EQ(gfx::Rect(2, 1, 8, 9),
-            ConvertRectBetweenWindows(&v1, &v3, gfx::Rect(10, 11, 8, 9)));
-
-  EXPECT_EQ(gfx::Rect(18, 21, 8, 9),
-            ConvertRectBetweenWindows(&v3, &v1, gfx::Rect(10, 11, 8, 9)));
-}
-
-TEST_F(WindowCoordinateConversionsTest, ConvertPointFBetweenWindows) {
-  TestServerWindowDelegate d1, d2, d3;
-  ServerWindow v1(&d1, WindowId()), v2(&d2, WindowId()), v3(&d3, WindowId());
-  v1.SetBounds(gfx::Rect(1, 2, 100, 100));
-  v2.SetBounds(gfx::Rect(3, 4, 100, 100));
-  v3.SetBounds(gfx::Rect(5, 6, 100, 100));
-  v1.Add(&v2);
-  v2.Add(&v3);
-
-  {
-    const gfx::PointF result(
-        ConvertPointFBetweenWindows(&v1, &v3, gfx::PointF(10.5f, 11.9f)));
-    EXPECT_FLOAT_EQ(2.5f, result.x());
-    EXPECT_FLOAT_EQ(1.9f, result.y());
-  }
-
-  {
-    const gfx::PointF result(
-        ConvertPointFBetweenWindows(&v3, &v1, gfx::PointF(10.2f, 11.4f)));
-    EXPECT_FLOAT_EQ(18.2f, result.x());
-    EXPECT_FLOAT_EQ(21.4f, result.y());
-  }
-}
-
-}  // namespace ws
-
-}  // namespace ui
diff --git a/services/ui/ws/window_finder.cc b/services/ui/ws/window_finder.cc
index 6aa0657..fa2d95b 100644
--- a/services/ui/ws/window_finder.cc
+++ b/services/ui/ws/window_finder.cc
@@ -7,7 +7,6 @@
 #include "base/containers/adapters.h"
 #include "services/ui/ws/server_window.h"
 #include "services/ui/ws/server_window_delegate.h"
-#include "services/ui/ws/window_coordinate_conversions.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point3_f.h"
 #include "ui/gfx/geometry/point_f.h"
@@ -35,30 +34,28 @@
   return true;
 }
 
-gfx::Point ConvertPointFromParentToChild(const ServerWindow* child,
-                                         const gfx::Point& location_in_parent) {
-  if (child->transform().IsIdentity()) {
-    return gfx::Point(location_in_parent.x() - child->bounds().x(),
-                      location_in_parent.y() - child->bounds().y());
-  }
-
-  gfx::Transform transform = child->transform();
+gfx::Transform TransformFromParent(const ServerWindow* window,
+                                   const gfx::Transform& current_transform) {
+  gfx::Transform transform = current_transform;
+  if (!window->transform().IsIdentity())
+    transform.ConcatTransform(window->transform());
   gfx::Transform translation;
-  translation.Translate(static_cast<float>(child->bounds().x()),
-                        static_cast<float>(child->bounds().y()));
+  translation.Translate(static_cast<float>(window->bounds().x()),
+                        static_cast<float>(window->bounds().y()));
   transform.ConcatTransform(translation);
-  gfx::Point3F location_in_child3(gfx::PointF{location_in_parent});
-  transform.TransformPointReverse(&location_in_child3);
-  return gfx::ToFlooredPoint(location_in_child3.AsPointF());
+  return transform;
 }
 
-bool FindDeepestVisibleWindowForEventsImpl(ServerWindow* window,
-                                           const gfx::Point& location,
-                                           DeepestWindow* deepest_window) {
+bool FindDeepestVisibleWindowForEventsImpl(
+    ServerWindow* window,
+    const gfx::Point& location_in_root,
+    const gfx::Point& location_in_window,
+    const gfx::Transform& transform_from_parent,
+    DeepestWindow* deepest_window) {
   // The non-client area takes precedence over descendants, as otherwise the
   // user would likely not be able to hit the non-client area as it's common
   // for descendants to go into the non-client area.
-  if (IsLocationInNonclientArea(window, location)) {
+  if (IsLocationInNonclientArea(window, location_in_window)) {
     deepest_window->window = window;
     deepest_window->in_non_client_area = true;
     return true;
@@ -77,8 +74,12 @@
       if (!child->visible())
         continue;
 
-      gfx::Point location_in_child =
-          ConvertPointFromParentToChild(child, location);
+      const gfx::Transform child_transform =
+          TransformFromParent(child, transform_from_parent);
+      gfx::Point3F location_in_child3(gfx::PointF{location_in_root});
+      child_transform.TransformPointReverse(&location_in_child3);
+      const gfx::Point location_in_child =
+          gfx::ToFlooredPoint(location_in_child3.AsPointF());
       gfx::Rect child_bounds(child->bounds().size());
       child_bounds.Inset(-child->extended_hit_test_region().left(),
                          -child->extended_hit_test_region().top(),
@@ -90,8 +91,9 @@
         continue;
       }
 
-      if (FindDeepestVisibleWindowForEventsImpl(child, location_in_child,
-                                                deepest_window)) {
+      if (FindDeepestVisibleWindowForEventsImpl(
+              child, location_in_root, location_in_child, child_transform,
+              deepest_window)) {
         return true;
       }
     }
@@ -110,19 +112,10 @@
 DeepestWindow FindDeepestVisibleWindowForEvents(ServerWindow* root_window,
                                                 const gfx::Point& location) {
   DeepestWindow result;
-  FindDeepestVisibleWindowForEventsImpl(root_window, location, &result);
+  FindDeepestVisibleWindowForEventsImpl(root_window, location, location,
+                                        gfx::Transform(), &result);
   return result;
 }
 
-gfx::Transform GetTransformToWindow(ServerWindow* window) {
-  gfx::Transform transform;
-  ServerWindow* current = window;
-  while (current->parent()) {
-    transform.Translate(-current->bounds().x(), -current->bounds().y());
-    current = current->parent();
-  }
-  return transform;
-}
-
 }  // namespace ws
 }  // namespace ui
diff --git a/services/ui/ws/window_finder.h b/services/ui/ws/window_finder.h
index 8f203c4..9ea4d2bb 100644
--- a/services/ui/ws/window_finder.h
+++ b/services/ui/ws/window_finder.h
@@ -7,7 +7,6 @@
 
 namespace gfx {
 class Point;
-class Transform;
 }
 
 namespace ui {
@@ -27,10 +26,6 @@
 DeepestWindow FindDeepestVisibleWindowForEvents(ServerWindow* root_window,
                                                 const gfx::Point& location);
 
-// Retrieve the transform to the provided |window|'s coordinate space from the
-// root.
-gfx::Transform GetTransformToWindow(ServerWindow* window);
-
 }  // namespace ws
 }  // namespace ui
 
diff --git a/services/ui/ws/window_server.cc b/services/ui/ws/window_server.cc
index 1252adc..1031515 100644
--- a/services/ui/ws/window_server.cc
+++ b/services/ui/ws/window_server.cc
@@ -19,7 +19,6 @@
 #include "services/ui/ws/server_window.h"
 #include "services/ui/ws/server_window_compositor_frame_sink_manager.h"
 #include "services/ui/ws/user_activity_monitor.h"
-#include "services/ui/ws/window_coordinate_conversions.h"
 #include "services/ui/ws/window_manager_access_policy.h"
 #include "services/ui/ws/window_manager_display_root.h"
 #include "services/ui/ws/window_manager_state.h"
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 3bb22249..ff8be44 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -3519,11 +3519,23 @@
   "Linux Builder": {
     "additional_compile_targets": [
       "all"
+    ],
+    "scripts": [
+      {
+        "name": "check_gn_headers",
+        "script": "check_gn_headers.py"
+      }
     ]
   },
   "Linux Builder (dbg)": {
     "additional_compile_targets": [
       "all"
+    ],
+    "scripts": [
+      {
+        "name": "check_gn_headers",
+        "script": "check_gn_headers.py"
+      }
     ]
   },
   "Linux Tests": {
diff --git a/testing/scripts/check_gn_headers.py b/testing/scripts/check_gn_headers.py
new file mode 100755
index 0000000..7aa36a6a
--- /dev/null
+++ b/testing/scripts/check_gn_headers.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+import os
+import sys
+
+
+import common
+
+
+def main_run(args):
+  with common.temporary_file() as tempfile_path:
+    rc = common.run_command([
+        sys.executable,
+        os.path.join(common.SRC_DIR, 'build', 'check_gn_headers.py'),
+        '--out-dir',
+        os.path.join(args.paths['checkout'], 'out', args.build_config_fs),
+        '--whitelist',
+        os.path.join(common.SRC_DIR, 'build', 'check_gn_headers_whitelist.txt'),
+        '--json', tempfile_path
+    ], cwd=common.SRC_DIR)
+
+    with open(tempfile_path) as f:
+      failures = json.load(f)
+
+  json.dump({
+      'valid': True,
+      'failures': failures,
+  }, args.output)
+
+  return rc
+
+
+def main_compile_targets(args):
+  json.dump([], args.output)
+
+
+if __name__ == '__main__':
+  funcs = {
+    'run': main_run,
+    'compile_targets': main_compile_targets,
+  }
+  sys.exit(common.run_script(sys.argv[1:], funcs))
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5be5f25..8d0eed5e 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2841,21 +2841,6 @@
             ]
         }
     ],
-    "SeccompSandboxAndroid": [
-        {
-            "platforms": [
-                "android"
-            ],
-            "experiments": [
-                {
-                    "name": "SandboxEnabled",
-                    "enable_features": [
-                        "SeccompSandboxAndroid"
-                    ]
-                }
-            ]
-        }
-    ],
     "SecurityChip": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 5b8b6375..7ea2e39 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -22601,7 +22601,7 @@
 crbug.com/591099 virtual/display_list_2d_canvas/fast/canvas/webgl/webgl-texture-binding-preserved.html [ Failure ]
 crbug.com/591099 virtual/display_list_2d_canvas/fast/canvas/webgl/webgl-viewport-parameters-preserved.html [ Failure ]
 crbug.com/591099 virtual/display_list_2d_canvas/fast/canvas/zero-size-fill-rect.html [ Crash ]
-crbug.com/591099 virtual/enable_wasm/http/tests/wasm/wasm_local_iframe_test.html [ Crash Timeout ]
+crbug.com/591099 virtual/enable_wasm/external/wpt/wasm/wasm_local_iframe_test.html [ Crash Timeout ]
 crbug.com/591099 virtual/gpu/fast/canvas/2d.composite.globalAlpha.fillPath.html [ Crash ]
 crbug.com/591099 virtual/gpu/fast/canvas/2d.fillText.gradient.html [ Crash ]
 crbug.com/591099 virtual/gpu/fast/canvas/2d.text.draw.fill.maxWidth.gradient.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
index edc37dca3..0725edaf 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
@@ -3658,11 +3658,11 @@
 Bug(none) virtual/display_list_2d_canvas/fast/canvas/webgl/texImage-imageBitmap-from-blob.html [ Failure ]
 Bug(none) virtual/display_list_2d_canvas/fast/canvas/webgl/texImage-imageBitmap-from-imageBitmap-from-blob.html [ Failure ]
 Bug(none) virtual/enable_asmjs/http/tests/asmjs/asm-warnings.html [ Failure ]
-Bug(none) virtual/enable_wasm/http/tests/wasm/wasm_indexeddb_test.html [ Timeout ]
-Bug(none) virtual/enable_wasm/http/tests/wasm/wasm_local_iframe_test.html [ Failure ]
+Bug(none) virtual/enable_wasm/external/wpt/wasm/wasm_indexeddb_test.html [ Timeout ]
+Bug(none) virtual/enable_wasm/external/wpt/wasm/wasm_local_iframe_test.html [ Failure ]
 Bug(none) virtual/enable_wasm/http/tests/wasm/wasm_remote_postMessage_test.https.html [ Timeout ]
-Bug(none) virtual/enable_wasm/http/tests/wasm/wasm_serialization_tests.html [ Failure ]
-Bug(none) virtual/enable_wasm/http/tests/wasm/wasm_service_worker_test.html [ Failure ]
+Bug(none) virtual/enable_wasm/external/wpt/wasm/wasm_serialization_tests.html [ Failure ]
+Bug(none) virtual/enable_wasm/external/wpt/wasm/wasm_service_worker_test.html [ Failure ]
 Bug(none) virtual/enable_wasm_streaming/http/tests/wasm_streaming/wasm_response_apis.html [ Failure ]
 Bug(none) virtual/gpu/fast/canvas/canvas-composite-video-shadow.html [ Timeout ]
 Bug(none) virtual/gpu/fast/canvas/canvas-createImageBitmap-blob-in-workers.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests
index 422fcebd..ba6e67c 100644
--- a/third_party/WebKit/LayoutTests/NeverFixTests
+++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -213,6 +213,7 @@
 
 # wasm tests. Currently under virtual/enable_wasm or virtual/enable_wasm_streaming
 crbug.com/642912 http/tests/wasm/ [ WontFix ]
+crbug.com/642912 external/wpt/wasm/ [ WontFix ]
 crbug.com/642912 virtual/mojo-loading/http/tests/wasm/ [ WontFix ]
 crbug.com/712970 http/tests/wasm_streaming/ [ WontFix ]
 crbug.com/712970 virtual/mojo-loading/http/tests/wasm_streaming/ [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 14bcce1..883fb3e 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -372,6 +372,11 @@
     "args": ["--enable-features=WebAssembly"]
   },
   {
+    "prefix": "enable_wasm",
+    "base": "external/wpt/wasm",
+    "args": ["--enable-features=WebAssembly"]
+  },
+  {
     "prefix": "enable_wasm_streaming",
     "base": "http/tests/wasm_streaming",
     "args": ["--enable-features=WebAssemblyStreaming"]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-pluginarray.html b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-pluginarray.html
new file mode 100644
index 0000000..8798b263
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-pluginarray.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type="text/javascript">
+test(function () {
+  for (var i = 0; i < navigator.plugins.length; i++) {
+    var plugin = navigator.plugins[i];
+    var name = plugin.name;
+    assert_equals(plugin, navigator.plugins[i]);
+    assert_equals(plugin, navigator.plugins[name]);
+  }
+  for (var i = 0; i < navigator.mimeTypes.length; i++) {
+    var mime_type = navigator.mimeTypes[i];
+    var type = mime_type.type;
+    assert_equals(mime_type, navigator.mimeTypes[i]);
+    assert_equals(mime_type, navigator.mimeTypes[type]);
+    assert_equals(mime_type.enabledPlugin, navigator.plugins[mime_type.enabledPlugin.name]);
+  }
+}, "Tests that navigator.plugins and navigator.mimeTypes returns the same object when queried multiple times.");
+
+test(function () {
+  var iframe = document.createElement("iframe");
+  iframe.src = "about:blank";
+  document.body.appendChild(iframe);
+  assert_equals(navigator.plugins.length, iframe.contentWindow.navigator.plugins.length);
+  assert_equals(navigator.mimeTypes.length, iframe.contentWindow.navigator.mimeTypes.length);
+  for (var i = 0; i < navigator.plugins.length; i++) {
+    var plugin = navigator.plugins[i];
+    var name = plugin.name;
+    assert_not_equals(plugin, iframe.contentWindow.navigator.plugins[i]);
+    assert_not_equals(plugin, iframe.contentWindow.navigator.plugins[name]);
+  }
+  for (var i = 0; i < navigator.mimeTypes.length; i++) {
+    var mime_type = navigator.mimeTypes[i];
+    var type = mime_type.type;
+    assert_not_equals(mime_type, iframe.contentWindow.navigator.mimeTypes[i]);
+    assert_not_equals(mime_type, iframe.contentWindow.navigator.mimeTypes[type]);
+    assert_not_equals(mime_type.enabledPlugin, iframe.contentWindow.navigator.plugins[mime_type.enabledPlugin.name]);
+  }
+  iframe.remove();
+}, "Tests that navigator.plugins and navigator.mimeTypes does not return the same object on different frames.");
+
+test(function () {
+  for (var i = 1; i < navigator.plugins.length; i++) {
+    assert_less_than_equal(navigator.plugins[i-1].name.localeCompare(navigator.plugins[i].name), 0);
+  }
+  for (var i = 1; i < navigator.mimeTypes.length; i++) {
+    assert_less_than_equal(navigator.mimeTypes[i-1].type.localeCompare(navigator.mimeTypes[i].type), 0);
+  }
+}, "Tests that navigator.plugins and navigator.mimeTypes returns plugins sorted in alphabetical order by plugin name.");
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/compile_worker.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/compile_worker.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/compile_worker.js
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/compile_worker.js
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/incrementer.wasm b/third_party/WebKit/LayoutTests/external/wpt/wasm/incrementer.wasm
new file mode 100644
index 0000000..47afcde
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/incrementer.wasm
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/resources/blank.html b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/blank.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/resources/blank.html
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/resources/blank.html
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/resources/frame.html b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/frame.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/resources/frame.html
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/resources/frame.html
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/incrementer.wasm b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/incrementer.wasm
new file mode 100644
index 0000000..47afcde
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/incrementer.wasm
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/load_wasm.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/load_wasm.js
new file mode 100644
index 0000000..eca4b8d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/load_wasm.js
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function createWasmModule() {
+    // the file incrementer.wasm is copied from
+    // //v8/test/mjsunit/wasm. This is because currently we cannot
+    // reference files outside the LayoutTests folder. When wasm format
+    // changes require that file to be updated, there is a test on the
+    // v8 side (same folder), ensure-wasm-binaries-up-to-date.js, which
+    // fails and will require incrementer.wasm to be updated on that side.
+    return fetch('incrementer.wasm')
+        .then(response => {
+            if (!response.ok) throw new Error(response.statusText);
+            return response.arrayBuffer();
+        })
+        .then(WebAssembly.compile);
+}
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/resources/service-worker.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/resources/service-worker.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/resources/service-worker.js
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/resources/service-worker.js
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_indexeddb_test.html b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_indexeddb_test.html
similarity index 71%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/wasm_indexeddb_test.html
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_indexeddb_test.html
index 03d891c..ec91708 100644
--- a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_indexeddb_test.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_indexeddb_test.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <html>
 <head>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../resources/get-host-info.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
 <script src="resources/load_wasm.js"></script>
 <script src="wasm_indexeddb_test.js"></script>
 </head>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_indexeddb_test.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_indexeddb_test.js
new file mode 100644
index 0000000..301cbd2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_indexeddb_test.js
@@ -0,0 +1,81 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var db_name = 'db';
+var obj_store = 'store';
+var module_key = 'my_module';
+
+function createAndSaveToIndexedDB() {
+  return new Promise((resolve, reject) => {
+    createWasmModule()
+      .then(mod => {
+        var delete_request = indexedDB.deleteDatabase(db_name);
+        delete_request.onsuccess = function() {
+          var open_request = indexedDB.open(db_name);
+          open_request.onupgradeneeded = function() {
+            var db = open_request.result;
+            db.createObjectStore(obj_store);
+          };
+          open_request.onsuccess = function() {
+            var db = open_request.result;
+            var tx = db.transaction(obj_store, 'readwrite');
+            var store = tx.objectStore(obj_store);
+            try {
+              store.put(mod, module_key);
+            } catch(e) {
+              reject(e);
+              return;
+            }
+            tx.oncomplete = function() {
+              resolve();
+            };
+            tx.onabort = function() {
+              reject(transaction.error);
+            };
+          };
+        };
+      })
+      .catch(error => reject(error));
+  });
+}
+
+function loadFromIndexedDB(prev) {
+  return new Promise((resolve, reject) => {
+    prev.then(() => {
+      var open_request = indexedDB.open(db_name);
+      open_request.onsuccess = function() {
+        var db = open_request.result;
+        var tx = db.transaction(obj_store);
+        var store = tx.objectStore(obj_store);
+        var get_request = store.get(module_key);
+        get_request.onsuccess = function() {
+          var mod = get_request.result;
+          assert_true(mod instanceof WebAssembly.Module);
+          try {
+            var instance = new WebAssembly.Instance(mod);
+          } catch(e) {
+            reject(e);
+            return;
+          }
+          resolve(instance.exports.increment(1));
+        };
+      };
+    });
+  });
+}
+
+function TestIndexedDBLoadStoreSecure() {
+  return loadFromIndexedDB(createAndSaveToIndexedDB())
+    .then(res => assert_equals(res, 2),
+          error => assert_unreached(error));
+}
+
+function TestIndexedDBLoadStoreInsecure() {
+  return createAndSaveToIndexedDB()
+    .then(assert_unreached,
+          error => {
+            assert_true(error instanceof DOMException);
+            assert_equals(error.name, 'DataCloneError');
+          });
+}
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_local_iframe_test.html b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_local_iframe_test.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/wasm_local_iframe_test.html
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_local_iframe_test.html
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_serialization_tests.html b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_serialization_tests.html
similarity index 69%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/wasm_serialization_tests.html
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_serialization_tests.html
index 9e77140..0011095 100644
--- a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_serialization_tests.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_serialization_tests.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script src="resources/load_wasm.js"></script>
 <script src="wasm_serialization_tests.js"></script>
 <script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_serialization_tests.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_serialization_tests.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/wasm_serialization_tests.js
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_serialization_tests.js
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_serialization_worker.js b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_serialization_worker.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/wasm_serialization_worker.js
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_serialization_worker.js
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_service_worker_test.html b/third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_service_worker_test.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/wasm/wasm_service_worker_test.html
rename to third_party/WebKit/LayoutTests/external/wpt/wasm/wasm_service_worker_test.html
diff --git a/third_party/WebKit/LayoutTests/virtual/enable_wasm/external/wpt/wasm/README.txt b/third_party/WebKit/LayoutTests/virtual/enable_wasm/external/wpt/wasm/README.txt
new file mode 100644
index 0000000..5888cfc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/enable_wasm/external/wpt/wasm/README.txt
@@ -0,0 +1 @@
+Tests that depend on --enable-features=WebAssembly
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp
index f7477a8..26347fa5 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp
@@ -72,25 +72,30 @@
   return start_pos != end_pos;
 }
 
-// TODO(rlanday): make this not take O(n^2) time when all the markers are
-// removed
 bool DocumentMarkerListEditor::ShiftMarkersContentDependent(
     MarkerList* list,
     unsigned offset,
     unsigned old_length,
     unsigned new_length) {
-  bool did_shift_marker = false;
-  for (MarkerList::iterator it = list->begin(); it != list->end(); ++it) {
-    DocumentMarker& marker = **it;
+  // Find first marker that ends after the start of the region being edited.
+  // Markers before this one can be left untouched. This saves us some time over
+  // scanning the entire list linearly if the edit region is near the end of the
+  // text node.
+  const MarkerList::iterator& shift_range_begin =
+      std::upper_bound(list->begin(), list->end(), offset,
+                       [](size_t offset, const Member<DocumentMarker>& marker) {
+                         return offset < marker->EndOffset();
+                       });
 
-    // marked text is neither changed nor shifted
-    if (marker.EndOffset() <= offset)
-      continue;
+  MarkerList::iterator erase_range_end = shift_range_begin;
+
+  bool did_shift_marker = false;
+  for (MarkerList::iterator it = shift_range_begin; it != list->end(); ++it) {
+    DocumentMarker& marker = **it;
 
     // marked text is (potentially) changed by edit, remove marker
     if (marker.StartOffset() < offset + old_length) {
-      list->erase(it - list->begin());
-      --it;
+      erase_range_end = std::next(it);
       did_shift_marker = true;
       continue;
     }
@@ -100,24 +105,41 @@
     did_shift_marker = true;
   }
 
+  // Note: shift_range_begin could point at a marker being shifted instead of
+  // deleted, but if this is the case, we don't need to delete any markers, and
+  // erase() will get 0 for the length param
+  list->erase(shift_range_begin - list->begin(),
+              erase_range_end - shift_range_begin);
   return did_shift_marker;
 }
 
-// TODO(rlanday): make this not take O(n^2) time when all the markers are
-// removed
 bool DocumentMarkerListEditor::ShiftMarkersContentIndependent(
     MarkerList* list,
     unsigned offset,
     unsigned old_length,
     unsigned new_length) {
+  // Find first marker that ends after the start of the region being edited.
+  // Markers before this one can be left untouched. This saves us some time over
+  // scanning the entire list linearly if the edit region is near the end of the
+  // text node.
+  const MarkerList::iterator& shift_range_begin =
+      std::upper_bound(list->begin(), list->end(), offset,
+                       [](size_t offset, const Member<DocumentMarker>& marker) {
+                         return offset < marker->EndOffset();
+                       });
+
+  MarkerList::iterator erase_range_begin = list->end();
+  MarkerList::iterator erase_range_end = list->end();
+
   bool did_shift_marker = false;
-  for (MarkerList::iterator it = list->begin(); it != list->end(); ++it) {
+  for (MarkerList::iterator it = shift_range_begin; it != list->end(); ++it) {
     DocumentMarker& marker = **it;
     Optional<DocumentMarker::MarkerOffsets> result =
         marker.ComputeOffsetsAfterShift(offset, old_length, new_length);
     if (result == WTF::nullopt) {
-      list->erase(it - list->begin());
-      --it;
+      if (erase_range_begin == list->end())
+        erase_range_begin = it;
+      erase_range_end = std::next(it);
       did_shift_marker = true;
       continue;
     }
@@ -130,6 +152,8 @@
     }
   }
 
+  list->erase(erase_range_begin - list->begin(),
+              erase_range_end - erase_range_begin);
   return did_shift_marker;
 }
 
diff --git a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
index f7aebabe..cc62f80 100644
--- a/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/SpellCheckMarkerListImpl.cpp
@@ -19,27 +19,42 @@
     return;
   }
 
-  auto first_overlapping = std::lower_bound(
+  // Find first marker that ends after the one being inserted starts. If any
+  // markers overlap the one being inserted, this is the first one.
+  const auto& first_overlapping = std::lower_bound(
       markers_.begin(), markers_.end(), marker,
       [](const Member<DocumentMarker>& marker_in_list,
          const DocumentMarker* marker_to_insert) {
         return marker_in_list->EndOffset() < marker_to_insert->StartOffset();
       });
 
-  size_t index = first_overlapping - markers_.begin();
-  markers_.insert(index, marker);
-  const auto inserted = markers_.begin() + index;
-  first_overlapping = inserted + 1;
-  // TODO(rlanday): optimize this loop so it runs in O(N) time and not O(N^2)
-  for (const auto i = first_overlapping;
-       i != markers_.end() &&
-       (*i)->StartOffset() <= (*inserted)->EndOffset();) {
-    (*inserted)->SetStartOffset(
-        std::min((*inserted)->StartOffset(), (*i)->StartOffset()));
-    (*inserted)->SetEndOffset(
-        std::max((*inserted)->EndOffset(), (*i)->EndOffset()));
-    markers_.erase(i - markers_.begin());
+  // If this marker does not overlap the one being inserted, insert before it
+  // and we are done.
+  if (marker->EndOffset() < (*first_overlapping)->StartOffset()) {
+    markers_.insert(first_overlapping - markers_.begin(), marker);
+    return;
   }
+
+  // Otherwise, find the last overlapping marker, replace the first marker with
+  // the newly-inserted marker (to get the new description), set its start and
+  // end offsets to include all the overlapped markers, and erase the rest of
+  // the old markers.
+
+  const auto& last_overlapping = std::upper_bound(
+      first_overlapping, markers_.end(), marker,
+      [](const DocumentMarker* marker_to_insert,
+         const Member<DocumentMarker>& marker_in_list) {
+        return marker_to_insert->EndOffset() < marker_in_list->StartOffset();
+      });
+
+  marker->SetStartOffset(
+      std::min(marker->StartOffset(), (*first_overlapping)->StartOffset()));
+  marker->SetEndOffset(
+      std::max(marker->EndOffset(), (*(last_overlapping - 1))->EndOffset()));
+
+  *first_overlapping = marker;
+  size_t num_to_erase = last_overlapping - (first_overlapping + 1);
+  markers_.erase(first_overlapping + 1 - markers_.begin(), num_to_erase);
 }
 
 void SpellCheckMarkerListImpl::Clear() {
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
index 53e8f65b..75179486 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
@@ -350,6 +350,9 @@
   if (!node)
     return 0;
 
+  if (!node->IsElementNode() && !node->IsTextNode() && !node->IsDocumentNode())
+    return 0;  // Only documents, elements and text nodes get a11y objects
+
   if (AXObjectImpl* obj = Get(node))
     return obj;
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp
index 7ff8a43..0f395ca 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectImpl.cpp
@@ -821,11 +821,7 @@
 }
 
 bool AXObjectImpl::CanReceiveAccessibilityFocus() const {
-  const Node* node = this->GetNode();
-  if (!node)
-    return false;
-
-  const Element* elem = ToElement(node);
+  const Element* elem = GetElement();
   if (!elem)
     return false;
 
diff --git a/third_party/WebKit/Source/platform/scheduler/base/moveable_auto_lock.h b/third_party/WebKit/Source/platform/scheduler/base/moveable_auto_lock.h
index ac2b8a8..d7bcb9ff 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/moveable_auto_lock.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/moveable_auto_lock.h
@@ -16,7 +16,7 @@
     lock_.Acquire();
   }
 
-  explicit MoveableAutoLock(MoveableAutoLock&& other)
+  MoveableAutoLock(MoveableAutoLock&& other)
       : lock_(other.lock_), moved_(other.moved_) {
     lock_.AssertAcquired();
     other.moved_ = true;
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
index 97ad1c53..c8049df 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.cc
@@ -68,10 +68,10 @@
       this);
   selector_.SetTaskQueueSelectorObserver(this);
 
-  delayed_do_work_closure_ =
-      base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), true);
-  immediate_do_work_closure_ =
-      base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), false);
+  delayed_do_work_closure_ = base::BindRepeating(
+      &TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), true);
+  immediate_do_work_closure_ = base::BindRepeating(
+      &TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), false);
 
   // TODO(alexclarke): Change this to be a parameter that's passed in.
   RegisterTimeDomain(real_time_domain_.get());
@@ -208,7 +208,7 @@
 
 void TaskQueueManager::MaybeScheduleImmediateWorkLocked(
     const tracked_objects::Location& from_here,
-    MoveableAutoLock&& lock) {
+    MoveableAutoLock lock) {
   {
     MoveableAutoLock auto_lock(std::move(lock));
     // Unless we're nested, try to avoid posting redundant DoWorks.
@@ -368,7 +368,7 @@
 void TaskQueueManager::PostDoWorkContinuationLocked(
     base::Optional<NextTaskDelay> next_delay,
     LazyNow* lazy_now,
-    MoveableAutoLock&& lock) {
+    MoveableAutoLock lock) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
 
   {
diff --git a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
index 49be896..dd8e9a18 100644
--- a/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
+++ b/third_party/WebKit/Source/platform/scheduler/base/task_queue_manager.h
@@ -246,7 +246,7 @@
   // Post a DoWork continuation if |next_delay| is not empty.
   void PostDoWorkContinuationLocked(base::Optional<NextTaskDelay> next_delay,
                                     LazyNow* lazy_now,
-                                    MoveableAutoLock&& lock);
+                                    MoveableAutoLock lock);
 
   // Delayed Tasks with run_times <= Now() are enqueued onto the work queue and
   // reloads any empty work queues.
@@ -295,7 +295,7 @@
 
   void MaybeScheduleImmediateWorkLocked(
       const tracked_objects::Location& from_here,
-      MoveableAutoLock&& lock);
+      MoveableAutoLock lock);
 
   // Adds |queue| to |any_thread().has_incoming_immediate_work_| and if
   // |queue_is_blocked| is false it makes sure a DoWork is posted.
@@ -328,8 +328,8 @@
   scoped_refptr<TaskQueueManagerDelegate> delegate_;
   internal::TaskQueueSelector selector_;
 
-  base::Closure immediate_do_work_closure_;
-  base::Closure delayed_do_work_closure_;
+  base::RepeatingClosure immediate_do_work_closure_;
+  base::RepeatingClosure delayed_do_work_closure_;
   base::CancelableClosure cancelable_delayed_do_work_closure_;
 
   bool task_was_run_on_quiescence_monitored_queue_;
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
index 3752a4e9..35ab4247 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py
@@ -270,9 +270,7 @@
     def _test_input_for_file(self, test_file):
         return TestInput(test_file,
                          self._options.slow_time_out_ms if self._test_is_slow(test_file) else self._options.time_out_ms,
-                         self._test_requires_lock(test_file),
-                         should_add_missing_baselines=(self._options.new_test_results and
-                                                       not self._test_is_expected_missing(test_file)))
+                         self._test_requires_lock(test_file))
 
     def _test_requires_lock(self, test_file):
         """Return True if the test needs to be locked when running multiple
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
index f82824f..378b4e78 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/controllers/single_test_runner.py
@@ -57,8 +57,6 @@
 
 
 class SingleTestRunner(object):
-    (ALONGSIDE_TEST, PLATFORM_DIR, VERSION_DIR, UPDATE) = ('alongside', 'platform', 'version', 'update')
-
     def __init__(self, port, options, results_directory, worker_name,
                  primary_driver, secondary_driver, test_input, stop_when_done):
         self._port = port
@@ -73,7 +71,6 @@
         self._should_run_pixel_test = test_input.should_run_pixel_test
         self._should_run_pixel_test_first = test_input.should_run_pixel_test_first
         self._reference_files = test_input.reference_files
-        self._should_add_missing_baselines = test_input.should_add_missing_baselines
         self._stop_when_done = stop_when_done
 
         # If this is a virtual test that uses the default flags instead of the
@@ -105,7 +102,7 @@
                             self._port.expected_audio(self._test_name))
 
     def _should_fetch_expected_checksum(self):
-        return self._should_run_pixel_test and not (self._options.new_baseline or self._options.reset_results)
+        return self._should_run_pixel_test and not self._options.reset_results
 
     def _driver_input(self):
         # The image hash is used to avoid doing an image dump if the
@@ -166,8 +163,6 @@
         expected_driver_output = self._expected_driver_output()
 
         test_result = self._compare_output(expected_driver_output, driver_output)
-        if self._should_add_missing_baselines:
-            self._add_missing_baselines(test_result, driver_output)
         test_result_writer.write_test_result(self._filesystem, self._port, self._results_directory,
                                              self._test_name, driver_output, expected_driver_output, test_result.failures)
         return test_result
@@ -185,61 +180,32 @@
 
     _render_tree_dump_pattern = re.compile(r"^layer at \(\d+,\d+\) size \d+x\d+\n")
 
-    def _add_missing_baselines(self, test_result, driver_output):
-        missing_image = test_result.has_failure_matching_types(
-            test_failures.FailureMissingImage, test_failures.FailureMissingImageHash)
-        if test_result.has_failure_matching_types(test_failures.FailureMissingResult):
-            self._save_baseline_data(driver_output.text, '.txt', self._location_for_missing_baseline(driver_output.text, '.txt'))
-        if test_result.has_failure_matching_types(test_failures.FailureMissingAudio):
-            self._save_baseline_data(driver_output.audio, '.wav', self._location_for_missing_baseline(driver_output.audio, '.wav'))
-        if missing_image:
-            self._save_baseline_data(driver_output.image, '.png', self._location_for_missing_baseline(driver_output.image, '.png'))
-
-    def _location_for_missing_baseline(self, data, extension):
-        if self._options.add_platform_exceptions:
-            return self.VERSION_DIR
-        if extension == '.png':
-            return self.PLATFORM_DIR
-        if extension == '.wav':
-            return self.ALONGSIDE_TEST
-        if extension == '.txt' and self._render_tree_dump_pattern.match(data):
-            return self.PLATFORM_DIR
-        return self.ALONGSIDE_TEST
-
     def _update_or_add_new_baselines(self, driver_output):
-        location = self.VERSION_DIR if self._options.add_platform_exceptions else self.UPDATE
-        self._save_baseline_data(driver_output.text, '.txt', location)
-        self._save_baseline_data(driver_output.audio, '.wav', location)
+        self._save_baseline_data(driver_output.text, '.txt')
+        self._save_baseline_data(driver_output.audio, '.wav')
         if self._should_run_pixel_test:
-            self._save_baseline_data(driver_output.image, '.png', location)
+            self._save_baseline_data(driver_output.image, '.png')
 
-    def _save_baseline_data(self, data, extension, location):
+    def _save_baseline_data(self, data, extension):
         if data is None:
             return
         port = self._port
         fs = self._filesystem
 
-        if location == self.ALONGSIDE_TEST:
-            output_dir = fs.dirname(port.abspath_for_test(self._test_name))
-        elif location == self.VERSION_DIR:
+        if self._options.add_platform_exceptions:
             output_dir = fs.join(port.baseline_version_dir(), fs.dirname(self._test_name))
-        elif location == self.PLATFORM_DIR:
-            output_dir = fs.join(port.baseline_platform_dir(), fs.dirname(self._test_name))
-        elif location == self.UPDATE:
-            output_dir = fs.dirname(port.expected_filename(self._test_name, extension))
         else:
-            raise AssertionError('unrecognized baseline location: %s' % location)
+            output_dir = fs.dirname(port.expected_filename(self._test_name, extension))
 
         fs.maybe_make_directory(output_dir)
         output_basename = fs.basename(fs.splitext(self._test_name)[0] + '-expected' + extension)
         output_path = fs.join(output_dir, output_basename)
 
-        if location == self.VERSION_DIR:
-            fallback_path = port.expected_filename(self._test_name, extension)
-            if fallback_path != output_path and fs.sha1(fallback_path) == hashlib.sha1(data).hexdigest():
-                _log.info('Not writing new expected result "%s" because it is the same as "%s"',
-                          port.relative_test_filename(output_path), port.relative_test_filename(fallback_path))
-                return
+        current_expected_path = port.expected_filename(self._test_name, extension)
+        if fs.exists(current_expected_path) and fs.sha1(current_expected_path) == hashlib.sha1(data).hexdigest():
+            _log.info('Not writing new expected result "%s" because it is the same as the current expected result',
+                      port.relative_test_filename(output_path))
+            return
 
         _log.info('Writing new expected result "%s"', port.relative_test_filename(output_path))
         port.update_baseline(output_path, data)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_input.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_input.py
index a14f4ae..c08a82f 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_input.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/models/test_input.py
@@ -32,7 +32,7 @@
     """Groups information about a test for easy passing of data."""
 
     def __init__(self, test_name, timeout_ms=None, requires_lock=None, reference_files=None,
-                 should_run_pixel_test=None, should_add_missing_baselines=True):
+                 should_run_pixel_test=None):
         # TestInput objects are normally constructed by the manager and passed
         # to the workers, but these some fields are set lazily in the workers
         # where possible, because they require us to look at the filesystem,
@@ -42,16 +42,13 @@
         self.requires_lock = requires_lock
         self.reference_files = reference_files
         self.should_run_pixel_test = should_run_pixel_test
-        self.should_add_missing_baselines = should_add_missing_baselines
 
     def __repr__(self):
         return (
             "TestInput('%s', timeout_ms=%s, requires_lock=%s, "
-            'reference_files=%s, should_run_pixel_test=%s, '
-            'should_add_missing_baselines=%s)' % (
+            'reference_files=%s, should_run_pixel_test=%s)' % (
                 self.test_name,
                 self.timeout_ms,
                 self.requires_lock,
                 self.reference_files,
-                self.should_run_pixel_test,
-                self.should_add_missing_baselines))
+                self.should_run_pixel_test))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
index 37b0a644..df94420 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
@@ -80,6 +80,10 @@
         return exit_codes.UNEXPECTED_ERROR_EXIT_STATUS
 
 
+def deprecate(option, opt_str, _, parser):
+    parser.error('%s: %s' % (opt_str, option.help))
+
+
 def parse_args(args):
     option_group_definitions = []
 
@@ -174,16 +178,16 @@
                 help='Path to write the JSON test results for only *failing* tests.'),
             optparse.make_option(
                 '--new-baseline',
-                action='store_true',
-                default=False,
-                help=('Save generated results as new baselines into the *most-specific-platform* '
-                      "directory, overwriting whatever's already there. Equivalent to "
-                      '--reset-results --add-platform-exceptions')),
+                action='callback',
+                callback=deprecate,
+                help=('Deprecated. Use "webkit-patch rebaseline-cl" instead, or '
+                      '"--reset-results --add-platform-exceptions" if you do want to create '
+                      'platform-version-specific new baselines locally.')),
             optparse.make_option(
                 '--new-test-results',
-                action='store_true',
-                default=False,
-                help='Create new baselines when no expected results exist'),
+                action='callback',
+                callback=deprecate,
+                help='Deprecated. Use --reset-results instead.'),
             optparse.make_option(
                 '--no-show-results',
                 dest='show_results',
@@ -509,10 +513,6 @@
             additional_platform_directories.append(port.host.filesystem.abspath(path))
         options.additional_platform_directory = additional_platform_directories
 
-    if options.new_baseline:
-        options.reset_results = True
-        options.add_platform_exceptions = True
-
     if options.pixel_test_directories:
         options.pixel_tests = True
         verified_dirs = set()
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
index 9a81b7d8..aefcb5c 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests_unittest.py
@@ -47,13 +47,11 @@
 from webkitpy.layout_tests.port import test
 
 
-def parse_args(extra_args=None, tests_included=False, new_results=False):
+def parse_args(extra_args=None, tests_included=False):
     extra_args = extra_args or []
     args = []
     if not '--platform' in extra_args:
         args.extend(['--platform', 'test'])
-    if new_results:
-        args.append('--new-test-results')
 
     if not '--child-processes' in extra_args:
         args.extend(['--child-processes', 1])
@@ -83,10 +81,9 @@
     return run_details.exit_code == 0
 
 
-def logging_run(extra_args=None, port_obj=None, tests_included=False, host=None, new_results=False, shared_port=True):
+def logging_run(extra_args=None, port_obj=None, tests_included=False, host=None, shared_port=True):
     options, parsed_args = parse_args(extra_args=extra_args,
-                                      tests_included=tests_included,
-                                      new_results=new_results)
+                                      tests_included=tests_included)
     host = host or MockHost()
     if not port_obj:
         port_obj = host.port_factory.get(port_name=options.platform, options=options)
@@ -609,7 +606,7 @@
         host = MockHost()
 
         # Both tests have failing checksum. We include only the first in pixel tests so only that should fail.
-        args = ['--pixel-tests', '--retry-failures', '--pixel-test-directory', 'failures/unexpected/pixeldir',
+        args = ['--retry-failures', '--pixel-test-directory', 'failures/unexpected/pixeldir',
                 'failures/unexpected/pixeldir/image_in_pixeldir.html',
                 'failures/unexpected/image_not_in_pixeldir.html']
         details, _, _ = logging_run(extra_args=args, host=host, tests_included=True)
@@ -953,7 +950,7 @@
 
     def test_output_diffs(self):
         host = MockHost()
-        logging_run(['--pixel-tests', 'failures/unexpected/text-image-checksum.html'], tests_included=True, host=host)
+        logging_run(['failures/unexpected/text-image-checksum.html'], tests_included=True, host=host)
         written_files = host.filesystem.written_files
         self.assertTrue(any(path.endswith('-diff.txt') for path in written_files.keys()))
         self.assertTrue(any(path.endswith('-pretty-diff.html') for path in written_files.keys()))
@@ -1101,59 +1098,45 @@
     # supposed to be.
 
     def test_reset_results(self):
-        # Test that we update expectations in place. If the expectation
-        # is missing, update the expected generic location.
+        # Test that we update expectations in place.
         host = MockHost()
         details, err, _ = logging_run(
-            ['--pixel-tests', '--reset-results', 'passes/image.html'],
-            tests_included=True, host=host, new_results=True)
+            ['--reset-results', 'failures/unexpected/text-image-checksum.html'],
+            tests_included=True, host=host)
         file_list = host.filesystem.written_files.keys()
         self.assertEqual(details.exit_code, 0)
         self.assertEqual(len(file_list), 8)
-        self.assert_baselines(file_list, 'passes/image', ['.txt', '.png'], err)
+        self.assert_baselines(file_list, 'failures/unexpected/text-image-checksum', ['.txt', '.png'], err)
 
-    def test_missing_results(self):
-        # Test that we update expectations in place. If the expectation
-        # is missing, update the expected generic location.
+    def test_reset_missing_results(self):
+        # Test that we create new baselines at the generic location for missing expectations.
         host = MockHost()
-        details, err, _ = logging_run(['--no-show-results',
+        details, err, _ = logging_run(['--reset-results', '--no-show-results',
                                        'failures/unexpected/missing_text.html',
                                        'failures/unexpected/missing_image.html',
                                        'failures/unexpected/missing_render_tree_dump.html'],
-                                      tests_included=True, host=host, new_results=True)
-        file_list = host.filesystem.written_files.keys()
-        self.assertEqual(details.exit_code, 3)
-        self.assertEqual(len(file_list), 12)
-        self.assert_baselines(file_list, 'failures/unexpected/missing_text', ['.txt'], err)
-        self.assert_baselines(file_list, 'platform/test/failures/unexpected/missing_image', ['.png'], err)
-        self.assert_baselines(file_list, 'platform/test/failures/unexpected/missing_render_tree_dump', ['.txt'], err)
-
-    def test_new_baseline(self):
-        # Test that we update the platform expectations in the version-specific directories
-        # for both existing and new baselines.
-        host = MockHost()
-        details, err, _ = logging_run(
-            ['--pixel-tests', '--new-baseline', 'failures/unexpected/text-image-checksum.html'],
-            tests_included=True, host=host, new_results=True)
+                                      tests_included=True, host=host)
         file_list = host.filesystem.written_files.keys()
         self.assertEqual(details.exit_code, 0)
-        self.assertEqual(len(file_list), 8)
-        self.assert_baselines(file_list,
-                              'platform/test-mac-mac10.10/failures/unexpected/text-image-checksum',
-                              ['.txt', '.png'], err)
+        self.assertEqual(len(file_list), 9)
+        self.assert_baselines(file_list, 'failures/unexpected/missing_text', ['.txt'], err)
+        self.assert_baselines(file_list, 'failures/unexpected/missing_image', ['.png'], err)
+        self.assert_baselines(file_list, 'failures/unexpected/missing_render_tree_dump', ['.txt'], err)
 
-    def test_new_baseline_same_as_fallback(self):
+    def test_reset_platform_baseline(self):
         # Test that we update the platform expectations in the version-specific directories
         # if the new baseline is different from the fallback baseline.
         host = MockHost()
         host.filesystem.write_text_file(
             test.LAYOUT_TEST_DIR + '/failures/unexpected/text-image-checksum-expected.txt',
-            # Make the fallback baseline the same as the actual result of the test.
-            # The value is the same as actual_result of the test defined in test.py.
+            # Make the current text expectation the same as the actual text result of the test.
+            # The value is the same as actual_result of the test defined in
+            # webkitpy.layout_tests.port.test.
             'text-image-checksum_fail-txt')
         details, err, _ = logging_run(
-            ['--pixel-tests', '--new-baseline', 'failures/unexpected/text-image-checksum.html'],
-            tests_included=True, host=host, new_results=True)
+            ['--reset-results', '--add-platform-exceptions',
+             'failures/unexpected/text-image-checksum.html'],
+            tests_included=True, host=host)
         file_list = host.filesystem.written_files.keys()
         self.assertEqual(details.exit_code, 0)
         self.assertEqual(len(file_list), 8)
@@ -1179,24 +1162,6 @@
         self.assertEqual(len(file_list), 6)
         self.assert_baselines(file_list, 'passes/reftest', ['.txt'], err)
 
-    def test_reftest_new_baseline(self):
-        # Test rebaseline of reftests.
-        # Should ignore reftests without text expectations.
-        host = MockHost()
-        details, err, _ = logging_run(['--new-baseline', 'passes/reftest.html'], tests_included=True, host=host)
-        file_list = host.filesystem.written_files.keys()
-        self.assertEqual(details.exit_code, 0)
-        self.assertEqual(len(file_list), 6)
-        self.assert_baselines(file_list, '', [], err)
-
-        host.filesystem.write_text_file(test.LAYOUT_TEST_DIR + '/passes/reftest-expected.txt', '')
-        host.filesystem.clear_written_files()
-        details, err, _ = logging_run(['--new-baseline', 'passes/reftest.html'], tests_included=True, host=host)
-        file_list = host.filesystem.written_files.keys()
-        self.assertEqual(details.exit_code, 0)
-        self.assertEqual(len(file_list), 6)
-        self.assert_baselines(file_list, 'platform/test-mac-mac10.10/passes/reftest', ['.txt'], err)
-
 
 class PortTest(unittest.TestCase):
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
index 836ceb5..748a78d 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing.py
@@ -81,10 +81,6 @@
         if self._options.order == 'random':
             self._print_default('Using random order with seed: %d' % self._options.seed)
 
-        # FIXME: should these options be in printing_options?
-        if self._options.new_baseline:
-            self._print_default('Placing new baselines in %s' % self._port.baseline_version_dir())
-
         fs = self._port.host.filesystem
         fallback_path = [fs.split(x)[1] for x in self._port.baseline_search_path()]
         self._print_default('Baseline search path: %s -> generic' % ' -> '.join(fallback_path))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
index 233e2ec..8b33f9dd 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/views/printing_unittest.py
@@ -122,7 +122,6 @@
         # FIXME: Make it so these options don't have to be set directly.
         # pylint: disable=protected-access
         printer._options.pixel_tests = True
-        printer._options.new_baseline = True
         printer._options.time_out_ms = 6000
         printer._options.slow_time_out_ms = 12000
         printer._options.order = 'random'
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7a56084..eaae8b92 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -378,6 +378,9 @@
   <int value="5" label="Write to minidump failed"/>
   <int value="6" label="Debug file deletion failed"/>
   <int value="7" label="Finished writing crash report failed"/>
+  <int value="8" label="Unclean shutdown"/>
+  <int value="9" label="Unclean session"/>
+  <int value="10" label="Collection attempt"/>
 </enum>
 
 <enum name="ActivityTrackerRecordEvent" type="int">
@@ -2969,11 +2972,6 @@
   <int value="1" label="Has worked at least once"/>
 </enum>
 
-<enum name="BooleanExists" type="int">
-  <int value="0" label="Does not exist"/>
-  <int value="1" label="Exists"/>
-</enum>
-
 <enum name="BooleanExpired" type="int">
   <int value="0" label="Unexpired"/>
   <int value="1" label="Expired"/>
@@ -21953,6 +21951,7 @@
   <int value="-1746767834" label="ssl-interstitial-v2-gray"/>
   <int value="-1740519217" label="disable-software-rasterizer"/>
   <int value="-1735643253" label="enable-display-list-2d-canvas"/>
+  <int value="-1734254845" label="ash-enable-night-light"/>
   <int value="-1732561795" label="ConsistentOmniboxGeolocation:enabled"/>
   <int value="-1729926412" label="enable-webusb-notifications"/>
   <int value="-1725507605" label="enable-web-midi"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 5c2f4eb9..3c69344 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -424,6 +424,9 @@
 </histogram>
 
 <histogram name="ActivityTracker.Collect.UncleanShutdownCount" units="count">
+  <obsolete>
+    Deprecated 05/2017 in favor of ActivityTracker.Collect.Status.
+  </obsolete>
   <owner>manzagop@chromium.org</owner>
   <summary>
     Number of unclean shutdowns, as derived from the stability instrumentation.
@@ -432,6 +435,9 @@
 </histogram>
 
 <histogram name="ActivityTracker.Collect.UncleanSystemCount" units="count">
+  <obsolete>
+    Deprecated 05/2017 in favor of ActivityTracker.Collect.Status.
+  </obsolete>
   <owner>manzagop@chromium.org</owner>
   <summary>
     Number of unclean shutdowns that can potentially be attributed to system
@@ -62376,14 +62382,6 @@
   </summary>
 </histogram>
 
-<histogram name="SafeBrowsing.V4UnusedStoreFileExists" enum="BooleanExists">
-  <owner>vakh@chromium.org</owner>
-  <summary>
-    Track the presence of store files that were previously created but have been
-    deprecated since so need to be removed from disk. Logged once per startup.
-  </summary>
-</histogram>
-
 <histogram name="SafeBrowsing.V4Update.Network.Result"
     enum="CombinedHttpResponseAndNetErrorCode">
   <owner>vakh@chromium.org</owner>
@@ -93497,7 +93495,6 @@
       name="SafeBrowsing.V4ReadFromDisk.DecodeAdditions.Result"/>
   <affected-histogram name="SafeBrowsing.V4ReadFromDisk.DecodeAdditions.Time"/>
   <affected-histogram name="SafeBrowsing.V4ReadFromDisk.MergeUpdate.Time"/>
-  <affected-histogram name="SafeBrowsing.V4UnusedStoreFileExists"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="SafeBrowsingLists" separator=".">
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py
index a985c75..f2d02c4 100644
--- a/tools/perf/benchmarks/media.py
+++ b/tools/perf/benchmarks/media.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import re
+
 from core import perf_benchmark
 
 from telemetry import benchmark
@@ -16,6 +18,12 @@
 import page_sets
 
 
+# See tr.v.Numeric.getSummarizedScalarNumericsWithNames()
+# https://github.com/catapult-project/catapult/blob/master/tracing/tracing/value/numeric.html#L323
+_IGNORED_STATS_RE = re.compile(
+    r'(?<!dump)(?<!process)_(std|count|max|min|sum|pct_\d{4}(_\d+)?)$')
+
+
 class _MSEMeasurement(legacy_page_test.LegacyPageTest):
 
   def __init__(self):
@@ -116,6 +124,12 @@
   def Name(cls):
     return 'media.tough_video_cases_tbmv2'
 
+  @classmethod
+  def ValueCanBeAddedPredicate(cls, value, is_first_result):
+    # TODO(crbug.com/610962): Remove this stopgap when the perf dashboard
+    # is able to cope with the data load generated by TBMv2 metrics.
+    return not _IGNORED_STATS_RE.search(value.name)
+
 
 @benchmark.Owner(emails=['johnchen@chromium.org', 'crouleau@chromium.org'],
                  component='Internals>Media')
@@ -149,6 +163,12 @@
         ['--ignore-autoplay-restrictions',
          '--disable-gesture-requirement-for-media-playback'])
 
+  @classmethod
+  def ValueCanBeAddedPredicate(cls, value, is_first_result):
+    # TODO(crbug.com/610962): Remove this stopgap when the perf dashboard
+    # is able to cope with the data load generated by TBMv2 metrics.
+    return not _IGNORED_STATS_RE.search(value.name)
+
 
 @benchmark.Disabled('all')  # crbug/676345
 @benchmark.Owner(emails=['crouleau@chromium.org', 'videostack-eng@google.com'],
diff --git a/tools/perf/benchmarks/octane.py b/tools/perf/benchmarks/octane.py
index 6245c334..117c0fe 100644
--- a/tools/perf/benchmarks/octane.py
+++ b/tools/perf/benchmarks/octane.py
@@ -148,8 +148,10 @@
     ps = story.StorySet(
         archive_data_file='../page_sets/data/octane.json',
         base_dir=os.path.dirname(os.path.abspath(__file__)),
-        cloud_storage_bucket=story.PUBLIC_BUCKET)
+        cloud_storage_bucket=story.PUBLIC_BUCKET,
+        verify_names=True)
     ps.AddStory(page_module.Page(
         'http://chromium.github.io/octane/index.html?auto=1',
-        ps, ps.base_dir, make_javascript_deterministic=False))
+        ps, ps.base_dir, make_javascript_deterministic=False,
+        name='http://chromium.github.io/octane/index.html?auto=1'))
     return ps
diff --git a/tools/perf/page_sets/blink_memory_mobile.py b/tools/perf/page_sets/blink_memory_mobile.py
index e6c53c94..68d9766 100644
--- a/tools/perf/page_sets/blink_memory_mobile.py
+++ b/tools/perf/page_sets/blink_memory_mobile.py
@@ -92,7 +92,8 @@
   def __init__(self):
     super(BlinkMemoryMobilePageSet, self).__init__(
         archive_data_file='data/blink_memory_mobile.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
+        cloud_storage_bucket=story.PARTNER_BUCKET,
+        verify_names=True)
 
     # Why: High rate of Blink's memory consumption rate.
     self.AddStory(BlinkMemoryMobilePage(
diff --git a/tools/perf/page_sets/google_pages.py b/tools/perf/page_sets/google_pages.py
index 6fb0ef4..f20394d 100644
--- a/tools/perf/page_sets/google_pages.py
+++ b/tools/perf/page_sets/google_pages.py
@@ -31,7 +31,8 @@
     super(GmailPage, self).__init__(
         url='https://mail.google.com/mail/',
         page_set=page_set,
-        shared_page_state_class=shared_page_state_class)
+        shared_page_state_class=shared_page_state_class,
+        name='https://mail.google.com/mail/')
 
   def RunNavigateSteps(self, action_runner):
     google_login.LoginGoogleAccount(action_runner, 'google',
diff --git a/tools/perf/page_sets/long_running_idle_google_cases.py b/tools/perf/page_sets/long_running_idle_google_cases.py
index 302cddf..559e542 100644
--- a/tools/perf/page_sets/long_running_idle_google_cases.py
+++ b/tools/perf/page_sets/long_running_idle_google_cases.py
@@ -35,7 +35,8 @@
   def __init__(self):
     super(LongRunningIdleGmailPageSet, self).__init__(
         archive_data_file='data/long_running_idle_gmail_page.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
+        cloud_storage_bucket=story.PARTNER_BUCKET,
+        verify_names=True)
     self.AddStory(
         _CreateIdlePageClass(google_pages.GmailPage)(self))
 
@@ -45,6 +46,7 @@
     # Reuse the wpr of foreground gmail.
     super(LongRunningIdleGmailBackgroundPageSet, self).__init__(
         archive_data_file='data/long_running_idle_gmail_page.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
+        cloud_storage_bucket=story.PARTNER_BUCKET,
+        verify_names=True)
     self.AddStory(
         _CreateIdleBackgroundPageClass(google_pages.GmailPage)(self))
diff --git a/tools/perf/page_sets/memory_top_10_mobile.py b/tools/perf/page_sets/memory_top_10_mobile.py
index ceab901..7833d30 100644
--- a/tools/perf/page_sets/memory_top_10_mobile.py
+++ b/tools/perf/page_sets/memory_top_10_mobile.py
@@ -68,7 +68,8 @@
   def __init__(self):
     super(MemoryTop10Mobile, self).__init__(
         archive_data_file='data/memory_top_10_mobile.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
+        cloud_storage_bucket=story.PARTNER_BUCKET,
+        verify_names=True)
 
     for url in top_10_mobile.URL_LIST:
       # We name pages so their foreground/background counterparts are easy
diff --git a/tools/perf/page_sets/oopif_basic_page_set.py b/tools/perf/page_sets/oopif_basic_page_set.py
index 854b07a9..9276096 100644
--- a/tools/perf/page_sets/oopif_basic_page_set.py
+++ b/tools/perf/page_sets/oopif_basic_page_set.py
@@ -13,7 +13,8 @@
   def __init__(self, cache_temperatures=None):
     super(OopifBasicPageSet, self).__init__(
         archive_data_file='data/oopif_basic.json',
-        cloud_storage_bucket=story.PARTNER_BUCKET)
+        cloud_storage_bucket=story.PARTNER_BUCKET,
+        verify_names=True)
     if cache_temperatures is None:
       cache_temperatures = [cache_temperature_module.ANY]
 
@@ -36,4 +37,4 @@
 
     for url in urls:
       for temp in cache_temperatures:
-        self.AddStory(page.Page(url, self, cache_temperature=temp))
+        self.AddStory(page.Page(url, self, cache_temperature=temp, name=url))
diff --git a/tools/perf/page_sets/oortonline.py b/tools/perf/page_sets/oortonline.py
index 6e1f6e8..312f580 100644
--- a/tools/perf/page_sets/oortonline.py
+++ b/tools/perf/page_sets/oortonline.py
@@ -24,7 +24,8 @@
         url='http://oortonline.gl/#run', page_set=page_set,
         shared_page_state_class=(
             webgl_supported_shared_state.WebGLSupportedSharedState),
-        make_javascript_deterministic=False)
+        make_javascript_deterministic=False,
+        name='http://oortonline.gl/#run')
     self.archive_data_file = 'data/oortonline.json'
     self.script_to_evaluate_on_commit = STARTUP_SCRIPT
 
@@ -42,7 +43,8 @@
   def __init__(self):
     super(OortOnlinePageSet, self).__init__(
       archive_data_file='data/oortonline.json',
-      cloud_storage_bucket=story.PARTNER_BUCKET)
+      cloud_storage_bucket=story.PARTNER_BUCKET,
+      verify_names=True)
     self.AddStory(OortOnlinePage(self))
 
 class OortOnlineTBMPage(OortOnlinePage):
@@ -76,5 +78,6 @@
   def __init__(self):
     super(OortOnlineTBMPageSet, self).__init__(
       archive_data_file='data/oortonline.json',
-      cloud_storage_bucket=story.PARTNER_BUCKET)
+      cloud_storage_bucket=story.PARTNER_BUCKET,
+      verify_names=True)
     self.AddStory(OortOnlineTBMPage(self))
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index e550ddb..f47ba8a4d 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -5,6 +5,7 @@
 #include "ui/aura/mus/window_port_mus.h"
 
 #include "base/memory/ptr_util.h"
+#include "components/viz/client/local_surface_id_provider.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/mus/client_surface_embedder.h"
@@ -109,7 +110,9 @@
       std::move(context_provider), nullptr /* worker_context_provider */,
       gpu_memory_buffer_manager, nullptr /* shared_bitmap_manager */,
       nullptr /* synthetic_begin_frame_source */, std::move(sink_info),
-      std::move(client_request), enable_surface_synchronization);
+      std::move(client_request),
+      base::MakeUnique<viz::DefaultLocalSurfaceIdProvider>(),
+      enable_surface_synchronization);
   window_tree_client_->AttachCompositorFrameSink(
       server_id(), std::move(sink_request), std::move(client));
   return std::move(compositor_frame_sink);
diff --git a/ui/gfx/codec/jpeg_codec.cc b/ui/gfx/codec/jpeg_codec.cc
index 6d926378b..3dc5ad62 100644
--- a/ui/gfx/codec/jpeg_codec.cc
+++ b/ui/gfx/codec/jpeg_codec.cc
@@ -43,17 +43,6 @@
 
 }  // namespace
 
-// This method helps identify at run time which library chromium is using.
-JPEGCodec::LibraryVariant JPEGCodec::JpegLibraryVariant() {
-#if defined(USE_SYSTEM_LIBJPEG)
-  return SYSTEM_LIBJPEG;
-#elif defined(USE_LIBJPEG_TURBO)
-  return LIBJPEG_TURBO;
-#else
-  return IJG_LIBJPEG;
-#endif
-}
-
 // Encoder ---------------------------------------------------------------------
 //
 // This code is based on nsJPEGEncoder from Mozilla.
@@ -144,33 +133,6 @@
   state->out->resize(state->image_buffer_used);
 }
 
-#if !defined(JCS_EXTENSIONS)
-// Converts RGBA to RGB (removing the alpha values) to prepare to send data to
-// libjpeg. This converts one row of data in rgba with the given width in
-// pixels the the given rgb destination buffer (which should have enough space
-// reserved for the final data).
-void StripAlpha(const unsigned char* rgba, int pixel_width, unsigned char* rgb)
-{
-  for (int x = 0; x < pixel_width; x++)
-    memcpy(&rgb[x * 3], &rgba[x * 4], 3);
-}
-
-// Converts BGRA to RGB by reordering the color components and dropping the
-// alpha. This converts  one row of data in rgba with the given width in
-// pixels the the given rgb destination buffer (which should have enough space
-// reserved for the final data).
-void BGRAtoRGB(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
-{
-  for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &bgra[x * 4];
-    unsigned char* pixel_out = &rgb[x * 3];
-    pixel_out[0] = pixel_in[2];
-    pixel_out[1] = pixel_in[1];
-    pixel_out[2] = pixel_in[0];
-  }
-}
-#endif  // !defined(JCS_EXTENSIONS)
-
 // This class destroys the given jpeg_compress object when it goes out of
 // scope. It simplifies the error handling in Encode (and even applies to the
 // success case).
@@ -204,9 +166,6 @@
   CompressDestroyer destroyer;
   destroyer.SetManagedObject(&cinfo);
   output->clear();
-#if !defined(JCS_EXTENSIONS)
-  unsigned char* row_buffer = NULL;
-#endif
 
   // We set up the normal JPEG error routines, then override error_exit.
   // This must be done before the call to create_compress.
@@ -222,9 +181,6 @@
     // goto using a call to longjmp."  So we delete the CompressDestroyer's
     // object manually instead.
     destroyer.DestroyManagedObject();
-#if !defined(JCS_EXTENSIONS)
-    delete[] row_buffer;
-#endif
     return false;
   }
 
@@ -233,22 +189,16 @@
 
   cinfo.image_width = w;
   cinfo.image_height = h;
-  cinfo.input_components = 3;
-#ifdef JCS_EXTENSIONS
+  cinfo.input_components = 4;
   // Choose an input colorspace and return if it is an unsupported one. Since
   // libjpeg-turbo supports all input formats used by Chromium (i.e. RGB, RGBA,
   // and BGRA), we just map the input parameters to a colorspace used by
   // libjpeg-turbo.
-  if (format == FORMAT_RGB) {
-    cinfo.input_components = 3;
-    cinfo.in_color_space = JCS_RGB;
-  } else if (format == FORMAT_RGBA ||
-             (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
-    cinfo.input_components = 4;
+  if (format == FORMAT_RGBA ||
+      (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
     cinfo.in_color_space = JCS_EXT_RGBX;
   } else if (format == FORMAT_BGRA ||
              (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
-    cinfo.input_components = 4;
     cinfo.in_color_space = JCS_EXT_BGRX;
   } else {
     // We can exit this function without calling jpeg_destroy_compress() because
@@ -256,9 +206,6 @@
     NOTREACHED() << "Invalid pixel format";
     return false;
   }
-#else
-  cinfo.in_color_space = JCS_RGB;
-#endif
   cinfo.data_precision = 8;
 
   jpeg_set_defaults(&cinfo);
@@ -277,7 +224,6 @@
   jpeg_start_compress(&cinfo, 1);
 
   // feed it the rows, doing necessary conversions for the color format
-#ifdef JCS_EXTENSIONS
   // This function already returns when the input format is not supported by
   // libjpeg-turbo and needs conversion. Therefore, we just encode lines without
   // conversions.
@@ -285,37 +231,6 @@
     const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
     jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
   }
-#else
-  if (format == FORMAT_RGB) {
-    // no conversion necessary
-    while (cinfo.next_scanline < cinfo.image_height) {
-      const unsigned char* row = &input[cinfo.next_scanline * row_byte_width];
-      jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1);
-    }
-  } else {
-    // get the correct format converter
-    void (*converter)(const unsigned char* in, int w, unsigned char* rgb);
-    if (format == FORMAT_RGBA ||
-        (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
-      converter = StripAlpha;
-    } else if (format == FORMAT_BGRA ||
-               (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
-      converter = BGRAtoRGB;
-    } else {
-      NOTREACHED() << "Invalid pixel format";
-      return false;
-    }
-
-    // output row after converting
-    row_buffer = new unsigned char[w * 3];
-
-    while (cinfo.next_scanline < cinfo.image_height) {
-      converter(&input[cinfo.next_scanline * row_byte_width], w, row_buffer);
-      jpeg_write_scanlines(&cinfo, &row_buffer, 1);
-    }
-    delete[] row_buffer;
-  }
-#endif
 
   jpeg_finish_compress(&cinfo);
   return true;
@@ -398,31 +313,6 @@
 void TermSource(j_decompress_ptr cinfo) {
 }
 
-#if !defined(JCS_EXTENSIONS)
-// Converts one row of rgb data to rgba data by adding a fully-opaque alpha
-// value.
-void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) {
-  for (int x = 0; x < pixel_width; x++) {
-    memcpy(&rgba[x * 4], &rgb[x * 3], 3);
-    rgba[x * 4 + 3] = 0xff;
-  }
-}
-
-// Converts one row of RGB data to BGRA by reordering the color components and
-// adding alpha values of 0xff.
-void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb)
-{
-  for (int x = 0; x < pixel_width; x++) {
-    const unsigned char* pixel_in = &bgra[x * 3];
-    unsigned char* pixel_out = &rgb[x * 4];
-    pixel_out[0] = pixel_in[2];
-    pixel_out[1] = pixel_in[1];
-    pixel_out[2] = pixel_in[0];
-    pixel_out[3] = 0xff;
-  }
-}
-#endif  // !defined(JCS_EXTENSIONS)
-
 // This class destroys the given jpeg_decompress object when it goes out of
 // scope. It simplifies the error handling in Decode (and even applies to the
 // success case).
@@ -496,16 +386,12 @@
     case JCS_GRAYSCALE:
     case JCS_RGB:
     case JCS_YCbCr:
-#ifdef JCS_EXTENSIONS
       // Choose an output colorspace and return if it is an unsupported one.
       // Same as JPEGCodec::Encode(), libjpeg-turbo supports all input formats
       // used by Chromium (i.e. RGB, RGBA, and BGRA) and we just map the input
       // parameters to a colorspace.
-      if (format == FORMAT_RGB) {
-        cinfo.out_color_space = JCS_RGB;
-        cinfo.output_components = 3;
-      } else if (format == FORMAT_RGBA ||
-                 (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
+      if (format == FORMAT_RGBA ||
+          (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
         cinfo.out_color_space = JCS_EXT_RGBX;
         cinfo.output_components = 4;
       } else if (format == FORMAT_BGRA ||
@@ -518,9 +404,6 @@
         NOTREACHED() << "Invalid pixel format";
         return false;
       }
-#else
-      cinfo.out_color_space = JCS_RGB;
-#endif
       break;
     case JCS_CMYK:
     case JCS_YCCK:
@@ -530,9 +413,6 @@
       // care about these anyway.
       return false;
   }
-#ifndef JCS_EXTENSIONS
-  cinfo.output_components = 3;
-#endif
 
   jpeg_calc_output_dimensions(&cinfo);
   *w = cinfo.output_width;
@@ -544,7 +424,6 @@
   // how to align row lengths as we do for the compressor.
   int row_read_stride = cinfo.output_width * cinfo.output_components;
 
-#ifdef JCS_EXTENSIONS
   // Create memory for a decoded image and write decoded lines to the memory
   // without conversions same as JPEGCodec::Encode().
   int row_write_stride = row_read_stride;
@@ -555,49 +434,6 @@
     if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
       return false;
   }
-#else
-  if (format == FORMAT_RGB) {
-    // easy case, row needs no conversion
-    int row_write_stride = row_read_stride;
-    output->resize(row_write_stride * cinfo.output_height);
-
-    for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
-      unsigned char* rowptr = &(*output)[row * row_write_stride];
-      if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
-        return false;
-    }
-  } else {
-    // Rows need conversion to output format: read into a temporary buffer and
-    // expand to the final one. Performance: we could avoid the extra
-    // allocation by doing the expansion in-place.
-    int row_write_stride;
-    void (*converter)(const unsigned char* rgb, int w, unsigned char* out);
-    if (format == FORMAT_RGBA ||
-        (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) {
-      row_write_stride = cinfo.output_width * 4;
-      converter = AddAlpha;
-    } else if (format == FORMAT_BGRA ||
-               (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) {
-      row_write_stride = cinfo.output_width * 4;
-      converter = RGBtoBGRA;
-    } else {
-      NOTREACHED() << "Invalid pixel format";
-      jpeg_destroy_decompress(&cinfo);
-      return false;
-    }
-
-    output->resize(row_write_stride * cinfo.output_height);
-
-    std::unique_ptr<unsigned char[]> row_data(
-        new unsigned char[row_read_stride]);
-    unsigned char* rowptr = row_data.get();
-    for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) {
-      if (!jpeg_read_scanlines(&cinfo, &rowptr, 1))
-        return false;
-      converter(rowptr, *w, &(*output)[row * row_write_stride]);
-    }
-  }
-#endif
 
   jpeg_finish_decompress(&cinfo);
   jpeg_destroy_decompress(&cinfo);
diff --git a/ui/gfx/codec/jpeg_codec.h b/ui/gfx/codec/jpeg_codec.h
index 5d10be5..fbc07903 100644
--- a/ui/gfx/codec/jpeg_codec.h
+++ b/ui/gfx/codec/jpeg_codec.h
@@ -23,10 +23,6 @@
 class CODEC_EXPORT JPEGCodec {
  public:
   enum ColorFormat {
-    // 3 bytes per pixel (packed), in RGB order regardless of endianness.
-    // This is the native JPEG format.
-    FORMAT_RGB,
-
     // 4 bytes per pixel, in RGBA order in mem regardless of endianness.
     FORMAT_RGBA,
 
@@ -39,15 +35,6 @@
     FORMAT_SkBitmap
   };
 
-  enum LibraryVariant {
-    SYSTEM_LIBJPEG = 0,
-    LIBJPEG_TURBO,
-    IJG_LIBJPEG,
-  };
-
-  // This method helps identify at run time which library chromium is using.
-  static LibraryVariant JpegLibraryVariant();
-
   // Encodes the given raw 'input' data, with each pixel being represented as
   // given in 'format'. The encoded JPEG data will be written into the supplied
   // vector and true will be returned on success. On failure (false), the
diff --git a/ui/gfx/codec/jpeg_codec_unittest.cc b/ui/gfx/codec/jpeg_codec_unittest.cc
index 8849102e..9c4c8f1 100644
--- a/ui/gfx/codec/jpeg_codec_unittest.cc
+++ b/ui/gfx/codec/jpeg_codec_unittest.cc
@@ -88,62 +88,26 @@
   return acc / static_cast<double>(a.size());
 }
 
-static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) {
-  dat->resize(w * h * 3);
+static void MakeRGBAImage(int w, int h, std::vector<unsigned char>* dat) {
+  dat->resize(w * h * 4);
   for (int y = 0; y < h; y++) {
     for (int x = 0; x < w; x++) {
-      unsigned char* org_px = &(*dat)[(y * w + x) * 3];
+      unsigned char* org_px = &(*dat)[(y * w + x) * 4];
       org_px[0] = x * 3;      // r
       org_px[1] = x * 3 + 1;  // g
       org_px[2] = x * 3 + 2;  // b
+      org_px[3] = 0xFF;       // a
     }
   }
 }
 
-TEST(JPEGCodec, EncodeDecodeRGB) {
-  int w = 20, h = 20;
-
-  // create an image with known values
-  std::vector<unsigned char> original;
-  MakeRGBImage(w, h, &original);
-
-  // encode, making sure it was compressed some
-  std::vector<unsigned char> encoded;
-  EXPECT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h,
-                                w * 3, jpeg_quality, &encoded));
-  EXPECT_GT(original.size(), encoded.size());
-
-  // decode, it should have the same size as the original
-  std::vector<unsigned char> decoded;
-  int outw, outh;
-  EXPECT_TRUE(JPEGCodec::Decode(&encoded[0], encoded.size(),
-                                JPEGCodec::FORMAT_RGB, &decoded,
-                                &outw, &outh));
-  ASSERT_EQ(w, outw);
-  ASSERT_EQ(h, outh);
-  ASSERT_EQ(original.size(), decoded.size());
-
-  // Images must be approximately equal (compression will have introduced some
-  // minor artifacts).
-  ASSERT_GE(jpeg_equality_threshold, AveragePixelDelta(original, decoded));
-}
-
 TEST(JPEGCodec, EncodeDecodeRGBA) {
   int w = 20, h = 20;
 
   // create an image with known values, a must be opaque because it will be
   // lost during compression
   std::vector<unsigned char> original;
-  original.resize(w * h * 4);
-  for (int y = 0; y < h; y++) {
-    for (int x = 0; x < w; x++) {
-      unsigned char* org_px = &original[(y * w + x) * 4];
-      org_px[0] = x * 3;      // r
-      org_px[1] = x * 3 + 1;  // g
-      org_px[2] = x * 3 + 2;  // b
-      org_px[3] = 0xFF;       // a (opaque)
-    }
-  }
+  MakeRGBAImage(w, h, &original);
 
   // encode, making sure it was compressed some
   std::vector<unsigned char> encoded;
@@ -172,31 +136,31 @@
 
   // some random data (an uncompressed image)
   std::vector<unsigned char> original;
-  MakeRGBImage(w, h, &original);
+  MakeRGBAImage(w, h, &original);
 
   // it should fail when given non-JPEG compressed data
   std::vector<unsigned char> output;
   int outw, outh;
   ASSERT_FALSE(JPEGCodec::Decode(&original[0], original.size(),
-                                 JPEGCodec::FORMAT_RGB, &output,
-                                 &outw, &outh));
+                                 JPEGCodec::FORMAT_RGBA, &output, &outw,
+                                 &outh));
 
   // make some compressed data
   std::vector<unsigned char> compressed;
-  ASSERT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h,
+  ASSERT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGBA, w, h,
                                 w * 3, jpeg_quality, &compressed));
 
   // try decompressing a truncated version
   ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size() / 2,
-                                 JPEGCodec::FORMAT_RGB, &output,
-                                 &outw, &outh));
+                                 JPEGCodec::FORMAT_RGBA, &output, &outw,
+                                 &outh));
 
   // corrupt it and try decompressing that
   for (int i = 10; i < 30; i++)
     compressed[i] = i;
   ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size(),
-                                 JPEGCodec::FORMAT_RGB, &output,
-                                 &outw, &outh));
+                                 JPEGCodec::FORMAT_RGBA, &output, &outw,
+                                 &outh));
 }
 
 // Test that we can decode JPEG images without invalid-read errors on valgrind.
@@ -207,11 +171,6 @@
   int outw, outh;
   JPEGCodec::Decode(kTopSitesMigrationTestImage,
                     arraysize(kTopSitesMigrationTestImage),
-                    JPEGCodec::FORMAT_RGB, &output,
-                    &outw, &outh);
-
-  JPEGCodec::Decode(kTopSitesMigrationTestImage,
-                    arraysize(kTopSitesMigrationTestImage),
                     JPEGCodec::FORMAT_RGBA, &output,
                     &outw, &outh);
 }
diff --git a/ui/views/focus/focus_manager.cc b/ui/views/focus/focus_manager.cc
index 530da58..b4b827d 100644
--- a/ui/views/focus/focus_manager.cc
+++ b/ui/views/focus/focus_manager.cc
@@ -550,12 +550,6 @@
   // such that ViewRemoved() is never called.
   CHECK_EQ(view, focused_view_);
   SetFocusedView(nullptr);
-  if (GetStoredFocusView() == view) {
-    // SetFocusedView() stored |view|. As |view| is being deleting and because
-    // a ViewObserver was just added, ViewTracker won't get
-    // OnViewIsDeleting() to properly clean up. Force that cleanup by
-    SetStoredFocusView(nullptr);
-  }
 }
 
 }  // namespace views
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index b1f2574c..09c44ee 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -364,11 +364,18 @@
       if (!view_for_activation) {
         view_for_activation = GetWidget()->GetRootView();
       } else if (view_for_activation == focus_manager->GetStoredFocusView()) {
-        focus_manager->RestoreFocusedView();
-        // Set to false if desktop native widget has activated activation
-        // change, so that aura window activation change focus restore operation
-        // can be ignored.
-        restore_focus_on_activate_ = false;
+        // When desktop native widget has modal transient child, we don't
+        // restore focused view here, as the modal transient child window will
+        // get activated and focused. Thus, we are not left with multiple
+        // focuses. For aura child widgets, since their views are managed by
+        // |focus_manager|, we then allow restoring focused view.
+        if (!wm::GetModalTransient(GetWidget()->GetNativeView())) {
+          focus_manager->RestoreFocusedView();
+          // Set to false if desktop native widget has activated activation
+          // change, so that aura window activation change focus restore
+          // operation can be ignored.
+          restore_focus_on_activate_ = false;
+        }
       }
       activation_client->ActivateWindow(
           view_for_activation->GetWidget()->GetNativeView());
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc
index a1e82b4..cd58ae0 100644
--- a/ui/views/widget/widget_interactive_uitest.cc
+++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -1271,6 +1271,53 @@
   widget->CloseNow();
 }
 
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_WIN)
+// TODO(warx): Investigate the flakiness on OS_WIN (crbug.com/729331).
+// Tests that when a desktop native widget has modal transient child, it should
+// avoid restore focused view itself as the modal transient child window will do
+// that, thus avoids having multiple focused view visually (crbug.com/727641).
+TEST_F(WidgetTestInteractive, DesktopNativeWidgetWithModalTransientChild) {
+  // Create a top level desktop native widget.
+  Widget* top_level = CreateWidget();
+
+  Textfield* textfield = new Textfield;
+  textfield->SetBounds(0, 0, 200, 20);
+  top_level->GetRootView()->AddChildView(textfield);
+  ShowSync(top_level);
+  textfield->RequestFocus();
+  EXPECT_TRUE(textfield->HasFocus());
+
+  // Create a modal dialog.
+  // This instance will be destroyed when the dialog is destroyed.
+  ModalDialogDelegate* dialog_delegate =
+      new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW);
+  Widget* modal_dialog_widget = DialogDelegate::CreateDialogWidget(
+      dialog_delegate, nullptr, top_level->GetNativeView());
+  modal_dialog_widget->SetBounds(gfx::Rect(0, 0, 100, 10));
+  Textfield* dialog_textfield = new Textfield;
+  dialog_textfield->SetBounds(0, 0, 50, 5);
+  modal_dialog_widget->GetRootView()->AddChildView(dialog_textfield);
+  // Dialog widget doesn't need a ShowSync as it gains active status
+  // synchronously.
+  modal_dialog_widget->Show();
+  dialog_textfield->RequestFocus();
+  EXPECT_TRUE(dialog_textfield->HasFocus());
+  EXPECT_FALSE(textfield->HasFocus());
+
+  DeactivateSync(top_level);
+  EXPECT_FALSE(dialog_textfield->HasFocus());
+  EXPECT_FALSE(textfield->HasFocus());
+
+  // After deactivation and activation of top level widget, only modal dialog
+  // should restore focused view.
+  ActivateSync(top_level);
+  EXPECT_TRUE(dialog_textfield->HasFocus());
+  EXPECT_FALSE(textfield->HasFocus());
+
+  top_level->CloseNow();
+}
+#endif  // defined(USE_AURA) && !defined(OS_CHROMEOS) && !defined(OS_WIN)
+
 namespace {
 
 // Helper class for CaptureLostTrackingWidget to store whether