diff --git a/DEPS b/DEPS
index 88a7ebe5..c675bee9 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': '7f9c29a887106ab3babe0ec423a3bcae87ae4788',
+  'skia_revision': 'e330eb2c0e6bab83add6986119864b546bf0b0a7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '33316fccc6a523077d15dc7944a492099a99f6e1',
+  'pdfium_revision': 'd198e406d13b831ffd4b1a2bfdf12522dea31205',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
diff --git a/ash/ash_chromeos_strings.grdp b/ash/ash_chromeos_strings.grdp
index c2a38c08..23b56ca 100644
--- a/ash/ash_chromeos_strings.grdp
+++ b/ash/ash_chromeos_strings.grdp
@@ -226,6 +226,10 @@
   </message>
 
   <!-- Status tray audio strings. -->
+  <message name="IDS_ASH_STATUS_TRAY_AUDIO_FRONT_MIC" desc="label used for front microphone">
+    Front Microphone
+  </message>
+
   <message name="IDS_ASH_STATUS_TRAY_AUDIO_HEADPHONE" desc="label used for audio headphone device">
     Headphone
   </message>
@@ -238,6 +242,10 @@
     Microphone (Internal)
   </message>
 
+  <message name="IDS_ASH_STATUS_TRAY_AUDIO_REAR_MIC" desc="label used for rear microphone">
+    Rear Microphone
+  </message>
+
   <message name="IDS_ASH_STATUS_TRAY_AUDIO_USB_DEVICE" desc="label used for usb audio device">
     <ph name="device_name">$1<ex>Headphone</ex></ph> (USB)
   </message>
diff --git a/ash/aura/wm_shell_aura.cc b/ash/aura/wm_shell_aura.cc
index 01127cf..2643fa3 100644
--- a/ash/aura/wm_shell_aura.cc
+++ b/ash/aura/wm_shell_aura.cc
@@ -26,7 +26,6 @@
 #include "ash/touch/touch_uma.h"
 #include "ash/virtual_keyboard_controller.h"
 #include "ash/wm/drag_window_resizer.h"
-#include "ash/wm/lock_state_controller.h"
 #include "ash/wm/maximize_mode/maximize_mode_event_handler_aura.h"
 #include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/window_cycle_event_filter_aura.h"
@@ -263,10 +262,6 @@
   pointer_watcher_adapter_->RemovePointerWatcher(watcher);
 }
 
-void WmShellAura::RequestShutdown() {
-  Shell::GetInstance()->lock_state_controller()->RequestShutdown();
-}
-
 bool WmShellAura::IsTouchDown() {
   return aura::Env::GetInstance()->is_touch_down();
 }
diff --git a/ash/aura/wm_shell_aura.h b/ash/aura/wm_shell_aura.h
index 32b256b5..347794b1 100644
--- a/ash/aura/wm_shell_aura.h
+++ b/ash/aura/wm_shell_aura.h
@@ -77,7 +77,6 @@
   void AddPointerWatcher(views::PointerWatcher* watcher,
                          views::PointerWatcherEventTypes events) override;
   void RemovePointerWatcher(views::PointerWatcher* watcher) override;
-  void RequestShutdown() override;
   bool IsTouchDown() override;
   void ToggleIgnoreExternalKeyboard() override;
   void SetLaserPointerEnabled(bool enabled) override;
diff --git a/ash/common/frame/header_view.cc b/ash/common/frame/header_view.cc
index d868bcf..b1c5a68 100644
--- a/ash/common/frame/header_view.cc
+++ b/ash/common/frame/header_view.cc
@@ -164,20 +164,20 @@
 
 void HeaderView::OnImmersiveRevealStarted() {
   fullscreen_visible_fraction_ = 0;
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   parent()->Layout();
 }
 
 void HeaderView::OnImmersiveRevealEnded() {
   fullscreen_visible_fraction_ = 0;
-  SetPaintToLayer(false);
+  DestroyLayer();
   parent()->Layout();
 }
 
 void HeaderView::OnImmersiveFullscreenExited() {
   fullscreen_visible_fraction_ = 0;
-  SetPaintToLayer(false);
+  DestroyLayer();
   parent()->Layout();
 }
 
diff --git a/ash/common/shelf/overflow_bubble_view.cc b/ash/common/shelf/overflow_bubble_view.cc
index db47ae84..ae76618 100644
--- a/ash/common/shelf/overflow_bubble_view.cc
+++ b/ash/common/shelf/overflow_bubble_view.cc
@@ -60,7 +60,7 @@
   set_can_activate(false);
 
   // Makes bubble view has a layer and clip its children layers.
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   layer()->SetMasksToBounds(true);
 
diff --git a/ash/common/shelf/shelf_button.cc b/ash/common/shelf/shelf_button.cc
index e7b4c05..613f9cd 100644
--- a/ash/common/shelf/shelf_button.cc
+++ b/ash/common/shelf/shelf_button.cc
@@ -273,7 +273,7 @@
   icon_shadows_.assign(kShadows, kShadows + arraysize(kShadows));
 
   // TODO: refactor the layers so each button doesn't require 2.
-  icon_view_->SetPaintToLayer(true);
+  icon_view_->SetPaintToLayer();
   icon_view_->layer()->SetFillsBoundsOpaquely(false);
   icon_view_->SetHorizontalAlignment(views::ImageView::CENTER);
   icon_view_->SetVerticalAlignment(views::ImageView::LEADING);
diff --git a/ash/common/shelf/shelf_view.cc b/ash/common/shelf/shelf_view.cc
index 48458db..8463037 100644
--- a/ash/common/shelf/shelf_view.cc
+++ b/ash/common/shelf/shelf_view.cc
@@ -405,7 +405,7 @@
   const ShelfItems& items(model_->items());
   for (ShelfItems::const_iterator i = items.begin(); i != items.end(); ++i) {
     views::View* child = CreateViewForItem(*i);
-    child->SetPaintToLayer(true);
+    child->SetPaintToLayer();
     view_model_->Add(child, static_cast<int>(i - items.begin()));
     AddChildView(child);
   }
@@ -1392,7 +1392,7 @@
 }
 
 void ShelfView::ConfigureChildView(views::View* view) {
-  view->SetPaintToLayer(true);
+  view->SetPaintToLayer();
   view->layer()->SetFillsBoundsOpaquely(false);
 }
 
diff --git a/ash/common/system/chromeos/audio/audio_detailed_view.cc b/ash/common/system/chromeos/audio/audio_detailed_view.cc
index b65a8e8ce..9eef7fd 100644
--- a/ash/common/system/chromeos/audio/audio_detailed_view.cc
+++ b/ash/common/system/chromeos/audio/audio_detailed_view.cc
@@ -27,6 +27,8 @@
 
 base::string16 GetAudioDeviceName(const chromeos::AudioDevice& device) {
   switch (device.type) {
+    case chromeos::AUDIO_TYPE_FRONT_MIC:
+      return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_FRONT_MIC);
     case chromeos::AUDIO_TYPE_HEADPHONE:
       return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_HEADPHONE);
     case chromeos::AUDIO_TYPE_INTERNAL_SPEAKER:
@@ -34,6 +36,8 @@
           IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_SPEAKER);
     case chromeos::AUDIO_TYPE_INTERNAL_MIC:
       return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_MIC);
+    case chromeos::AUDIO_TYPE_REAR_MIC:
+      return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_REAR_MIC);
     case chromeos::AUDIO_TYPE_USB:
       return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_AUDIO_USB_DEVICE,
                                         base::UTF8ToUTF16(device.display_name));
diff --git a/ash/common/system/chromeos/network/network_state_list_detailed_view.cc b/ash/common/system/chromeos/network/network_state_list_detailed_view.cc
index b87f56b..80908899 100644
--- a/ash/common/system/chromeos/network/network_state_list_detailed_view.cc
+++ b/ash/common/system/chromeos/network/network_state_list_detailed_view.cc
@@ -195,7 +195,7 @@
 class ScanningThrobber : public ThrobberView {
  public:
   ScanningThrobber() {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
     layer()->SetOpacity(1.0);
     accessible_name_ =
@@ -241,7 +241,7 @@
     SetImageAlignment(ALIGN_CENTER, ALIGN_MIDDLE);
     SetAccessibleName(
         bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_NETWORK_INFO));
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
     layer()->SetOpacity(1.0);
   }
diff --git a/ash/common/system/date/date_default_view.cc b/ash/common/system/date/date_default_view.cc
index fda24673..a0b92e84 100644
--- a/ash/common/system/date/date_default_view.cc
+++ b/ash/common/system/date/date_default_view.cc
@@ -14,6 +14,8 @@
 #include "ash/common/system/tray/tray_constants.h"
 #include "ash/common/system/tray/tray_popup_header_button.h"
 #include "ash/common/wm_shell.h"
+#include "ash/shell.h"
+#include "ash/wm/lock_state_controller.h"
 #include "base/i18n/rtl.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
@@ -129,7 +131,7 @@
     shell->system_tray_controller()->ShowHelp();
   } else if (sender == shutdown_button_) {
     shell->RecordUserMetricsAction(UMA_TRAY_SHUT_DOWN);
-    shell->RequestShutdown();
+    Shell::GetInstance()->lock_state_controller()->RequestShutdown();
   } else if (sender == lock_button_) {
     shell->RecordUserMetricsAction(UMA_TRAY_LOCK_SCREEN);
     chromeos::DBusThreadManager::Get()
diff --git a/ash/common/system/status_area_widget_delegate.cc b/ash/common/system/status_area_widget_delegate.cc
index fda7c12..920c171e6 100644
--- a/ash/common/system/status_area_widget_delegate.cc
+++ b/ash/common/system/status_area_widget_delegate.cc
@@ -57,7 +57,7 @@
   // Allow the launcher to surrender the focus to another window upon
   // navigation completion by the user.
   set_allow_deactivate_on_esc(true);
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 }
 
diff --git a/ash/common/system/tiles/tiles_default_view.cc b/ash/common/system/tiles/tiles_default_view.cc
index 12dd394..91fe4fc 100644
--- a/ash/common/system/tiles/tiles_default_view.cc
+++ b/ash/common/system/tiles/tiles_default_view.cc
@@ -16,6 +16,8 @@
 #include "ash/common/system/tray/tray_popup_utils.h"
 #include "ash/common/wm_shell.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
+#include "ash/wm/lock_state_controller.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
 #include "grit/ash_strings.h"
@@ -121,7 +123,7 @@
         ->RequestLockScreen();
   } else if (sender == power_button_) {
     shell->RecordUserMetricsAction(UMA_TRAY_SHUT_DOWN);
-    shell->RequestShutdown();
+    Shell::GetInstance()->lock_state_controller()->RequestShutdown();
   }
 
   owner_->system_tray()->CloseSystemBubble();
diff --git a/ash/common/system/tray/throbber_view.cc b/ash/common/system/tray/throbber_view.cc
index a049503a..76600c6 100644
--- a/ash/common/system/tray/throbber_view.cc
+++ b/ash/common/system/tray/throbber_view.cc
@@ -40,7 +40,7 @@
   throbber_->set_stop_delay_ms(kThrobberAnimationDurationMs);
   AddChildView(throbber_);
 
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   layer()->SetOpacity(0.0);
 }
diff --git a/ash/common/system/tray/tray_background_view.cc b/ash/common/system/tray/tray_background_view.cc
index 4fdc3dcf..d7634c2f 100644
--- a/ash/common/system/tray/tray_background_view.cc
+++ b/ash/common/system/tray/tray_background_view.cc
@@ -302,7 +302,7 @@
   SetContents(tray_container_);
   tray_event_filter_.reset(new TrayEventFilter);
 
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   // Start the tray items not visible, because visibility changes are animated.
   views::View::SetVisible(false);
diff --git a/ash/common/system/tray/tray_details_view.cc b/ash/common/system/tray/tray_details_view.cc
index 6e1b8739..9fbe9c6 100644
--- a/ash/common/system/tray/tray_details_view.cc
+++ b/ash/common/system/tray/tray_details_view.cc
@@ -367,7 +367,7 @@
   scroller_->SetContentsView(scroll_content_);
   // Make the |scroller_| have a layer to clip |scroll_content_|'s children.
   // TODO(varkha): Make the sticky rows work with EnableViewPortLayer().
-  scroller_->SetPaintToLayer(true);
+  scroller_->SetPaintToLayer();
   scroller_->set_background(
       views::Background::CreateSolidBackground(kBackgroundColor));
   scroller_->layer()->SetMasksToBounds(true);
diff --git a/ash/common/system/tray/tray_details_view_unittest.cc b/ash/common/system/tray/tray_details_view_unittest.cc
index 0fb4de6..a3eceb2 100644
--- a/ash/common/system/tray/tray_details_view_unittest.cc
+++ b/ash/common/system/tray/tray_details_view_unittest.cc
@@ -326,14 +326,14 @@
   RunAllPendingInMessageLoop();
   test_item->detailed_view()->CreateScrollerViews();
 
-  test_item->detailed_view()->scroll_content()->SetPaintToLayer(true);
+  test_item->detailed_view()->scroll_content()->SetPaintToLayer();
   views::View* view1 = new views::View();
   test_item->detailed_view()->scroll_content()->AddChildView(view1);
   views::View* view2 = new views::View();
-  view2->SetPaintToLayer(true);
+  view2->SetPaintToLayer();
   test_item->detailed_view()->scroll_content()->AddChildView(view2);
   views::View* view3 = new views::View();
-  view3->SetPaintToLayer(true);
+  view3->SetPaintToLayer();
   test_item->detailed_view()->scroll_content()->AddChildView(view3);
 
   // Child layers should have same order as the child views.
@@ -346,7 +346,7 @@
   // Mark |view2| as sticky and add one more child (which will reorder layers).
   view2->set_id(VIEW_ID_STICKY_HEADER);
   views::View* view4 = new views::View();
-  view4->SetPaintToLayer(true);
+  view4->SetPaintToLayer();
   test_item->detailed_view()->scroll_content()->AddChildView(view4);
 
   // Sticky header layer should be above the last child's layer.
diff --git a/ash/common/system/tray/tray_item_view.cc b/ash/common/system/tray/tray_item_view.cc
index a2cb5e7..476bcff 100644
--- a/ash/common/system/tray/tray_item_view.cc
+++ b/ash/common/system/tray/tray_item_view.cc
@@ -30,7 +30,7 @@
 
 TrayItemView::TrayItemView(SystemTrayItem* owner)
     : owner_(owner), label_(NULL), image_view_(NULL) {
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   SetLayoutManager(new views::FillLayout());
 }
diff --git a/ash/common/system/tray/tray_popup_item_container.cc b/ash/common/system/tray/tray_popup_item_container.cc
index f342878f..3a3d0d5 100644
--- a/ash/common/system/tray/tray_popup_item_container.cc
+++ b/ash/common/system/tray/tray_popup_item_container.cc
@@ -19,7 +19,7 @@
   layout->SetDefaultFlex(1);
   SetLayoutManager(layout);
   if (view->layer()) {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(view->layer()->fills_bounds_opaquely());
   }
   AddChildView(view);
diff --git a/ash/common/system/tray/tray_popup_utils.cc b/ash/common/system/tray/tray_popup_utils.cc
index 2abe1f6..608d5679 100644
--- a/ash/common/system/tray/tray_popup_utils.cc
+++ b/ash/common/system/tray/tray_popup_utils.cc
@@ -286,7 +286,7 @@
       views::Background::CreateSolidBackground(kBackgroundColor));
   view->SetBorder(
       views::CreateEmptyBorder(gfx::Insets(kMenuSeparatorVerticalPadding, 0)));
-  view->SetPaintToLayer(true);
+  view->SetPaintToLayer();
   view->layer()->SetFillsBoundsOpaquely(false);
 }
 
diff --git a/ash/common/system/user/tray_user.cc b/ash/common/system/user/tray_user.cc
index 98f009e95..c4d5c98 100644
--- a/ash/common/system/user/tray_user.cc
+++ b/ash/common/system/user/tray_user.cc
@@ -158,7 +158,7 @@
     if (need_avatar) {
       avatar_ = new tray::RoundedImageView(kTrayRoundedBorderRadius, true);
       if (MaterialDesignController::IsShelfMaterial()) {
-        avatar_->SetPaintToLayer(true);
+        avatar_->SetPaintToLayer();
         avatar_->layer()->SetFillsBoundsOpaquely(false);
       }
       layout_view_->AddChildView(avatar_);
diff --git a/ash/common/system/web_notification/web_notification_tray.cc b/ash/common/system/web_notification/web_notification_tray.cc
index c6487da9..ce82103 100644
--- a/ash/common/system/web_notification/web_notification_tray.cc
+++ b/ash/common/system/web_notification/web_notification_tray.cc
@@ -124,7 +124,7 @@
   WebNotificationItem(gfx::AnimationContainer* container,
                       WebNotificationTray* tray)
       : tray_(tray) {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
     views::View::SetVisible(false);
     set_owned_by_client();
diff --git a/ash/common/wallpaper/wallpaper_view.cc b/ash/common/wallpaper/wallpaper_view.cc
index c690b520..85c14e9d 100644
--- a/ash/common/wallpaper/wallpaper_view.cc
+++ b/ash/common/wallpaper/wallpaper_view.cc
@@ -34,7 +34,7 @@
  public:
   explicit LayerControlView(views::View* view) {
     AddChildView(view);
-    view->SetPaintToLayer(true);
+    view->SetPaintToLayer();
   }
 
   // Overrides views::View.
diff --git a/ash/common/wm/overview/window_selector_item.cc b/ash/common/wm/overview/window_selector_item.cc
index 582c39f..0671de9 100644
--- a/ash/common/wm/overview/window_selector_item.cc
+++ b/ash/common/wm/overview/window_selector_item.cc
@@ -192,7 +192,7 @@
         current_value_(0),
         layer_(nullptr),
         animation_(new gfx::SlideAnimation(this)) {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
   }
 
diff --git a/ash/common/wm/window_cycle_list.cc b/ash/common/wm/window_cycle_list.cc
index e60ebd0..f1dc275 100644
--- a/ash/common/wm/window_cycle_list.cc
+++ b/ash/common/wm/window_cycle_list.cc
@@ -213,7 +213,7 @@
         highlight_view_(new views::View()),
         target_window_(nullptr) {
     DCHECK(!windows.empty());
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
     layer()->SetMasksToBounds(true);
     layer()->SetOpacity(0.0);
@@ -232,7 +232,7 @@
     layout->set_cross_axis_alignment(
         views::BoxLayout::CROSS_AXIS_ALIGNMENT_START);
     mirror_container_->SetLayoutManager(layout);
-    mirror_container_->SetPaintToLayer(true);
+    mirror_container_->SetPaintToLayer();
     mirror_container_->layer()->SetFillsBoundsOpaquely(false);
 
     for (WmWindow* window : windows) {
@@ -249,7 +249,8 @@
         views::Painter::CreateRoundRectWith1PxBorderPainter(
             SkColorSetA(SK_ColorWHITE, 0x4D), SkColorSetA(SK_ColorWHITE, 0x33),
             kBackgroundCornerRadius)));
-    highlight_view_->SetPaintToLayer(true);
+    highlight_view_->SetPaintToLayer();
+
     highlight_view_->layer()->SetFillsBoundsOpaquely(false);
 
     AddChildView(highlight_view_);
diff --git a/ash/common/wm_shell.h b/ash/common/wm_shell.h
index f13fb91..d6bfae07b 100644
--- a/ash/common/wm_shell.h
+++ b/ash/common/wm_shell.h
@@ -430,12 +430,6 @@
   void AddLockStateObserver(LockStateObserver* observer);
   void RemoveLockStateObserver(LockStateObserver* observer);
 
-  // Displays the shutdown animation and requests a system shutdown or system
-  // restart depending on the the state of the |RebootOnShutdown| device policy.
-  // TODO(mash): Remove this method and call LockStateController directly when
-  // it is available to code in ash/common.
-  virtual void RequestShutdown() = 0;
-
   void SetShelfDelegateForTesting(std::unique_ptr<ShelfDelegate> test_delegate);
   void SetPaletteDelegateForTesting(
       std::unique_ptr<PaletteDelegate> palette_delegate);
diff --git a/ash/mus/accelerators/accelerator_controller_unittest.cc b/ash/mus/accelerators/accelerator_controller_unittest.cc
index 0846c34f..a071772 100644
--- a/ash/mus/accelerators/accelerator_controller_unittest.cc
+++ b/ash/mus/accelerators/accelerator_controller_unittest.cc
@@ -972,10 +972,8 @@
       ui::Accelerator(ui::VKEY_M, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN)));
 
   // Lock screen
-  // NOTE: Accelerators that do not work on the lock screen need to be
-  // tested before the sequence below is invoked because it causes a side
-  // effect of locking the screen.
-  EXPECT_TRUE(
+  // TODO(derat): Reenable this once user sessions work in mash.
+  EXPECT_FALSE(
       ProcessInController(ui::Accelerator(ui::VKEY_L, ui::EF_COMMAND_DOWN)));
 #endif
 }
diff --git a/ash/mus/bridge/wm_shell_mus.cc b/ash/mus/bridge/wm_shell_mus.cc
index ca58c142..b45d518 100644
--- a/ash/mus/bridge/wm_shell_mus.cc
+++ b/ash/mus/bridge/wm_shell_mus.cc
@@ -63,7 +63,11 @@
   int GetMaximumNumberOfLoggedInUsers() const override { return 3; }
   int NumberOfLoggedInUsers() const override { return 1; }
   bool IsActiveUserSessionStarted() const override { return true; }
-  bool CanLockScreen() const override { return true; }
+  bool CanLockScreen() const override {
+    // The Chrome OS session_manager process currently rejects screen-lock
+    // requests due to no user being logged in.
+    return false;
+  }
   bool IsScreenLocked() const override { return screen_locked_; }
   bool ShouldLockScreenAutomatically() const override { return false; }
   void LockScreen() override {
@@ -389,10 +393,6 @@
   pointer_watcher_event_router_->RemovePointerWatcher(watcher);
 }
 
-void WmShellMus::RequestShutdown() {
-  NOTIMPLEMENTED();
-}
-
 bool WmShellMus::IsTouchDown() {
   // TODO: implement me, http://crbug.com/634967.
   // NOTIMPLEMENTED is too spammy here.
diff --git a/ash/mus/bridge/wm_shell_mus.h b/ash/mus/bridge/wm_shell_mus.h
index 6539bab..438f02b3 100644
--- a/ash/mus/bridge/wm_shell_mus.h
+++ b/ash/mus/bridge/wm_shell_mus.h
@@ -106,7 +106,6 @@
   void AddPointerWatcher(views::PointerWatcher* watcher,
                          views::PointerWatcherEventTypes events) override;
   void RemovePointerWatcher(views::PointerWatcher* watcher) override;
-  void RequestShutdown() override;
   bool IsTouchDown() override;
   void ToggleIgnoreExternalKeyboard() override;
   void SetLaserPointerEnabled(bool enabled) override;
diff --git a/ash/shell.cc b/ash/shell.cc
index aa5144e..e765b34 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -679,9 +679,10 @@
         new ResolutionNotificationController);
   }
 
-  if (cursor_manager_)
+  if (cursor_manager_) {
     cursor_manager_->SetDisplay(
         display::Screen::GetScreen()->GetPrimaryDisplay());
+  }
 
   if (!is_mash) {
     // TODO(sky): move this to WmShell. http://crbug.com/671246.
diff --git a/ash/touch/touch_hud_debug.cc b/ash/touch/touch_hud_debug.cc
index 6c72ff4..0689e659 100644
--- a/ash/touch/touch_hud_debug.cc
+++ b/ash/touch/touch_hud_debug.cc
@@ -238,7 +238,7 @@
  public:
   explicit TouchHudCanvas(const TouchLog& touch_log)
       : touch_log_(touch_log), scale_(1) {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
 
     paint_.setStyle(SkPaint::kFill_Style);
diff --git a/ash/touch_hud/touch_hud_renderer.cc b/ash/touch_hud/touch_hud_renderer.cc
index adb5243..539bbdb 100644
--- a/ash/touch_hud/touch_hud_renderer.cc
+++ b/ash/touch_hud/touch_hud_renderer.cc
@@ -35,7 +35,7 @@
   explicit TouchPointView(views::Widget* parent_widget)
       : circle_center_(kPointRadius + 1, kPointRadius + 1),
         gradient_center_(SkPoint::Make(kPointRadius + 1, kPointRadius + 1)) {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
 
     SetSize(gfx::Size(2 * kPointRadius + 2, 2 * kPointRadius + 2));
diff --git a/ash/wm/lock_state_controller.cc b/ash/wm/lock_state_controller.cc
index 31a874d..293d95a 100644
--- a/ash/wm/lock_state_controller.cc
+++ b/ash/wm/lock_state_controller.cc
@@ -162,8 +162,12 @@
   shutting_down_ = true;
 
   Shell* shell = Shell::GetInstance();
-  shell->cursor_manager()->HideCursor();
-  shell->cursor_manager()->LockCursor();
+  // TODO(derat): Remove these null checks once mash instantiates a
+  // CursorManager.
+  if (shell->cursor_manager()) {
+    shell->cursor_manager()->HideCursor();
+    shell->cursor_manager()->LockCursor();
+  }
 
   animator_->StartAnimation(
       SessionStateAnimator::ROOT_CONTAINER,
@@ -200,8 +204,10 @@
   if (!shutting_down_) {
     shutting_down_ = true;
     Shell* shell = Shell::GetInstance();
-    shell->cursor_manager()->HideCursor();
-    shell->cursor_manager()->LockCursor();
+    if (shell->cursor_manager()) {
+      shell->cursor_manager()->HideCursor();
+      shell->cursor_manager()->LockCursor();
+    }
     animator_->StartAnimation(SessionStateAnimator::kAllNonRootContainersMask,
                               SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
                               SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
@@ -271,7 +277,8 @@
   shutting_down_ = true;
 
   Shell* shell = Shell::GetInstance();
-  shell->cursor_manager()->HideCursor();
+  if (shell->cursor_manager())
+    shell->cursor_manager()->HideCursor();
 
   StartRealShutdownTimer(false);
 }
@@ -307,7 +314,8 @@
 void LockStateController::StartCancellableShutdownAnimation() {
   Shell* shell = Shell::GetInstance();
   // Hide cursor, but let it reappear if the mouse moves.
-  shell->cursor_manager()->HideCursor();
+  if (shell->cursor_manager())
+    shell->cursor_manager()->HideCursor();
 
   animator_->StartAnimation(
       SessionStateAnimator::ROOT_CONTAINER,
diff --git a/ash/wm/window_mirror_view.cc b/ash/wm/window_mirror_view.cc
index 52c87d1..7b67803 100644
--- a/ash/wm/window_mirror_view.cc
+++ b/ash/wm/window_mirror_view.cc
@@ -86,7 +86,7 @@
   layer_owner_ =
       ::wm::MirrorLayers(target_->aura_window(), false /* sync_bounds */);
 
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->Add(GetMirrorLayer());
   // This causes us to clip the non-client areas of the window.
   layer()->SetMasksToBounds(true);
diff --git a/chrome/VERSION b/chrome/VERSION
index 983c555..16b3c719 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=58
 MINOR=0
-BUILD=2992
+BUILD=2993
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
index a39909f..1e047ec2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java
@@ -4,11 +4,9 @@
 
 package org.chromium.chrome.browser.webapps;
 
-import android.content.ContentResolver;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.provider.Settings;
 import android.text.TextUtils;
 
 import org.chromium.base.CommandLine;
@@ -244,21 +242,6 @@
     }
 
     /**
-     * Returns whether the user has enabled installing apps from sources other than the Google
-     * Play Store.
-     */
-    private static boolean installingFromUnknownSourcesAllowed() {
-        ContentResolver contentResolver = ContextUtils.getApplicationContext().getContentResolver();
-        try {
-            int setting = Settings.Secure.getInt(
-                    contentResolver, Settings.Secure.INSTALL_NON_MARKET_APPS);
-            return setting == 1;
-        } catch (Settings.SettingNotFoundException e) {
-            return false;
-        }
-    }
-
-    /**
      * Checks whether the WebAPK needs to be updated.
      * @param info               Meta data from WebAPK's Android Manifest.
      * @param fetchedInfo        Fetched data for Web Manifest.
diff --git a/chrome/android/webapk/channel_keys.h b/chrome/android/webapk/channel_keys.h
index 8f957a0..8418f71 100644
--- a/chrome/android/webapk/channel_keys.h
+++ b/chrome/android/webapk/channel_keys.h
@@ -2,16 +2,4 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#if channel_default
-#define SIGNATURE 48, -126, 4, 105, 48, -126, 2, -47, -96, 3, 2, 1, 2, 2, 21, 0, -29, -42, -41, -53, 102, -15, 87, 57, -41, 125, 122, -85, -104, -38, -85, 88, -76, 97, 121, -10, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, 30, 23, 13, 49, 54, 48, 56, 50, 51, 50, 48, 48, 56, 48, 53, 90, 23, 13, 52, 52, 48, 49, 49, 48, 50, 48, 48, 56, 48, 53, 90, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, -126, 1, -94, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, -113, 0, 48, -126, 1, -118, 2, -126, 1, -127, 0, -74, 37, 88, -114, -97, -9, 98, -39, -28, -24, 115, 93, -4, -90, -118, 97, -49, -27, -70, -81, 82, -9, -44, 56, 109, -72, -32, 47, 77, -74, -28, 26, 126, 110, 65, -49, -54, -3, 75, 116, -113, -62, 71, -28, -51, 62, 58, -99, 30, -26, 59, -123, 9, -19, 63, 67, 120, -12, 51, -122, -65, 80, 61, -5, -56, 95, -124, 5, -124, 2, -57, -41, 89, -51, 73, -94, -40, -109, -1, -48, 114, -106, 120, -62, -12, -35, -93, 47, 117, 54, -24, 93, 114, -79, -121, 68, 39, 37, 1, 39, -80, -92, 47, -124, 63, -41, 98, -7, -40, 107, -24, 107, 67, -36, 21, 94, 76, 112, -105, -38, -86, -68, -106, -65, 97, 61, 5, -71, 44, -68, -78, -89, -126, 115, 86, 24, -82, 1, -42, 3, -79, 64, 51, 81, 19, -40, -110, -24, 62, 118, 15, -66, -2, -50, -115, 56, 40, -72, 21, -86, -29, -37, 90, 31, 50, -67, 107, -6, -62, 28, 89, -122, -10, 59, -41, -119, -11, -51, 39, 33, 69, -74, 20, 9, -18, -51, 93, 24, -69, -13, 112, -45, 45, 123, 12, -112, 116, 86, 6, 25, -9, 108, 115, 94, 54, -108, 73, 71, -68, -4, 77, -124, 52, -56, 127, -10, -18, 111, 101, -57, -110, -65, 58, -32, 106, -49, -48, 79, 76, 89, -75, -128, -16, -42, -76, 94, -111, -37, -87, 77, -110, -9, 107, -64, 99, 18, -83, -76, 62, -82, -124, -68, 65, -108, 10, -77, 87, 46, -115, -73, -80, -68, -7, 75, -42, -108, 5, 119, 120, 13, -61, 54, -41, -2, -83, -14, 67, -19, -48, 67, -71, 84, 22, -51, 62, 8, 38, 14, -98, -43, 61, 106, -78, 104, 11, -15, 113, -66, -19, -8, 113, 84, -23, -126, -99, 127, 18, 120, 5, -27, -97, 59, 115, 3, -58, 54, 13, 119, -64, -52, 123, 38, -2, -47, 91, -89, -49, -28, -14, 49, -110, -74, 60, 79, 61, 91, 41, 82, 70, 63, 85, 104, 29, 34, 45, -64, -50, 85, 60, 31, -56, -123, -116, -44, -44, 55, 63, 91, 122, -103, 26, -58, 3, -106, 60, -16, -63, -55, 107, -125, 62, -116, -78, 67, 36, -124, 8, 101, 77, -6, 21, 126, -62, 73, 2, 3, 1, 0, 1, -93, 84, 48, 82, 48, 14, 6, 3, 85, 29, 15, 1, 1, -1, 4, 4, 3, 2, 2, -92, 48, 19, 6, 3, 85, 29, 37, 4, 12, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 48, 12, 6, 3, 85, 29, 19, 1, 1, -1, 4, 2, 48, 0, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 18, -48, 127, -38, 19, 24, 20, -121, 35, -100, 41, 44, -126, -62, 15, -42, -83, -70, -88, 26, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 3, -126, 1, -127, 0, 65, 81, 121, -11, 9, 99, 106, -28, -38, 58, 58, 118, 89, 67, 23, -100, -48, -110, -36, 64, -49, -37, 95, 25, 122, 29, -58, -83, 108, -101, -100, -12, 58, 112, 77, 81, 122, -124, -2, -82, -19, 11, 24, -105, -50, 69, 25, 27, 66, -84, -16, 46, 62, 76, 100, -32, 42, 52, 119, 47, -16, -89, 39, -62, 52, -124, -74, -41, 119, 52, 9, -35, -18, -22, -95, 35, 105, -82, -22, -37, 38, -82, 35, 36, -106, 92, -25, -40, -21, -112, -40, -60, -65, 45, 85, 45, -71, 80, -7, 65, 117, 120, -125, 69, -42, -70, 43, -125, -112, -127, 117, -70, 99, 82, 74, -102, -64, -82, 64, -8, -69, 35, -99, -29, -113, -44, 31, 27, -4, 54, -105, 93, 120, -87, 126, 62, -99, -73, -26, 59, 61, 79, 10, -119, -66, -3, 5, 7, 34, 122, -112, -11, -125, -64, 12, 89, 125, 62, 80, -43, -77, -120, -45, -91, 109, -105, -120, 93, -40, 93, -79, -87, -48, -72, -90, -122, 94, 95, 23, -14, -51, 115, -120, -11, -123, -30, -110, 96, -21, -96, -61, -118, -85, -106, 25, -126, -18, 48, -93, 47, -73, -46, 110, 74, 103, -119, -61, -85, 13, 106, -8, 10, 7, 102, 6, 84, -58, 45, -9, 76, -61, 65, 5, -115, 90, -89, -111, 86, 53, 29, -76, -10, -110, -49, -51, 108, -59, -128, 120, 99, -35, -117, -17, 45, -25, 105, -20, 66, -14, -12, -85, 8, -121, 36, -62, -82, -43, 53, 64, 111, -72, 63, -36, -78, -48, -73, 28, -115, 16, 85, 19, 126, 7, -114, 125, 28, -69, 82, 71, -15, -50, 107, -110, -117, 79, 71, 111, -55, -39, 28, -82, -121, 37, 33, -105, 62, -43, -57, 16, -72, -110, 34, -103, -107, -21, -15, 12, 113, -40, 35, 35, -35, -37, -51, 39, -114, 60, -2, -102, -69, 14, -112, 86, 58, -96, 94, 74, 2, -127, 56, -61, 89, -121, 66, -127, 83, -128, -82, 42, -94, -80, 80, -106, 35, 115, 77, -30, -79, -87, -43, -42, 78, -41, -9, 84, -57, -18, -26, 94, -36, -125, 46, -83, 34, -106, -62, 41, -116, 45, 62, 70, 102, 103, 115, 41, 109, -32, -114, -68, 19, -4, 62, 114, -126
-#elif channel_beta
-#define SIGNATURE 48, -126, 4, 105, 48, -126, 2, -47, -96, 3, 2, 1, 2, 2, 21, 0, -121, 121, 20, -109, -117, -41, 17, 61, -22, 52, -78, -99, 104, -45, 89, -7, 73, 30, -115, 52, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, 30, 23, 13, 49, 54, 48, 56, 50, 51, 50, 48, 48, 56, 48, 57, 90, 23, 13, 52, 52, 48, 49, 49, 48, 50, 48, 48, 56, 48, 57, 90, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, -126, 1, -94, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, -113, 0, 48, -126, 1, -118, 2, -126, 1, -127, 0, -51, -66, 2, -125, -32, 35, -23, 78, -18, -96, -109, -40, 114, 83, 39, 1, 53, 94, 37, -100, 5, -23, -59, -29, 122, -109, -46, 71, -106, 1, 29, 45, 56, 10, 41, 77, 5, -61, -26, 94, -37, -123, -59, 37, -119, -81, 33, 66, 60, 12, 56, 2, 37, -105, 101, 127, -30, -22, -110, -43, -126, 117, 73, 107, -65, 88, 111, 41, 100, -79, -105, 39, 50, 123, -28, 22, -73, 20, -121, 96, -43, -114, -122, 75, -105, 108, -83, -42, 84, 68, 99, -12, 50, 112, -113, -82, -71, -14, -14, 119, 99, -122, 127, -19, 39, -95, 125, -4, 94, 33, -75, 90, -95, -120, 10, 58, 105, 35, -9, -4, -50, -116, 17, -102, -10, -95, 68, -39, 28, 77, -31, 31, -45, -80, 64, 94, 15, 11, 69, -44, -119, 52, -61, -6, 30, -124, -60, -31, 51, -76, -56, 120, 78, 126, 0, 112, -37, -43, 29, 55, -34, 80, 81, -12, -3, -94, 27, 64, 14, 82, 127, -112, 28, 103, -51, -39, -111, -122, 15, 45, -114, -89, 43, -75, 91, 58, -76, 3, -84, -100, -65, -48, -51, 52, -82, -57, -107, 27, -41, -48, -109, -101, 114, 28, -32, -123, -38, -80, 121, -112, 92, -4, -87, -32, -63, 127, 58, 57, 104, -67, -71, 122, -66, -11, 41, -79, 65, -43, -43, -30, -69, -29, -25, 14, 19, 56, 34, -53, 3, -63, 50, 84, -60, -62, 74, -31, 59, 127, 96, 53, 124, -91, 114, 11, -127, 16, -24, -38, -124, -82, -96, 63, 113, 22, 127, -48, 117, -104, 13, 46, -11, -22, 110, 46, -109, 118, -50, 58, -115, -119, -24, 22, 75, 121, 80, -83, -31, -76, 48, 11, -22, 91, -113, -109, 74, -78, 52, 3, -1, -83, 80, 97, -21, -52, -51, 82, 21, -109, -8, -126, -115, -64, 112, 100, 32, -23, 116, 55, -27, 39, -3, 1, -86, -65, -102, 14, -33, -51, 33, 21, 75, -9, 120, -126, 39, 27, 85, -116, -85, -49, -33, 8, 116, 90, 49, -106, -5, 3, 36, 84, -99, 97, -55, 16, 39, 8, -58, -24, 56, 2, -37, -117, 83, -50, 78, -65, 93, -76, -100, 17, 104, -114, -104, -96, 109, 62, -109, 26, -81, 109, 74, -58, 79, 29, 2, 3, 1, 0, 1, -93, 84, 48, 82, 48, 14, 6, 3, 85, 29, 15, 1, 1, -1, 4, 4, 3, 2, 2, -92, 48, 19, 6, 3, 85, 29, 37, 4, 12, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 48, 12, 6, 3, 85, 29, 19, 1, 1, -1, 4, 2, 48, 0, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, -10, 51, -84, -48, 32, -35, -13, -60, 91, -72, -17, -52, -73, 93, -90, 105, 124, -108, -33, -18, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 3, -126, 1, -127, 0, -104, -10, -87, -123, -65, -127, 47, -21, -23, 48, 58, 109, -69, -38, -32, 12, -51, 79, 117, 48, 25, 121, -117, 51, -83, 127, 8, -71, -124, -107, 111, 47, -29, 115, -5, 97, 79, -45, -36, -1, 55, -55, 11, 27, 108, 41, -33, -93, -30, 115, 63, 27, -99, -21, -16, 41, 53, 60, 55, -44, 71, -81, 119, -85, 11, 111, -73, -50, 76, 96, 114, 119, 47, -77, 4, -37, -55, -111, -118, 4, -93, 71, -126, 4, -27, 66, 115, -24, 38, -56, 99, -1, -20, 100, -45, 102, 41, 32, -128, 22, -26, -107, 90, 94, -104, -96, 31, -63, 41, 6, 29, 75, 99, 46, -10, -107, -88, -127, 55, -107, 52, -4, -23, -7, -101, 1, -102, -42, 38, 73, 102, -19, 53, 56, -4, 27, 125, 117, 121, 84, 88, 30, 102, -38, -104, 70, -6, 90, -68, -72, 36, -52, -14, 46, -28, -88, -110, -54, -30, -102, -87, -60, -52, 12, -125, 109, 113, 94, 82, -73, -37, 5, 54, 65, -125, -59, 107, 31, -104, 27, -54, -77, -60, 18, 97, 12, -67, -50, -23, -36, 46, -106, 62, 107, -92, -71, 29, -13, -98, 127, 50, 85, 28, -43, -93, -91, 2, -26, -79, 57, 98, -116, -73, 13, -58, -67, -56, -69, -113, -89, -81, 110, -74, 66, -54, 17, 88, -40, 37, 56, -91, 21, 54, 107, 39, -38, 2, -57, -1, 9, 20, -12, 92, 109, 64, 6, -116, 71, 23, -109, 5, -8, -66, -11, -90, 50, -64, 111, 76, -103, -59, 124, 114, -104, -33, 3, 73, -33, -15, 6, -104, -73, 24, -59, 50, 64, -29, -45, 48, 105, -56, 75, -61, -37, 67, -82, -12, -120, -92, 109, 118, 28, 119, -70, -96, 80, 7, -20, -73, -4, 61, 66, -87, 31, -113, 57, -116, -62, 93, 62, 30, 62, -55, 91, 56, 54, -29, 108, -92, 91, 20, -45, -69, -12, -8, -91, 70, 14, 42, 69, -101, 21, 115, -103, 80, -35, -108, -45, -70, -37, -31, -49, -7, -122, -89, -41, -45, 7, 89, -101, -5, -101, -69, 78, 8, 39, 4, 44, -13, 84, 28, -9, 78, 107, 19, -74, 11, -122, -52, 65, 32, 100, 101, -70, -111, 53, -30, 127, -1, 105, -22, -92, -18, 105
-#elif channel_canary
-#define SIGNATURE 48, -126, 4, 104, 48, -126, 2, -48, -96, 3, 2, 1, 2, 2, 20, 72, 93, 81, -18, -49, -94, 41, -90, -128, -77, -127, 16, 84, -115, 114, -47, -2, -1, 81, -54, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, 30, 23, 13, 49, 54, 48, 56, 50, 51, 50, 48, 48, 56, 49, 50, 90, 23, 13, 52, 52, 48, 49, 49, 48, 50, 48, 48, 56, 49, 50, 90, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, -126, 1, -94, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, -113, 0, 48, -126, 1, -118, 2, -126, 1, -127, 0, -65, -59, 68, 42, -49, 27, 116, 117, 109, 58, -32, 65, -89, 48, -31, -73, 6, -10, 33, 77, -40, -4, 120, 111, -63, 96, -16, -79, 77, -45, -14, 113, -3, 45, -69, 55, -122, -61, -127, 106, -49, -60, -20, -65, 122, 50, 30, -116, 124, -121, 12, -110, 83, -127, 3, 79, -89, 123, -69, -65, -20, -18, 56, -69, 20, -54, 87, 77, -50, 65, -107, 69, 84, -90, 4, -27, -108, 22, -82, -3, -127, -36, -61, 73, 84, 103, -90, -113, -12, -124, 27, 74, 27, -77, 87, -1, 59, -66, 19, 0, -25, -74, 50, 52, 12, -122, 88, -104, 89, -113, -22, 9, 76, 61, -54, -59, 125, -128, -120, 58, -6, -2, -49, -71, -22, -40, 0, -94, -6, 27, 21, -17, -33, -122, 115, 3, 72, 123, -35, 113, 33, -43, -116, 53, -63, 123, -40, -39, -66, -28, -22, 29, 16, -81, 94, 108, 125, -54, -29, 76, 14, 11, 39, -90, 96, -93, 57, -54, 89, -61, -107, -122, -71, -89, -58, 90, -70, 9, -120, -15, 69, 64, 117, -88, -100, -64, -33, 28, -117, 55, 57, -47, -126, -17, 14, 9, 89, 123, 17, 72, 90, 126, 65, 42, -26, -111, -87, -71, 17, -26, -27, -64, -19, 123, -31, -90, 4, 96, 49, 116, 41, -107, 73, -24, -23, 86, 108, 27, -33, 3, 28, 58, 1, -3, -102, -36, -117, 1, -117, 127, 100, -102, 122, -7, 78, 81, -31, 51, 127, -40, -41, -114, -66, 113, 12, 49, -21, 27, -65, -33, 50, -89, -64, 127, 50, -79, -14, -95, -92, -72, -64, 63, 11, 42, 126, -25, -6, -124, 114, 96, 15, -81, -104, 115, -29, -84, -42, -54, 106, -85, 37, 47, 71, -68, 94, 59, 67, 109, 126, -107, 0, -65, -74, -34, 29, -19, -5, -57, 3, -52, 71, 75, 68, 90, 123, 12, -92, 6, 92, -94, 25, 7, 62, 27, 14, 59, 107, 23, 16, 116, -23, 66, 72, -113, -62, -56, 70, -63, -97, -92, 67, -42, -103, 74, 78, -25, 80, 56, -75, 11, -117, -72, -109, 11, 30, 99, 62, -88, 68, 119, -24, -25, 25, 83, 52, -114, 105, 53, 115, 78, 97, 19, -18, 93, -47, 117, 122, 40, -57, -75, 20, -105, 54, -95, 2, 3, 1, 0, 1, -93, 84, 48, 82, 48, 14, 6, 3, 85, 29, 15, 1, 1, -1, 4, 4, 3, 2, 2, -92, 48, 19, 6, 3, 85, 29, 37, 4, 12, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 48, 12, 6, 3, 85, 29, 19, 1, 1, -1, 4, 2, 48, 0, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, -64, -105, 43, 96, 47, 50, 80, -42, -87, -72, -31, -74, -72, -32, 34, -107, 62, -31, 74, -36, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 3, -126, 1, -127, 0, -120, -33, -25, 88, -72, -37, 94, 98, 81, 117, -29, 63, -4, -76, -60, 59, -24, 90, 55, -51, 121, 47, -127, 125, -106, 38, 89, -43, -122, 0, 45, -105, -33, 7, -36, -71, 1, -82, -128, -105, 113, 65, 111, 96, 46, 32, -113, 31, -98, 2, -93, 94, -78, -37, 74, -124, -19, 52, -54, -8, 25, -14, 1, 44, 59, -101, 34, 35, 33, 38, -109, -57, 4, 57, 24, 23, 78, -72, -31, 27, 106, 58, 32, 39, 33, 56, 3, 88, -12, 18, 78, -64, -5, 59, -9, -27, -119, -8, 21, 36, 16, -17, -12, -111, -16, 16, 5, 97, -22, 20, 63, 61, -46, -26, 83, 123, -115, -30, -115, -96, 51, -110, 95, 105, -39, 96, 73, 90, 2, 24, 67, 6, -52, -56, -43, -124, -113, 77, -107, 0, 68, 58, 115, -96, -56, -61, -46, 118, 64, 59, 126, 5, 72, 44, 95, 111, -69, 18, -10, -78, -53, -59, -32, 80, -18, -71, 49, 65, -65, 7, -60, -88, 104, 63, -14, 35, 21, 127, -69, -16, 64, -42, 62, -101, -100, -85, -124, 26, 85, -53, -21, 122, 70, 49, 45, 107, 55, 24, -123, 117, 122, -28, 71, 112, -110, -32, -37, -121, 96, 86, -12, -75, -31, -109, -97, -22, -113, -74, -112, -41, -51, -85, -23, 82, 61, -40, 104, 4, 85, -102, -14, -39, 110, -96, 22, -36, 54, -96, 101, 50, -41, 42, 54, 65, -19, 116, 63, 56, 67, -85, 3, 30, 97, -66, -1, -77, 115, 64, 109, 24, -21, 84, -6, 115, 97, -60, 4, -67, -55, -111, -118, 6, -98, 73, -33, -75, -82, 29, 24, 88, -46, 89, 53, -25, 44, -117, -35, -56, 78, 90, -101, 110, 63, -18, -47, 121, 69, 77, 37, -33, 7, -35, -61, 37, 97, -27, 62, -23, -87, -101, -125, 72, 120, -70, -60, 14, -9, 18, -68, 14, 67, 29, 89, 7, 100, -44, 26, 92, -38, 31, 107, -96, -86, 86, 64, 30, 3, -51, 98, 3, 120, -67, -36, -14, 125, -19, 55, -46, 49, -120, -6, -64, 83, 98, 58, -117, 31, 101, 8, -71, 13, -84, -28, 8, -10, 48, 49, -32, -101, 109, 98, 91, 71, -109, 44, 64, -90, -18, 89, 30, -76, 36, 29, 38
-#elif channel_dev
 #define SIGNATURE 48, -126, 4, 104, 48, -126, 2, -48, -96, 3, 2, 1, 2, 2, 20, 120, 33, -22, -36, -115, 7, 116, 66, 116, 113, -122, -126, -124, 32, 44, 72, -43, 127, -13, -11, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, 30, 23, 13, 49, 54, 48, 56, 50, 51, 50, 48, 48, 56, 49, 49, 90, 23, 13, 52, 52, 48, 49, 49, 48, 50, 48, 48, 56, 49, 49, 90, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, -126, 1, -94, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, -113, 0, 48, -126, 1, -118, 2, -126, 1, -127, 0, -60, 57, -128, 54, 45, -71, -75, 1, 42, 100, 64, 111, -6, 104, -8, -57, -45, 42, 33, 124, 43, 20, -115, -88, -100, -115, -19, 90, 25, 16, 50, -96, 27, 4, 64, -65, 92, -74, 61, 98, 54, 88, 122, -71, -116, -86, -26, -30, -34, -16, -87, -113, 103, -81, -76, -2, -120, -124, -70, 41, 30, 46, 75, 119, 22, 61, 92, -8, 92, 55, 8, -53, -70, 100, 40, 77, 77, 10, -5, -74, 37, 48, -71, 108, 92, -72, -56, 17, 39, -11, 61, -86, -52, -3, -128, 72, 68, 27, 28, -128, -126, 91, 64, -117, -67, 58, 114, -70, 112, -44, -70, -51, -81, -95, -78, 12, -1, 123, 68, -96, -32, -76, 65, -59, 37, -31, 88, 34, -125, -67, 58, 77, 75, -112, 23, 127, -15, -45, 56, -10, 46, -40, -82, -49, 42, 35, 127, -70, 120, -37, 8, 24, 38, 59, -1, -39, -104, -85, 101, 43, 39, 107, -35, -7, -80, -38, -47, -86, -53, -94, 11, 62, 86, 78, 72, 90, -54, 5, 21, -72, -122, 14, 102, 95, -81, 94, -11, -101, 36, -31, 104, -66, -51, 24, -99, 74, 21, -106, 76, 67, 86, 49, 121, 59, -7, 79, -87, -115, 27, -82, -98, 15, -5, 13, -90, -20, 70, 17, -68, 124, 82, 35, 53, 76, -52, 69, 23, -97, -2, -117, 7, -27, 79, 46, -54, -99, 41, -22, -108, -25, 8, 108, 109, -23, 13, -11, 56, 11, -71, 20, 37, -64, -13, -114, -34, -78, -49, 7, 45, 80, 7, 69, 106, 3, -128, 70, 0, 41, -105, 76, 106, -114, 18, -22, 92, -113, -12, 109, 11, 111, 48, -63, 22, 29, -5, -113, -2, 117, -105, 1, -21, 40, 23, -8, -36, -109, -41, -1, 92, 94, 51, 122, -67, -116, -64, 28, 38, 112, 4, -25, -18, -92, 53, -119, -37, 58, -28, 62, -88, 126, -66, -113, -101, 57, -66, -48, -88, -47, -65, -65, 108, -116, -52, -87, -33, 30, 121, -6, -50, -97, 99, -102, 106, -31, 119, -26, -49, 63, 90, -19, 119, 103, -83, 125, 29, -32, -102, -97, -99, -45, 59, 36, 30, 58, 28, 59, 2, 48, 0, -76, 108, 98, 62, 68, -11, -82, -74, -38, 93, -22, -79, -110, 73, 13, 2, 3, 1, 0, 1, -93, 84, 48, 82, 48, 14, 6, 3, 85, 29, 15, 1, 1, -1, 4, 4, 3, 2, 2, -92, 48, 19, 6, 3, 85, 29, 37, 4, 12, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 48, 12, 6, 3, 85, 29, 19, 1, 1, -1, 4, 2, 48, 0, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 62, -89, -113, -1, -62, -65, -19, 4, 56, -51, 41, -53, 51, 41, -28, -42, -36, 31, -89, -19, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 3, -126, 1, -127, 0, 27, -3, -41, -125, 123, -93, 20, -83, -83, -13, -120, -13, -123, 29, 107, -9, 77, -75, -118, 106, 5, 113, -11, 40, -84, 98, -126, -55, 39, 77, -87, -53, -5, 122, -63, -108, 23, 112, -88, 17, 28, 15, -103, 75, 75, 123, -21, 119, 125, -78, -125, 87, 78, 41, -103, 6, 9, 91, 89, 42, 86, 25, -62, -30, 117, 22, -107, 53, 66, 56, -23, -115, 16, -85, -38, 77, -117, -87, 106, -4, -91, 36, 81, 40, -31, 123, -40, 69, -119, -69, -83, -78, 64, 123, 46, 36, 31, -29, 64, 36, -8, -22, -93, 22, -48, -49, 11, -67, 48, -1, 26, 94, -82, 65, 6, 82, 18, 91, -56, 118, -55, 111, -18, -114, -86, -98, -3, 40, -48, -46, -102, -120, 20, 101, -60, 91, -103, -92, 43, 12, -104, -10, -60, 10, -31, -26, 111, -125, 103, 102, -105, 116, -66, -89, -58, 11, 34, 1, 22, 112, 126, 81, 115, -104, -120, 115, -43, -59, 50, -67, -42, -42, 122, -66, 57, 121, 30, -22, -8, 96, 114, -110, -98, 102, 41, -64, 115, -44, -84, 10, 63, 19, -105, 118, -2, 98, 45, -49, 94, 114, 81, -84, -111, -34, -23, 49, 73, 91, 76, 81, 90, 86, 112, 12, -87, -41, -61, -82, 32, -87, -7, 95, -28, -25, -9, -38, -102, 54, -81, -109, -126, 59, -82, 103, 126, -92, -51, -81, 44, 61, 103, 127, -1, -23, -84, -35, -43, -88, 41, 120, 76, 120, 39, -124, 81, 90, 64, 69, 112, 18, 54, -115, -7, 12, -110, 105, 48, -44, 88, 35, -104, 107, -83, -4, -16, 123, 59, -13, 121, 63, -89, 118, 100, 38, 118, -30, 9, -57, -54, -108, -26, -45, 29, -22, 57, -81, 83, -124, -114, -50, 16, 78, 23, -41, -4, 119, -13, -68, 38, -23, 56, 22, -96, -63, -27, 70, -94, -35, 111, -45, -9, 59, -90, -27, 103, 95, 16, 127, -118, -98, -75, -52, 7, 32, 65, -27, -68, 62, -81, 98, -54, -80, -23, 59, 38, -127, 96, 71, 123, 34, -113, -23, -80, 32, -97, -55, -100, 121, 120, 50, -48, 58, 69, -105, 26, 126, 30, -1, -112, -41, -18, -16, 62, 48, -22, -2, 19, 117, -6, 59, 74, -13, 92, -1
-#elif channel_stable
-#define SIGNATURE 48, -126, 4, 105, 48, -126, 2, -47, -96, 3, 2, 1, 2, 2, 21, 0, -54, -123, 70, 125, -114, 20, 103, -74, -80, -36, -8, -128, -15, -61, 29, -35, -127, 116, 18, -4, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, 30, 23, 13, 49, 54, 48, 56, 50, 51, 50, 48, 48, 56, 48, 54, 90, 23, 13, 52, 52, 48, 49, 49, 48, 50, 48, 48, 56, 48, 54, 90, 48, 67, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 71, 111, 111, 103, 108, 101, 49, 22, 48, 20, 6, 3, 85, 4, 11, 19, 13, 67, 104, 114, 111, 109, 101, 32, 87, 101, 98, 65, 80, 75, 49, 11, 48, 9, 6, 3, 85, 4, 3, 19, 2, 67, 65, 48, -126, 1, -94, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, -113, 0, 48, -126, 1, -118, 2, -126, 1, -127, 0, -14, -95, 116, 76, 76, 30, 27, -37, -125, -52, 112, 114, 111, 4, -76, -81, -99, 45, 24, 50, -91, -46, -33, 83, 39, -42, 77, 100, 91, -88, 33, 30, -104, -15, -117, 79, 59, -44, 40, 109, 27, 110, -100, 23, 72, -21, -40, 101, -75, 58, 42, 97, 71, 36, 59, -81, -30, 16, 17, -126, 106, 85, 82, 106, 117, -18, -106, -75, 89, 34, 7, -83, -18, 103, -17, -19, -2, 110, 114, 12, -58, -72, 26, 55, 75, 105, -12, -81, 39, 90, 29, 76, 75, -97, -115, -71, -78, 41, -114, -15, -41, -88, 19, 116, 89, 4, 118, -82, 101, -97, 73, 7, -81, -39, -83, 106, 93, 45, 46, -111, -96, -85, 25, 91, 124, -89, -53, -99, -113, 88, -6, -8, 118, -111, 69, -123, -61, 35, 39, 24, 126, -50, 4, -92, -29, -18, 96, 55, -68, -27, 71, -87, -118, -85, -23, -77, -105, -47, 101, -55, 32, 54, 11, 117, -41, -115, 7, -81, 104, -102, -119, 117, -61, 100, -85, -34, 27, -89, 92, -56, 115, -12, -39, -84, -7, 25, 52, 94, 98, -43, 15, 8, -120, 88, -95, -32, 54, -80, -81, -71, -56, 35, -8, 57, -11, 30, 39, 47, -17, -119, -77, -1, -116, 60, -100, -96, -92, -33, 67, -49, -116, 72, -12, 122, -9, 45, -66, -124, 94, -103, 37, 29, 83, -22, -124, 9, -84, -124, -81, -88, -48, -8, 9, 127, 98, -91, 115, -83, 84, -102, -69, 33, -124, 19, 90, -79, -124, 11, 35, 85, 57, 116, 33, -34, 97, 0, -63, 123, 85, 47, 118, 102, -28, 42, 78, 114, -126, 65, -75, 37, 84, -112, 8, 27, -44, -6, -27, -102, 76, 111, -109, -20, 3, -85, 45, 28, 18, 101, 27, 104, -91, 87, -90, -37, -120, -55, 39, 114, -67, 54, 94, -55, 121, -20, -88, -125, 79, -97, 59, 65, 1, -42, -89, 37, -114, 32, -110, 48, 60, 73, 90, -100, -46, 49, -42, 25, 124, 110, 125, -115, -31, 93, 99, -35, -64, 3, -49, -92, -126, -60, 17, -72, -38, -120, -46, 123, 40, 114, -107, -63, 34, -61, -81, 7, -108, 71, -77, 73, 127, -36, -57, -3, 20, 122, 64, 31, 23, -28, 75, 63, 74, -72, 66, -79, 2, 3, 1, 0, 1, -93, 84, 48, 82, 48, 14, 6, 3, 85, 29, 15, 1, 1, -1, 4, 4, 3, 2, 2, -92, 48, 19, 6, 3, 85, 29, 37, 4, 12, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 48, 12, 6, 3, 85, 29, 19, 1, 1, -1, 4, 2, 48, 0, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, -95, 16, -62, -118, 72, 120, -127, 71, -97, 32, -60, 22, 94, 94, -97, -45, 72, -57, 106, 8, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 3, -126, 1, -127, 0, 124, 91, 76, 14, 98, -124, 61, 54, 77, 113, -107, -95, -1, 107, -33, 93, 101, -19, -2, -49, -106, 61, -102, -71, -67, 38, 108, 15, -82, 43, 102, 62, 101, -39, -10, 49, 81, -38, -107, -25, 48, -43, 9, -102, -72, -22, 12, -108, 60, -15, -11, -83, -15, 46, 122, -15, 115, -73, -53, -84, -20, 88, -32, 77, -69, -52, -47, 14, 53, -26, 93, -69, -85, -74, -31, 2, 37, -13, -52, -97, 13, -36, 12, -38, 92, 76, 61, 112, 67, -52, 109, -42, 30, 36, 92, 102, 96, -105, -128, 87, -108, 86, 45, 117, 49, 70, 58, 38, -75, 45, 38, -18, 47, -57, 127, 122, 118, -32, 119, 48, 54, 101, -118, 110, -55, 119, -77, -85, 20, -104, 63, 32, 121, 99, -91, 43, 3, -30, -43, 109, -8, -22, 13, -125, -80, -18, -108, 118, -60, 106, 44, 115, 1, 110, -82, 9, 30, 52, -104, -94, 72, 49, 55, -51, -21, -43, 18, 91, 25, -80, 61, 76, -61, 36, -42, -127, -83, -63, 55, -109, -17, 40, -122, 68, 73, -49, -84, 115, 102, -33, 53, 31, -123, 34, -45, -81, -13, 30, -16, -40, -87, 116, -13, -51, -75, 96, -28, 11, 82, -83, 11, 60, 10, -57, 5, -53, -103, 48, -46, 58, 106, 48, -84, 125, 89, 80, 117, 22, -32, 54, 12, -110, -10, 116, 60, 52, -40, -121, -19, -89, -71, -8, -60, 40, 16, -44, -46, -37, -84, 84, -10, -73, 31, 70, -60, 94, -121, 16, -60, 108, 58, -72, -46, -107, 41, 98, 106, 91, 82, -106, 71, -59, 105, -32, -61, -124, -69, -58, 58, 106, 78, 123, -80, -123, -106, -64, 116, 116, -46, -67, -3, 70, 54, 119, 112, 121, -113, 121, 21, -72, -71, -123, 24, -49, -14, -24, 5, 77, -18, -52, -10, -119, 31, -101, -90, 34, 7, -61, -16, -45, 0, 2, 22, 63, 80, 80, -64, -59, 4, -90, -79, 10, -59, -77, -43, -114, -117, -71, -25, 3, -45, 54, -1, -27, -89, 74, 103, 118, 76, 73, -112, 39, -79, 42, -19, 70, 54, -15, -107, 74, 8, -95, -95, 33, -122, 24, 62, -7, -40, -58, -33, 123, -77, 100, -71, -83, -105, -122, 70, -50, -23, -17, 75, 51
-#else
-#error "unknown channel"
-#endif
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
index 8ed416a..090e1f2 100644
--- a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
@@ -56,12 +56,10 @@
     webapk::InstallSource webapk_install_source) {
   bool is_webapk = ChromeWebApkHost::AreWebApkEnabled();
   std::string webapk_package_name = "";
-  if (is_webapk) {
-    webapk_package_name = ShortcutHelper::QueryWebApkPackage(
-        web_contents->GetLastCommittedURL());
-  }
-  bool is_webapk_already_installed = !webapk_package_name.empty();
   const GURL& url = shortcut_info->url;
+  if (is_webapk)
+    webapk_package_name = ShortcutHelper::QueryWebApkPackage(url);
+  bool is_webapk_already_installed = !webapk_package_name.empty();
   auto infobar_delegate =
       base::WrapUnique(new banners::AppBannerInfoBarDelegateAndroid(
           weak_manager, app_title, std::move(shortcut_info), std::move(icon),
diff --git a/chrome/browser/android/webapk/webapk_icon_hasher.cc b/chrome/browser/android/webapk/webapk_icon_hasher.cc
index 8004d9e..c685e08 100644
--- a/chrome/browser/android/webapk/webapk_icon_hasher.cc
+++ b/chrome/browser/android/webapk/webapk_icon_hasher.cc
@@ -6,6 +6,8 @@
 
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "net/base/data_url.h"
 #include "net/http/http_status_code.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -17,6 +19,17 @@
 // The seed to use when taking the murmur2 hash of the icon.
 const uint64_t kMurmur2HashSeed = 0;
 
+// Computes Murmur2 hash of |raw_image_data|.
+std::string ComputeMurmur2Hash(const std::string& raw_image_data) {
+  // WARNING: We are running in the browser process. |raw_image_data| is the
+  // image's raw, unsanitized bytes from the web. |raw_image_data| may contain
+  // malicious data. Decoding unsanitized bitmap data to an SkBitmap in the
+  // browser process is a security bug.
+  uint64_t hash = MurmurHash64A(&raw_image_data.front(), raw_image_data.size(),
+                                kMurmur2HashSeed);
+  return base::Uint64ToString(hash);
+}
+
 }  // anonymous namespace
 
 WebApkIconHasher::WebApkIconHasher() {}
@@ -27,8 +40,19 @@
     net::URLRequestContextGetter* request_context_getter,
     const GURL& icon_url,
     const Murmur2HashCallback& callback) {
-  callback_ = callback;
+  if (icon_url.SchemeIs(url::kDataScheme)) {
+    std::string mime_type, char_set, data;
+    std::string hash;
+    if (net::DataURL::Parse(icon_url, &mime_type, &char_set, &data) &&
+        !data.empty()) {
+      hash = ComputeMurmur2Hash(data);
+    }
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  base::Bind(callback, hash));
+    return;
+  }
 
+  callback_ = callback;
   url_fetcher_ = net::URLFetcher::Create(icon_url, net::URLFetcher::GET, this);
   url_fetcher_->SetRequestContext(request_context_getter);
   url_fetcher_->Start();
@@ -47,7 +71,5 @@
   // browser process is a security bug.
   std::string raw_image_data;
   source->GetResponseAsString(&raw_image_data);
-  uint64_t hash = MurmurHash64A(&raw_image_data.front(), raw_image_data.size(),
-                                kMurmur2HashSeed);
-  callback_.Run(base::Uint64ToString(hash));
+  callback_.Run(ComputeMurmur2Hash(raw_image_data));
 }
diff --git a/chrome/browser/android/webapk/webapk_icon_hasher_unittest.cc b/chrome/browser/android/webapk/webapk_icon_hasher_unittest.cc
index e34f8954..ba4cc6dd 100644
--- a/chrome/browser/android/webapk/webapk_icon_hasher_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_icon_hasher_unittest.cc
@@ -97,6 +97,22 @@
   EXPECT_EQ(kIconMurmur2Hash, runner.murmur2_hash());
 }
 
+TEST_F(WebApkIconHasherTest, DataUri) {
+  GURL icon_url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA"
+      "AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO"
+      "9TXL0Y4OHwAAAABJRU5ErkJggg==");
+  WebApkIconHasherRunner runner;
+  runner.Run(icon_url);
+  EXPECT_EQ("536500236142107998", runner.murmur2_hash());
+}
+
+TEST_F(WebApkIconHasherTest, DataUriInvalid) {
+  GURL icon_url("data:image/png;base64");
+  WebApkIconHasherRunner runner;
+  runner.Run(icon_url);
+  EXPECT_EQ("", runner.murmur2_hash());
+}
+
 // Test that the hash callback is called with an empty string if an HTTP error
 // prevents the icon URL from being fetched.
 TEST_F(WebApkIconHasherTest, HTTPError) {
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 6710125..05383ffe 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -100,6 +100,7 @@
       <include name="IDR_BLUETOOTH_INTERNALS_ADAPTER_BROKER_JS" file="resources\bluetooth_internals\adapter_broker.js" type="BINDATA" compress="gzip" />
       <include name="IDR_BLUETOOTH_INTERNALS_ADAPTER_PAGE_JS" file="resources\bluetooth_internals\adapter_page.js" type="BINDATA" compress="gzip" />
       <include name="IDR_BLUETOOTH_INTERNALS_CHARACTERISTIC_LIST_JS" file="resources\bluetooth_internals\characteristic_list.js" type="BINDATA" compress="gzip" />
+      <include name="IDR_BLUETOOTH_INTERNALS_DESCRIPTOR_LIST_JS" file="resources\bluetooth_internals\descriptor_list.js" type="BINDATA" compress="gzip" />
       <include name="IDR_BLUETOOTH_INTERNALS_DEVICE_BROKER_JS" file="resources\bluetooth_internals\device_broker.js" type="BINDATA" compress="gzip" />
       <include name="IDR_BLUETOOTH_INTERNALS_DEVICE_COLLECTION_JS" file="resources\bluetooth_internals\device_collection.js" type="BINDATA" compress="gzip" />
       <include name="IDR_BLUETOOTH_INTERNALS_DEVICE_DETAILS_PAGE_JS" file="resources\bluetooth_internals\device_details_page.js" type="BINDATA" compress="gzip" />
diff --git a/chrome/browser/browsing_data/browsing_data_remover_impl_unittest.cc b/chrome/browser/browsing_data/browsing_data_remover_impl_unittest.cc
index 30fc67e6..5059421 100644
--- a/chrome/browser/browsing_data/browsing_data_remover_impl_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_remover_impl_unittest.cc
@@ -26,6 +26,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/browsing_data/browsing_data_filter_builder.h"
 #include "chrome/browser/browsing_data/browsing_data_helper.h"
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index 8963d79..4e89ac2c 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -645,7 +645,8 @@
             CONTENT_SETTINGS_TYPE_APP_BANNER,
             base::Bind(&WebsiteSettingsFilterAdapter, filter));
 
-    PermissionDecisionAutoBlocker::RemoveCountsByUrl(profile_, filter);
+    PermissionDecisionAutoBlocker::GetForProfile(profile_)->RemoveCountsByUrl(
+        filter);
   }
 
   //////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index bdbf784e..009376f 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browsing_data/browsing_data_filter_builder.h"
@@ -551,31 +552,27 @@
 class RemovePermissionPromptCountsTest {
  public:
   explicit RemovePermissionPromptCountsTest(TestingProfile* profile)
-      : profile_(profile) {}
+      : autoblocker_(PermissionDecisionAutoBlocker::GetForProfile(profile)) {}
 
   int GetDismissCount(const GURL& url, content::PermissionType permission) {
-    return PermissionDecisionAutoBlocker::GetDismissCount(
-        url, permission, profile_);
+    return autoblocker_->GetDismissCount(url, permission);
   }
 
   int GetIgnoreCount(const GURL& url, content::PermissionType permission) {
-    return PermissionDecisionAutoBlocker::GetIgnoreCount(
-        url, permission, profile_);
+    return autoblocker_->GetIgnoreCount(url, permission);
   }
 
   int RecordIgnore(const GURL& url, content::PermissionType permission) {
-    return PermissionDecisionAutoBlocker::RecordIgnore(url, permission,
-                                                       profile_);
+    return autoblocker_->RecordIgnore(url, permission);
   }
 
   bool ShouldChangeDismissalToBlock(const GURL& url,
                                     content::PermissionType permission) {
-    return PermissionDecisionAutoBlocker::ShouldChangeDismissalToBlock(
-        url, permission, profile_);
+    return autoblocker_->RecordDismissAndEmbargo(url, permission);
   }
 
  private:
-  TestingProfile* profile_;
+  PermissionDecisionAutoBlocker* autoblocker_;
 
   DISALLOW_COPY_AND_ASSIGN(RemovePermissionPromptCountsTest);
 };
diff --git a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
index 82b9e01..01c3c6e 100644
--- a/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
+++ b/chrome/browser/chromeos/display/touch_calibrator/touch_calibrator_view.cc
@@ -515,7 +515,7 @@
   touch_point_view_->SetBounds(kTouchPointViewOffset, kTouchPointViewOffset,
                                kTapLabelWidth, kTouchPointViewHeight);
   touch_point_view_->SetVisible(false);
-  touch_point_view_->SetPaintToLayer(true);
+  touch_point_view_->SetPaintToLayer();
   touch_point_view_->layer()->SetFillsBoundsOpaquely(false);
   touch_point_view_->layer()->GetAnimator()->AddObserver(this);
   touch_point_view_->set_background(
@@ -572,7 +572,7 @@
   completion_message_view_ =
       new CompletionMessageView(msg_view_bounds, finish_msg_text);
   completion_message_view_->SetVisible(false);
-  completion_message_view_->SetPaintToLayer(true);
+  completion_message_view_->SetPaintToLayer();
   completion_message_view_->layer()->SetFillsBoundsOpaquely(false);
   completion_message_view_->layer()->GetAnimator()->AddObserver(this);
   completion_message_view_->set_background(
diff --git a/chrome/browser/installable/installable_manager_browsertest.cc b/chrome/browser/installable/installable_manager_browsertest.cc
index f75d21e..5ca3466 100644
--- a/chrome/browser/installable/installable_manager_browsertest.cc
+++ b/chrome/browser/installable/installable_manager_browsertest.cc
@@ -523,6 +523,26 @@
   }
 }
 
+IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckDataUrlIcon) {
+  // Verify that InstallableManager can handle data URL icons.
+  base::RunLoop run_loop;
+  std::unique_ptr<CallbackTester> tester(
+      new CallbackTester(run_loop.QuitClosure()));
+
+  NavigateAndRunInstallableManager(tester.get(), GetWebAppParams(),
+                                   GetURLOfPageWithServiceWorkerAndManifest(
+                                       "/banners/manifest_data_url_icon.json"));
+  run_loop.Run();
+
+  EXPECT_FALSE(tester->manifest().IsEmpty());
+  EXPECT_FALSE(tester->manifest_url().is_empty());
+  EXPECT_TRUE(tester->is_installable());
+  EXPECT_FALSE(tester->icon_url().is_empty());
+  ASSERT_NE(nullptr, tester->icon());
+  EXPECT_EQ(144, tester->icon()->width());
+  EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
+}
+
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
                        CheckManifestCorruptedIcon) {
   // Verify that the returned InstallableData::icon is null if the web manifest
diff --git a/chrome/browser/permissions/permission_blacklist_client.cc b/chrome/browser/permissions/permission_blacklist_client.cc
index 18f57460..ef0be9b3 100644
--- a/chrome/browser/permissions/permission_blacklist_client.cc
+++ b/chrome/browser/permissions/permission_blacklist_client.cc
@@ -75,11 +75,10 @@
     db_manager_->CancelApiCheck(this);
   timer_.reset(nullptr);
 
-  // TODO(meredithl): Convert the strings returned from Safe Browsing to the
-  // ones used by PermissionUtil for comparison.
   bool permission_blocked =
-      metadata.api_permissions.find(PermissionUtil::GetPermissionString(
-          permission_type_)) != metadata.api_permissions.end();
+      metadata.api_permissions.find(
+          PermissionUtil::ConvertPermissionTypeToSafeBrowsingName(
+              permission_type_)) != metadata.api_permissions.end();
 
   content::BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
diff --git a/chrome/browser/permissions/permission_blacklist_client.h b/chrome/browser/permissions/permission_blacklist_client.h
index 2d1666e0..2396143 100644
--- a/chrome/browser/permissions/permission_blacklist_client.h
+++ b/chrome/browser/permissions/permission_blacklist_client.h
@@ -31,6 +31,9 @@
       public base::RefCountedThreadSafe<PermissionBlacklistClient>,
       public content::WebContentsObserver {
  public:
+  // |callback| will not be called if |web_contents| is destroyed. Thus if the
+  // callback is run, the profile associated with |web_contents| is guaranteed
+  // to be alive.
   static void CheckSafeBrowsingBlacklist(
       scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
       content::PermissionType permission_type,
diff --git a/chrome/browser/permissions/permission_context_base.cc b/chrome/browser/permissions/permission_context_base.cc
index a282bf36..c00f3e7a 100644
--- a/chrome/browser/permissions/permission_context_base.cc
+++ b/chrome/browser/permissions/permission_context_base.cc
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -48,11 +49,6 @@
 // static
 const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] =
     "blocked";
-// Maximum time in milliseconds to wait for safe browsing service to check a
-// url for blacklisting. After this amount of time, the check will be aborted
-// and the url will be treated as not blacklisted.
-// TODO(meredithl): Revisit this once UMA metrics have data about request time.
-const int kCheckUrlTimeoutMs = 2000;
 
 PermissionContextBase::PermissionContextBase(
     Profile* profile,
@@ -61,7 +57,6 @@
     : profile_(profile),
       permission_type_(permission_type),
       content_settings_type_(content_settings_type),
-      safe_browsing_timeout_(kCheckUrlTimeoutMs),
       weak_factory_(this) {
 #if defined(OS_ANDROID)
   permission_queue_controller_.reset(new PermissionQueueController(
@@ -130,20 +125,12 @@
     return;
   }
 
-  if (!db_manager_) {
-    safe_browsing::SafeBrowsingService* sb_service =
-        g_browser_process->safe_browsing_service();
-    if (sb_service)
-      db_manager_ = sb_service->database_manager();
-  }
-
   // Asynchronously check whether the origin should be blocked from making this
   // permission request. It may be on the Safe Browsing API blacklist, or it may
   // have been dismissed too many times in a row. If the origin is allowed to
   // request, that request will be made to ContinueRequestPermission().
-  PermissionDecisionAutoBlocker::UpdateEmbargoedStatus(
-      db_manager_, permission_type_, requesting_origin, web_contents,
-      safe_browsing_timeout_, profile_, base::Time::Now(),
+  PermissionDecisionAutoBlocker::GetForProfile(profile_)->UpdateEmbargoedStatus(
+      permission_type_, requesting_origin, web_contents,
       base::Bind(&PermissionContextBase::ContinueRequestPermission,
                  weak_factory_.GetWeakPtr(), web_contents, id,
                  requesting_origin, embedding_origin, user_gesture, callback));
@@ -165,14 +152,11 @@
         base::StringPrintf(
             "%s permission has been auto-blocked.",
             PermissionUtil::GetPermissionString(permission_type_).c_str()));
-    // Permission has been blacklisted, block the request.
-    // TODO(meredithl): Consider setting the content setting and persisting
-    // the decision to block.
+    // Permission has been automatically blocked.
     callback.Run(CONTENT_SETTING_BLOCK);
     return;
   }
 
-  // Site is not blacklisted by Safe Browsing for the requested permission.
   PermissionUmaUtil::PermissionRequested(permission_type_, requesting_origin,
                                          embedding_origin, profile_);
 
@@ -195,8 +179,8 @@
   ContentSetting content_setting =
       GetPermissionStatusInternal(requesting_origin, embedding_origin);
   if (content_setting == CONTENT_SETTING_ASK &&
-      PermissionDecisionAutoBlocker::IsUnderEmbargo(
-          permission_type_, profile_, requesting_origin, base::Time::Now())) {
+      PermissionDecisionAutoBlocker::GetForProfile(profile_)->IsUnderEmbargo(
+          permission_type_, requesting_origin)) {
     return CONTENT_SETTING_BLOCK;
   }
   return content_setting;
@@ -327,8 +311,8 @@
   }
 
   if (content_setting == CONTENT_SETTING_DEFAULT &&
-      PermissionDecisionAutoBlocker::RecordDismissAndEmbargo(
-          requesting_origin, permission_type_, profile_, base::Time::Now())) {
+      PermissionDecisionAutoBlocker::GetForProfile(profile_)
+          ->RecordDismissAndEmbargo(requesting_origin, permission_type_)) {
     // The permission has been embargoed, so it is blocked for this permission
     // request, but not persisted.
     content_setting = CONTENT_SETTING_BLOCK;
@@ -390,10 +374,3 @@
                                       content_settings_type_, std::string(),
                                       content_setting);
 }
-
-void PermissionContextBase::SetSafeBrowsingDatabaseManagerAndTimeoutForTest(
-    scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
-    int timeout) {
-  db_manager_ = db_manager;
-  safe_browsing_timeout_ = timeout;
-}
diff --git a/chrome/browser/permissions/permission_context_base.h b/chrome/browser/permissions/permission_context_base.h
index 15a32a03..5b94ccc 100644
--- a/chrome/browser/permissions/permission_context_base.h
+++ b/chrome/browser/permissions/permission_context_base.h
@@ -28,10 +28,6 @@
 class WebContents;
 }
 
-namespace safe_browsing {
-class SafeBrowsingDatabaseManager;
-}
-
 using BrowserPermissionCallback = base::Callback<void(ContentSetting)>;
 
 // This base class contains common operations for granting permissions.
@@ -181,15 +177,9 @@
                                  const BrowserPermissionCallback& callback,
                                  bool permission_blocked);
 
-  void SetSafeBrowsingDatabaseManagerAndTimeoutForTest(
-      scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
-      int timeout);
-
   Profile* profile_;
   const content::PermissionType permission_type_;
   const ContentSettingsType content_settings_type_;
-  int safe_browsing_timeout_;
-  scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager_;
 #if defined(OS_ANDROID)
   std::unique_ptr<PermissionQueueController> permission_queue_controller_;
 #endif
diff --git a/chrome/browser/permissions/permission_context_base_unittest.cc b/chrome/browser/permissions/permission_context_base_unittest.cc
index e73f6c25..7e09f909 100644
--- a/chrome/browser/permissions/permission_context_base_unittest.cc
+++ b/chrome/browser/permissions/permission_context_base_unittest.cc
@@ -167,8 +167,9 @@
     }
   }
 
-  // Permission request will need to be responded to, so pass a callback to be
-  // run once the request has completed and the decision has been made.
+  // Set the callback to run if the permission is being responded to in the
+  // test. This is left empty where no response is needed, such as in parallel
+  // requests, permissions blacklisting, invalid origin, and killswitch.
   void SetRespondPermissionCallback(base::Closure callback) {
     respond_permission_ = callback;
   }
@@ -359,6 +360,19 @@
 
     TestPermissionContext permission_context(profile(), permission_type,
                                              content_settings_type);
+    const PermissionRequestID id(
+        web_contents()->GetRenderProcessHost()->GetID(),
+        web_contents()->GetMainFrame()->GetRoutingID(), -1);
+
+    permission_context.SetRespondPermissionCallback(
+        base::Bind(&PermissionContextBaseTests::RespondToPermission,
+                   base::Unretained(this), &permission_context, id, url, false,
+                   CONTENT_SETTING_ASK));
+
+    permission_context.RequestPermission(
+        web_contents(), id, url, true /* user_gesture */,
+        base::Bind(&TestPermissionContext::TrackPermissionDecision,
+                   base::Unretained(&permission_context)));
 
     EXPECT_EQ(CONTENT_SETTING_BLOCK,
               permission_context.GetPermissionStatus(url, url));
@@ -488,7 +502,6 @@
     TestPermissionContext permission_context(
         profile(), content::PermissionType::MIDI_SYSEX,
         CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
-
     EXPECT_EQ(CONTENT_SETTING_BLOCK,
               permission_context.GetPermissionStatus(url, url));
     variations::testing::ClearAllVariationParams();
@@ -629,29 +642,40 @@
       scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
       const GURL& url,
       int timeout,
-      ContentSetting response) {
+      ContentSetting expected_permission_status) {
     NavigateAndCommit(url);
     base::test::ScopedFeatureList scoped_feature_list;
     scoped_feature_list.InitAndEnableFeature(features::kPermissionsBlacklist);
     TestPermissionContext permission_context(profile(), permission_type,
                                              content_settings_type);
-    permission_context.SetSafeBrowsingDatabaseManagerAndTimeoutForTest(
-        db_manager, timeout);
+    PermissionDecisionAutoBlocker::GetForProfile(profile())
+        ->SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
+                                                             timeout);
     const PermissionRequestID id(
         web_contents()->GetRenderProcessHost()->GetID(),
         web_contents()->GetMainFrame()->GetRoutingID(), -1);
-    // The response callback needs to be set here to test a response being made
-    // in the case of a site not being blacklisted or a safe browsing timeout.
-    permission_context.SetRespondPermissionCallback(base::Bind(
-        &PermissionContextBaseTests::RespondToPermission,
-        base::Unretained(this), &permission_context, id, url, false, response));
+
+    // A response only needs to be made to the permission request if we do not
+    // expect he permission to be blacklisted, therefore set the response
+    // callback.
+    if (expected_permission_status == CONTENT_SETTING_ALLOW) {
+      permission_context.SetRespondPermissionCallback(
+          base::Bind(&PermissionContextBaseTests::RespondToPermission,
+                     base::Unretained(this), &permission_context, id, url,
+                     true /* persist */, expected_permission_status));
+    }
+
     permission_context.RequestPermission(
         web_contents(), id, url, true /* user_gesture */,
         base::Bind(&TestPermissionContext::TrackPermissionDecision,
                    base::Unretained(&permission_context)));
+    EXPECT_EQ(expected_permission_status,
+              permission_context.GetPermissionStatus(url, url));
 
-    ASSERT_EQ(1u, permission_context.decisions().size());
-    EXPECT_EQ(response, permission_context.decisions()[0]);
+    if (expected_permission_status == CONTENT_SETTING_ALLOW) {
+      ASSERT_EQ(1u, permission_context.decisions().size());
+      EXPECT_EQ(expected_permission_status, permission_context.decisions()[0]);
+    }
   }
 
  private:
@@ -815,45 +839,22 @@
   scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager =
       new MockSafeBrowsingDatabaseManager(true /* perform_callback */);
   const GURL url("https://www.example.com");
-  std::set<std::string> blacklisted_permissions{
-      PermissionUtil::GetPermissionString(
-          content::PermissionType::GEOLOCATION)};
+  std::set<std::string> blacklisted_permissions{"GEOLOCATION"};
   db_manager->BlacklistUrlPermissions(url, blacklisted_permissions);
   TestPermissionsBlacklisting(content::PermissionType::GEOLOCATION,
                               CONTENT_SETTINGS_TYPE_GEOLOCATION, db_manager,
                               url, 2000 /* timeout */, CONTENT_SETTING_BLOCK);
 }
 
-// Tests that a URL with a blacklisted permission is permitted to request a
-// non-blacklisted permission.
+// Tests that a URL that is blacklisted for one permission can still request
+// another and grant another.
 TEST_F(PermissionContextBaseTests, TestPermissionsBlacklistingAllowed) {
   scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager =
       new MockSafeBrowsingDatabaseManager(true /* perform_callback */);
   const GURL url("https://www.example.com");
-  std::set<std::string> blacklisted_permissions{
-      PermissionUtil::GetPermissionString(
-          content::PermissionType::GEOLOCATION)};
+  std::set<std::string> blacklisted_permissions{"GEOLOCATION"};
   db_manager->BlacklistUrlPermissions(url, blacklisted_permissions);
-  TestPermissionsBlacklisting(
-      content::PermissionType::GEOLOCATION, CONTENT_SETTINGS_TYPE_GEOLOCATION,
-      db_manager, url, 2000 /* timeout in ms */, CONTENT_SETTING_BLOCK);
   TestPermissionsBlacklisting(content::PermissionType::NOTIFICATIONS,
                               CONTENT_SETTINGS_TYPE_NOTIFICATIONS, db_manager,
-                              url, 2000 /* timeout in ms */,
-                              CONTENT_SETTING_ALLOW);
-}
-
-// Tests that a URL with a blacklisted permisison is permitted to request that
-// permission if Safe Browsing has timed out.
-TEST_F(PermissionContextBaseTests, TestSafeBrowsingTimeout) {
-  scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager =
-      new MockSafeBrowsingDatabaseManager(false /* perform_callback */);
-  const GURL url("https://www.example.com");
-  std::set<std::string> blacklisted_permissions{
-      PermissionUtil::GetPermissionString(
-          content::PermissionType::GEOLOCATION)};
-  db_manager->BlacklistUrlPermissions(url, blacklisted_permissions);
-  TestPermissionsBlacklisting(content::PermissionType::GEOLOCATION,
-                              CONTENT_SETTINGS_TYPE_GEOLOCATION, db_manager,
-                              url, 0 /* timeout in ms */, CONTENT_SETTING_ASK);
+                              url, 2000 /* timeout */, CONTENT_SETTING_ALLOW);
 }
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker.cc b/chrome/browser/permissions/permission_decision_auto_blocker.cc
index b38ee5e..9783477 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker.cc
+++ b/chrome/browser/permissions/permission_decision_auto_blocker.cc
@@ -10,13 +10,18 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/permissions/permission_blacklist_client.h"
 #include "chrome/browser/permissions/permission_util.h"
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/chrome_features.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/safe_browsing_db/database_manager.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/permission_type.h"
 #include "content/public/browser/web_contents.h"
@@ -36,6 +41,14 @@
 // permission due to repeated dismissals.
 int g_dismissal_embargo_days = 7;
 
+// Maximum time in milliseconds to wait for safe browsing service to check a
+// url for blacklisting. After this amount of time, the check will be aborted
+// and the url will be treated as not safe.
+// TODO(meredithl): Revisit this once UMA metrics have data about request time.
+const int kCheckUrlTimeoutMs = 2000;
+
+// TODO(meredithl): Migrate to a new and more fitting type, once metrics have
+// been gathered, and deprecate CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT.
 std::unique_ptr<base::DictionaryValue> GetOriginDict(
     HostContentSettingsMap* settings,
     const GURL& origin_url) {
@@ -102,6 +115,42 @@
 
 }  // namespace
 
+// PermissionDecisionAutoBlocker::Factory --------------------------------------
+
+// static
+PermissionDecisionAutoBlocker*
+PermissionDecisionAutoBlocker::Factory::GetForProfile(Profile* profile) {
+  return static_cast<PermissionDecisionAutoBlocker*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+// static
+PermissionDecisionAutoBlocker::Factory*
+PermissionDecisionAutoBlocker::Factory::GetInstance() {
+  return base::Singleton<PermissionDecisionAutoBlocker::Factory>::get();
+}
+
+PermissionDecisionAutoBlocker::Factory::Factory()
+    : BrowserContextKeyedServiceFactory(
+          "PermissionDecisionAutoBlocker",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+PermissionDecisionAutoBlocker::Factory::~Factory() {}
+
+KeyedService* PermissionDecisionAutoBlocker::Factory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = static_cast<Profile*>(context);
+  return new PermissionDecisionAutoBlocker(profile);
+}
+
+content::BrowserContext*
+PermissionDecisionAutoBlocker::Factory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
+}
+
+// PermissionDecisionAutoBlocker -----------------------------------------------
+
 // static
 const char PermissionDecisionAutoBlocker::kPromptDismissCountKey[] =
     "dismiss_count";
@@ -119,11 +168,28 @@
     "dismissal_embargo_days";
 
 // static
+PermissionDecisionAutoBlocker* PermissionDecisionAutoBlocker::GetForProfile(
+    Profile* profile) {
+  return PermissionDecisionAutoBlocker::Factory::GetForProfile(profile);
+}
+
+PermissionDecisionAutoBlocker::PermissionDecisionAutoBlocker(Profile* profile)
+    : profile_(profile),
+      db_manager_(nullptr),
+      safe_browsing_timeout_(kCheckUrlTimeoutMs),
+      clock_(new base::DefaultClock()) {
+  safe_browsing::SafeBrowsingService* sb_service =
+      g_browser_process->safe_browsing_service();
+  if (sb_service)
+    db_manager_ = sb_service->database_manager();
+}
+
+PermissionDecisionAutoBlocker::~PermissionDecisionAutoBlocker() {}
+
 void PermissionDecisionAutoBlocker::RemoveCountsByUrl(
-    Profile* profile,
     base::Callback<bool(const GURL& url)> filter) {
   HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(profile);
+      HostContentSettingsMapFactory::GetForProfile(profile_);
 
   std::unique_ptr<ContentSettingsForOneType> settings(
       new ContentSettingsForOneType);
@@ -141,62 +207,37 @@
   }
 }
 
-// static
 int PermissionDecisionAutoBlocker::GetDismissCount(
     const GURL& url,
-    content::PermissionType permission,
-    Profile* profile) {
-  return GetActionCount(url, permission, kPromptDismissCountKey, profile);
+    content::PermissionType permission) {
+  return GetActionCount(url, permission, kPromptDismissCountKey, profile_);
 }
 
-// static
 int PermissionDecisionAutoBlocker::GetIgnoreCount(
     const GURL& url,
-    content::PermissionType permission,
-    Profile* profile) {
-  return GetActionCount(url, permission, kPromptIgnoreCountKey, profile);
+    content::PermissionType permission) {
+  return GetActionCount(url, permission, kPromptIgnoreCountKey, profile_);
 }
 
-// static
 bool PermissionDecisionAutoBlocker::RecordDismissAndEmbargo(
     const GURL& url,
-    content::PermissionType permission,
-    Profile* profile,
-    base::Time current_time) {
+    content::PermissionType permission) {
   int current_dismissal_count = RecordActionInWebsiteSettings(
-      url, permission, kPromptDismissCountKey, profile);
+      url, permission, kPromptDismissCountKey, profile_);
+
   if (base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften) &&
       current_dismissal_count >= g_prompt_dismissals_before_block) {
-    HostContentSettingsMap* map =
-        HostContentSettingsMapFactory::GetForProfile(profile);
-    PlaceUnderEmbargo(permission, url, map, current_time,
-                      kPermissionDismissalEmbargoKey);
+    PlaceUnderEmbargo(permission, url, kPermissionDismissalEmbargoKey);
     return true;
   }
   return false;
 }
 
-// static
 int PermissionDecisionAutoBlocker::RecordIgnore(
     const GURL& url,
-    content::PermissionType permission,
-    Profile* profile) {
+    content::PermissionType permission) {
   return RecordActionInWebsiteSettings(url, permission, kPromptIgnoreCountKey,
-                                       profile);
-}
-
-// static
-bool PermissionDecisionAutoBlocker::ShouldChangeDismissalToBlock(
-    const GURL& url,
-    content::PermissionType permission,
-    Profile* profile) {
-  int current_dismissal_count =
-      RecordDismissAndEmbargo(url, permission, profile, base::Time::Now());
-
-  if (!base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften))
-    return false;
-
-  return current_dismissal_count >= g_prompt_dismissals_before_block;
+                                       profile_);
 }
 
 // static
@@ -228,30 +269,27 @@
   }
 }
 
-// static
-// TODO(meredithl): Have PermissionDecisionAutoBlocker handle the database
-// manager, rather than passing it in.
 void PermissionDecisionAutoBlocker::UpdateEmbargoedStatus(
-    scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
     content::PermissionType permission,
     const GURL& request_origin,
     content::WebContents* web_contents,
-    int timeout,
-    Profile* profile,
-    base::Time current_time,
     base::Callback<void(bool)> callback) {
   // Check if origin is currently under embargo for the requested permission.
-  if (IsUnderEmbargo(permission, profile, request_origin, current_time)) {
+  if (IsUnderEmbargo(permission, request_origin)) {
     callback.Run(true /* permission_blocked */);
     return;
   }
 
   if (base::FeatureList::IsEnabled(features::kPermissionsBlacklist) &&
-      db_manager) {
+      db_manager_) {
+    // The CheckSafeBrowsingResult callback won't be called if the profile is
+    // destroyed before a result is received. In that case this object will have
+    // been destroyed by that point.
     PermissionBlacklistClient::CheckSafeBrowsingBlacklist(
-        db_manager, permission, request_origin, web_contents, timeout,
+        db_manager_, permission, request_origin, web_contents,
+        safe_browsing_timeout_,
         base::Bind(&PermissionDecisionAutoBlocker::CheckSafeBrowsingResult,
-                   permission, profile, request_origin, current_time,
+                   base::Unretained(this), permission, request_origin,
                    callback));
     return;
   }
@@ -259,14 +297,11 @@
   callback.Run(false /* permission blocked */);
 }
 
-// static
 bool PermissionDecisionAutoBlocker::IsUnderEmbargo(
     content::PermissionType permission,
-    Profile* profile,
-    const GURL& request_origin,
-    base::Time current_time) {
+    const GURL& request_origin) {
   HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(profile);
+      HostContentSettingsMapFactory::GetForProfile(profile_);
   std::unique_ptr<base::DictionaryValue> dict =
       GetOriginDict(map, request_origin);
   base::DictionaryValue* permission_dict = GetOrCreatePermissionDict(
@@ -274,6 +309,7 @@
   double embargo_date = -1;
   bool is_under_dismiss_embargo = false;
   bool is_under_blacklist_embargo = false;
+  base::Time current_time = clock_->Now();
   if (base::FeatureList::IsEnabled(features::kPermissionsBlacklist) &&
       permission_dict->GetDouble(kPermissionBlacklistEmbargoKey,
                                  &embargo_date)) {
@@ -293,40 +329,52 @@
       is_under_dismiss_embargo = true;
     }
   }
-  // If either embargoes is still in effect, return true.
-  return is_under_dismiss_embargo || is_under_blacklist_embargo;
-}
 
-void PermissionDecisionAutoBlocker::PlaceUnderEmbargo(
-    content::PermissionType permission,
-    const GURL& request_origin,
-    HostContentSettingsMap* map,
-    base::Time current_time,
-    const char* key) {
-  std::unique_ptr<base::DictionaryValue> dict =
-      GetOriginDict(map, request_origin);
-  base::DictionaryValue* permission_dict = GetOrCreatePermissionDict(
-      dict.get(), PermissionUtil::GetPermissionString(permission));
-  permission_dict->SetDouble(key, current_time.ToInternalValue());
-  map->SetWebsiteSettingDefaultScope(
-      request_origin, GURL(), CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT,
-      std::string(), std::move(dict));
+  // If either embargo is still in effect, return true.
+  return is_under_dismiss_embargo || is_under_blacklist_embargo;
 }
 
 // static
 void PermissionDecisionAutoBlocker::CheckSafeBrowsingResult(
     content::PermissionType permission,
-    Profile* profile,
     const GURL& request_origin,
-    base::Time current_time,
     base::Callback<void(bool)> callback,
     bool should_be_embargoed) {
   if (should_be_embargoed) {
     // Requesting site is blacklisted for this permission, update the content
     // setting to place it under embargo.
     PlaceUnderEmbargo(permission, request_origin,
-                      HostContentSettingsMapFactory::GetForProfile(profile),
-                      current_time, kPermissionBlacklistEmbargoKey);
+                      kPermissionBlacklistEmbargoKey);
   }
   callback.Run(should_be_embargoed /* permission blocked */);
 }
+
+// static
+void PermissionDecisionAutoBlocker::PlaceUnderEmbargo(
+    content::PermissionType permission,
+    const GURL& request_origin,
+    const char* key) {
+  HostContentSettingsMap* map =
+      HostContentSettingsMapFactory::GetForProfile(profile_);
+  std::unique_ptr<base::DictionaryValue> dict =
+      GetOriginDict(map, request_origin);
+  base::DictionaryValue* permission_dict = GetOrCreatePermissionDict(
+      dict.get(), PermissionUtil::GetPermissionString(permission));
+  permission_dict->SetDouble(key, clock_->Now().ToInternalValue());
+  map->SetWebsiteSettingDefaultScope(
+      request_origin, GURL(), CONTENT_SETTINGS_TYPE_PROMPT_NO_DECISION_COUNT,
+      std::string(), std::move(dict));
+}
+
+void PermissionDecisionAutoBlocker::
+    SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(
+        scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
+        int timeout) {
+  db_manager_ = db_manager;
+  safe_browsing_timeout_ = timeout;
+}
+
+void PermissionDecisionAutoBlocker::SetClockForTesting(
+    std::unique_ptr<base::Clock> clock) {
+  clock_ = std::move(clock);
+}
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker.h b/chrome/browser/permissions/permission_decision_auto_blocker.h
index a84a5f9..5d9660d 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker.h
+++ b/chrome/browser/permissions/permission_decision_auto_blocker.h
@@ -8,6 +8,10 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/time/default_clock.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/permission_type.h"
 #include "url/gurl.h"
 
@@ -22,12 +26,6 @@
 class SafeBrowsingDatabaseManager;
 }
 
-namespace base {
-class Time;
-}
-
-class HostContentSettingsMap;
-
 // The PermissionDecisionAutoBlocker decides whether or not a given origin
 // should be automatically blocked from requesting a permission. When an origin
 // is blocked, it is placed under an "embargo". Until the embargo expires, any
@@ -36,86 +34,91 @@
 // result in it being placed under embargo again. Currently, an origin can be
 // placed under embargo if it appears on Safe Browsing's API blacklist, or if it
 // has a number of prior dismissals greater than a threshold.
-class PermissionDecisionAutoBlocker {
+class PermissionDecisionAutoBlocker : public KeyedService {
  public:
-  // Removes any recorded counts for urls which match |filter| under |profile|.
-  static void RemoveCountsByUrl(Profile* profile,
-                                base::Callback<bool(const GURL& url)> filter);
+  class Factory : public BrowserContextKeyedServiceFactory {
+   public:
+    static PermissionDecisionAutoBlocker* GetForProfile(Profile* profile);
+    static PermissionDecisionAutoBlocker::Factory* GetInstance();
+
+   private:
+    friend struct base::DefaultSingletonTraits<Factory>;
+
+    Factory();
+    ~Factory() override;
+
+    // BrowserContextKeyedServiceFactory
+    KeyedService* BuildServiceInstanceFor(
+        content::BrowserContext* context) const override;
+
+    content::BrowserContext* GetBrowserContextToUse(
+        content::BrowserContext* context) const override;
+  };
+
+  static PermissionDecisionAutoBlocker* GetForProfile(Profile* profile);
+
+  // Removes any recorded counts for urls which match |filter|.
+  void RemoveCountsByUrl(base::Callback<bool(const GURL& url)> filter);
 
   // Returns the current number of dismisses recorded for |permission| type at
   // |url|.
-  static int GetDismissCount(const GURL& url,
-                             content::PermissionType permission,
-                             Profile* profile);
+  int GetDismissCount(const GURL& url, content::PermissionType permission);
 
   // Returns the current number of ignores recorded for |permission|
   // type at |url|.
-  static int GetIgnoreCount(const GURL& url,
-                            content::PermissionType permission,
-                            Profile* profile);
+  int GetIgnoreCount(const GURL& url, content::PermissionType permission);
 
   // Records that a dismissal of a prompt for |permission| was made. If the
   // total number of dismissals exceeds a threshhold and
   // features::kBlockPromptsIfDismissedOften is enabled it will place |url|
   // under embargo for |permission|.
-  static bool RecordDismissAndEmbargo(const GURL& url,
-                                      content::PermissionType permission,
-                                      Profile* profile,
-                                      base::Time current_time);
+  bool RecordDismissAndEmbargo(const GURL& url,
+                               content::PermissionType permission);
 
   // Records that an ignore of a prompt for |permission| was made.
-  static int RecordIgnore(const GURL& url,
-                          content::PermissionType permission,
-                          Profile* profile);
-
-  // Records that a dismissal of a prompt for |permission| was made, and returns
-  // true if this dismissal should be considered a block. False otherwise.
-  // TODO(meredithl): Remove in favour of embargoing on repeated dismissals.
-  static bool ShouldChangeDismissalToBlock(const GURL& url,
-                                           content::PermissionType permission,
-                                           Profile* profile);
+  int RecordIgnore(const GURL& url, content::PermissionType permission);
 
   // Updates the threshold to start blocking prompts from the field trial.
   static void UpdateFromVariations();
 
   // Checks if |request_origin| is under embargo for |permission|. Internally,
   // this will make a call to IsUnderEmbargo to check the content setting first,
-  // but may also make a call to Safe Browsing to check if |request_origin| is
-  // blacklisted for |permission|, which is performed asynchronously.
-  static void UpdateEmbargoedStatus(
-      scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
-      content::PermissionType permission,
-      const GURL& request_origin,
-      content::WebContents* web_contents,
-      int timeout,
-      Profile* profile,
-      base::Time current_time,
-      base::Callback<void(bool)> callback);
+  // but may also make a call to Safe Browsing to check the API blacklist, which
+  // is performed asynchronously.
+  void UpdateEmbargoedStatus(content::PermissionType permission,
+                             const GURL& request_origin,
+                             content::WebContents* web_contents,
+                             base::Callback<void(bool)> callback);
 
   // Checks the status of the content setting to determine if |request_origin|
   // is under embargo for |permission|. This checks both embargo for Permissions
   // Blacklisting and repeated dismissals.
-  static bool IsUnderEmbargo(content::PermissionType permission,
-                             Profile* profile,
-                             const GURL& request_origin,
-                             base::Time current_time);
+  bool IsUnderEmbargo(content::PermissionType permission,
+                      const GURL& request_origin);
 
  private:
   friend class PermissionContextBaseTests;
   friend class PermissionDecisionAutoBlockerUnitTest;
 
-  static void CheckSafeBrowsingResult(content::PermissionType permission,
-                                      Profile* profile,
-                                      const GURL& request_origin,
-                                      base::Time current_time,
-                                      base::Callback<void(bool)> callback,
-                                      bool should_be_embargoed);
+  explicit PermissionDecisionAutoBlocker(Profile* profile);
+  ~PermissionDecisionAutoBlocker() override;
 
-  static void PlaceUnderEmbargo(content::PermissionType permission,
-                                const GURL& request_origin,
-                                HostContentSettingsMap* map,
-                                base::Time current_time,
-                                const char* key);
+  // Get the result of the Safe Browsing check, if |should_be_embargoed| is true
+  // then |request_origin| will be placed under embargo for that |permission|.
+  void CheckSafeBrowsingResult(content::PermissionType permission,
+                               const GURL& request_origin,
+                               base::Callback<void(bool)> callback,
+                               bool should_be_embargoed);
+
+  void PlaceUnderEmbargo(content::PermissionType permission,
+                         const GURL& request_origin,
+                         const char* key);
+
+  void SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(
+      scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager,
+      int timeout);
+
+  void SetClockForTesting(std::unique_ptr<base::Clock> clock);
 
   // Keys used for storing count data in a website setting.
   static const char kPromptDismissCountKey[];
@@ -123,7 +126,14 @@
   static const char kPermissionDismissalEmbargoKey[];
   static const char kPermissionBlacklistEmbargoKey[];
 
+  Profile* profile_;
+  scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> db_manager_;
+
+  // Timeout in ms.
+  int safe_browsing_timeout_;
+
+  std::unique_ptr<base::Clock> clock_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(PermissionDecisionAutoBlocker);
 };
-
 #endif  // CHROME_BROWSER_PERMISSIONS_PERMISSION_DECISION_AUTO_BLOCKER_H_
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc b/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
index f32bfc7..dfc0242 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
+++ b/chrome/browser/permissions/permission_decision_auto_blocker_unittest.cc
@@ -4,10 +4,14 @@
 
 #include "chrome/browser/permissions/permission_decision_auto_blocker.h"
 
+#include <map>
+
 #include "base/bind.h"
+#include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
-#include "base/time/time.h"
+#include "base/test/simple_test_clock.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/permissions/permission_util.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
@@ -24,183 +28,271 @@
   return true;
 }
 
-void AutoBlockerCallback(bool expected, bool result) {
-  EXPECT_EQ(expected, result);
-}
-
 }  // namespace
 
-// TODO(meredithl): Write unit tests to simulate entering Permissions
-// Blacklisting embargo status via the public API.
+class MockSafeBrowsingDatabaseManager
+    : public safe_browsing::TestSafeBrowsingDatabaseManager {
+ public:
+  explicit MockSafeBrowsingDatabaseManager(bool perform_callback)
+      : perform_callback_(perform_callback) {}
+
+  bool CheckApiBlacklistUrl(
+      const GURL& url,
+      safe_browsing::SafeBrowsingDatabaseManager::Client* client) override {
+    if (perform_callback_) {
+      safe_browsing::ThreatMetadata metadata;
+      const auto& blacklisted_permissions = permissions_blacklist_.find(url);
+      if (blacklisted_permissions != permissions_blacklist_.end())
+        metadata.api_permissions = blacklisted_permissions->second;
+      client->OnCheckApiBlacklistUrlResult(url, metadata);
+    }
+    return false;
+  }
+
+  bool CancelApiCheck(Client* client) override {
+    DCHECK(!perform_callback_);
+    // Returns true when client check could be stopped.
+    return true;
+  }
+
+  void BlacklistUrlPermissions(const GURL& url,
+                               const std::set<std::string> permissions) {
+    permissions_blacklist_[url] = permissions;
+  }
+
+  void SetPerformCallback(bool perform_callback) {
+    perform_callback_ = perform_callback;
+  }
+
+ protected:
+  ~MockSafeBrowsingDatabaseManager() override {}
+
+ private:
+  bool perform_callback_;
+  std::map<GURL, std::set<std::string>> permissions_blacklist_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
+};
+
 class PermissionDecisionAutoBlockerUnitTest
     : public ChromeRenderViewHostTestHarness {
  protected:
-  int GetDismissalCount(const GURL& url, content::PermissionType permission) {
-    return PermissionDecisionAutoBlocker::GetDismissCount(url, permission,
-                                                          profile());
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    autoblocker_ = PermissionDecisionAutoBlocker::GetForProfile(profile());
+    feature_list_.InitWithFeatures({features::kBlockPromptsIfDismissedOften,
+                                    features::kPermissionsBlacklist},
+                                   {});
+    last_embargoed_status_ = false;
+    std::unique_ptr<base::SimpleTestClock> clock =
+        base::MakeUnique<base::SimpleTestClock>();
+    clock_ = clock.get();
+    autoblocker_->SetClockForTesting(std::move(clock));
   }
 
-  int GetIgnoreCount(const GURL& url, content::PermissionType permission) {
-    return PermissionDecisionAutoBlocker::GetIgnoreCount(url, permission,
-                                                         profile());
-  }
-
-  int RecordDismissAndEmbargo(const GURL& url,
-                              content::PermissionType permission,
-                              base::Time current_time) {
-    return PermissionDecisionAutoBlocker::RecordDismissAndEmbargo(
-        url, permission, profile(), current_time);
-  }
-
-  int RecordIgnore(const GURL& url, content::PermissionType permission) {
-    return PermissionDecisionAutoBlocker::RecordIgnore(url, permission,
-                                                       profile());
+  void SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(
+      scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager,
+      int timeout) {
+    autoblocker_->SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
+                                                                     timeout);
   }
 
   void UpdateEmbargoedStatus(content::PermissionType permission,
-                             const GURL& url,
-                             base::Time current_time,
-                             bool expected_result) {
-    PermissionDecisionAutoBlocker::UpdateEmbargoedStatus(
-        nullptr /* db manager */, permission, url, nullptr /* web contents */,
-        2000 /* timeout in ms */, profile(), current_time,
-        base::Bind(&AutoBlockerCallback, expected_result));
+                             const GURL& url) {
+    base::RunLoop run_loop;
+    autoblocker_->UpdateEmbargoedStatus(
+        permission, url, nullptr,
+        base::Bind(&PermissionDecisionAutoBlockerUnitTest::SetLastEmbargoStatus,
+                   base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
   }
 
-  // Manually placing an origin, permission pair under embargo for blacklisting.
-  // To embargo on dismissals, RecordDismissAndEmbargo can be used.
+  // Manually placing an (origin, permission) pair under embargo for
+  // blacklisting. To embargo on dismissals, RecordDismissAndEmbargo can be
+  // used.
   void PlaceUnderBlacklistEmbargo(content::PermissionType permission,
-                                  const GURL& url,
-                                  HostContentSettingsMap* map,
-                                  base::Time current_time) {
-    PermissionDecisionAutoBlocker::PlaceUnderEmbargo(
-        permission, url, map, current_time,
+                                  const GURL& url) {
+    autoblocker_->PlaceUnderEmbargo(
+        permission, url,
         PermissionDecisionAutoBlocker::kPermissionBlacklistEmbargoKey);
   }
+
+  PermissionDecisionAutoBlocker* autoblocker() { return autoblocker_; }
+
+  void SetLastEmbargoStatus(base::Closure quit_closure, bool status) {
+    last_embargoed_status_ = status;
+    if (quit_closure) {
+      quit_closure.Run();
+      quit_closure.Reset();
+    }
+  }
+
+  bool last_embargoed_status() { return last_embargoed_status_; }
+
+  base::SimpleTestClock* clock() { return clock_; }
+
+ private:
+  PermissionDecisionAutoBlocker* autoblocker_;
+  base::test::ScopedFeatureList feature_list_;
+  base::SimpleTestClock* clock_;
+  bool last_embargoed_status_;
 };
 
 TEST_F(PermissionDecisionAutoBlockerUnitTest, RemoveCountsByUrl) {
   GURL url1("https://www.google.com");
   GURL url2("https://www.example.com");
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(features::kBlockPromptsIfDismissedOften);
 
   // Record some dismissals.
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url1, content::PermissionType::GEOLOCATION, base::Time::Now()));
-  EXPECT_EQ(1, GetDismissalCount(url1, content::PermissionType::GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(1, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::GEOLOCATION));
 
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url1, content::PermissionType::GEOLOCATION, base::Time::Now()));
-  EXPECT_EQ(2, GetDismissalCount(url1, content::PermissionType::GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(2, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::GEOLOCATION));
 
-  EXPECT_TRUE(RecordDismissAndEmbargo(
-      url1, content::PermissionType::GEOLOCATION, base::Time::Now()));
-  EXPECT_EQ(3, GetDismissalCount(url1, content::PermissionType::GEOLOCATION));
+  EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+      url1, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(3, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::GEOLOCATION));
 
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url2, content::PermissionType::GEOLOCATION, base::Time::Now()));
-  EXPECT_EQ(1, GetDismissalCount(url2, content::PermissionType::GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url2, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(1, autoblocker()->GetDismissCount(
+                   url2, content::PermissionType::GEOLOCATION));
 
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url1, content::PermissionType::NOTIFICATIONS, base::Time::Now()));
-  EXPECT_EQ(1, GetDismissalCount(url1, content::PermissionType::NOTIFICATIONS));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, content::PermissionType::NOTIFICATIONS));
+  EXPECT_EQ(1, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::NOTIFICATIONS));
 
   // Record some ignores.
-  EXPECT_EQ(1, RecordIgnore(url1, content::PermissionType::MIDI_SYSEX));
-  EXPECT_EQ(1, RecordIgnore(url1, content::PermissionType::DURABLE_STORAGE));
-  EXPECT_EQ(1, RecordIgnore(url2, content::PermissionType::GEOLOCATION));
-  EXPECT_EQ(2, RecordIgnore(url2, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(1, autoblocker()->RecordIgnore(
+                   url1, content::PermissionType::MIDI_SYSEX));
+  EXPECT_EQ(1, autoblocker()->RecordIgnore(
+                   url1, content::PermissionType::DURABLE_STORAGE));
+  EXPECT_EQ(1, autoblocker()->RecordIgnore(
+                   url2, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(2, autoblocker()->RecordIgnore(
+                   url2, content::PermissionType::GEOLOCATION));
 
-  PermissionDecisionAutoBlocker::RemoveCountsByUrl(profile(),
-                                                   base::Bind(&FilterGoogle));
+  autoblocker()->RemoveCountsByUrl(base::Bind(&FilterGoogle));
 
   // Expect that url1's actions are gone, but url2's remain.
-  EXPECT_EQ(0, GetDismissalCount(url1, content::PermissionType::GEOLOCATION));
-  EXPECT_EQ(0, GetDismissalCount(url1, content::PermissionType::NOTIFICATIONS));
-  EXPECT_EQ(0, GetIgnoreCount(url1, content::PermissionType::MIDI_SYSEX));
-  EXPECT_EQ(0, GetIgnoreCount(url1, content::PermissionType::DURABLE_STORAGE));
+  EXPECT_EQ(0, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(0, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::NOTIFICATIONS));
+  EXPECT_EQ(0, autoblocker()->GetIgnoreCount(
+                   url1, content::PermissionType::MIDI_SYSEX));
+  EXPECT_EQ(0, autoblocker()->GetIgnoreCount(
+                   url1, content::PermissionType::DURABLE_STORAGE));
 
-  EXPECT_EQ(1, GetDismissalCount(url2, content::PermissionType::GEOLOCATION));
-  EXPECT_EQ(2, GetIgnoreCount(url2, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(1, autoblocker()->GetDismissCount(
+                   url2, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(2, autoblocker()->GetIgnoreCount(
+                   url2, content::PermissionType::GEOLOCATION));
 
   // Add some more actions.
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url1, content::PermissionType::GEOLOCATION, base::Time::Now()));
-  EXPECT_EQ(1, GetDismissalCount(url1, content::PermissionType::GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(1, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::GEOLOCATION));
 
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url1, content::PermissionType::NOTIFICATIONS, base::Time::Now()));
-  EXPECT_EQ(1, GetDismissalCount(url1, content::PermissionType::NOTIFICATIONS));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url1, content::PermissionType::NOTIFICATIONS));
+  EXPECT_EQ(1, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::NOTIFICATIONS));
 
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url2, content::PermissionType::GEOLOCATION, base::Time::Now()));
-  EXPECT_EQ(2, GetDismissalCount(url2, content::PermissionType::GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url2, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(2, autoblocker()->GetDismissCount(
+                   url2, content::PermissionType::GEOLOCATION));
 
-  EXPECT_EQ(1, RecordIgnore(url1, content::PermissionType::GEOLOCATION));
-  EXPECT_EQ(1, RecordIgnore(url1, content::PermissionType::NOTIFICATIONS));
-  EXPECT_EQ(1, RecordIgnore(url1, content::PermissionType::DURABLE_STORAGE));
-  EXPECT_EQ(1, RecordIgnore(url2, content::PermissionType::MIDI_SYSEX));
+  EXPECT_EQ(1, autoblocker()->RecordIgnore(
+                   url1, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(1, autoblocker()->RecordIgnore(
+                   url1, content::PermissionType::NOTIFICATIONS));
+  EXPECT_EQ(1, autoblocker()->RecordIgnore(
+                   url1, content::PermissionType::DURABLE_STORAGE));
+  EXPECT_EQ(1, autoblocker()->RecordIgnore(
+                   url2, content::PermissionType::MIDI_SYSEX));
 
   // Remove everything and expect that it's all gone.
-  PermissionDecisionAutoBlocker::RemoveCountsByUrl(profile(),
-                                                   base::Bind(&FilterAll));
+  autoblocker()->RemoveCountsByUrl(base::Bind(&FilterAll));
 
-  EXPECT_EQ(0, GetDismissalCount(url1, content::PermissionType::GEOLOCATION));
-  EXPECT_EQ(0, GetDismissalCount(url1, content::PermissionType::NOTIFICATIONS));
-  EXPECT_EQ(0, GetDismissalCount(url2, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(0, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(0, autoblocker()->GetDismissCount(
+                   url1, content::PermissionType::NOTIFICATIONS));
+  EXPECT_EQ(0, autoblocker()->GetDismissCount(
+                   url2, content::PermissionType::GEOLOCATION));
 
-  EXPECT_EQ(0, GetIgnoreCount(url1, content::PermissionType::GEOLOCATION));
-  EXPECT_EQ(0, GetIgnoreCount(url1, content::PermissionType::NOTIFICATIONS));
-  EXPECT_EQ(0, GetIgnoreCount(url2, content::PermissionType::GEOLOCATION));
-  EXPECT_EQ(0, GetIgnoreCount(url2, content::PermissionType::DURABLE_STORAGE));
-  EXPECT_EQ(0, GetIgnoreCount(url2, content::PermissionType::MIDI_SYSEX));
+  EXPECT_EQ(0, autoblocker()->GetIgnoreCount(
+                   url1, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(0, autoblocker()->GetIgnoreCount(
+                   url1, content::PermissionType::NOTIFICATIONS));
+  EXPECT_EQ(0, autoblocker()->GetIgnoreCount(
+                   url2, content::PermissionType::GEOLOCATION));
+  EXPECT_EQ(0, autoblocker()->GetIgnoreCount(
+                   url2, content::PermissionType::DURABLE_STORAGE));
+  EXPECT_EQ(0, autoblocker()->GetIgnoreCount(
+                   url2, content::PermissionType::MIDI_SYSEX));
+}
+
+// Test that an origin that has been blacklisted for a permission is embargoed.
+TEST_F(PermissionDecisionAutoBlockerUnitTest, TestUpdateEmbargoBlacklist) {
+  GURL url("https://www.google.com");
+
+  scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager =
+      new MockSafeBrowsingDatabaseManager(true /* perform_callback */);
+  std::set<std::string> blacklisted_permissions{"GEOLOCATION"};
+  db_manager->BlacklistUrlPermissions(url, blacklisted_permissions);
+  SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
+                                                     2000 /* timeout in ms */);
+
+  UpdateEmbargoedStatus(content::PermissionType::GEOLOCATION, url);
+  EXPECT_TRUE(last_embargoed_status());
 }
 
 // Check that IsUnderEmbargo returns the correct value when the embargo is set
 // and expires.
 TEST_F(PermissionDecisionAutoBlockerUnitTest, CheckEmbargoStatus) {
   GURL url("https://www.google.com");
-  auto* map = HostContentSettingsMapFactory::GetForProfile(profile());
-  base::Time time_now = base::Time::Now();
-  base::test::ScopedFeatureList feature_list;
+  clock()->SetNow(base::Time::Now());
 
-  feature_list.InitAndEnableFeature(features::kPermissionsBlacklist);
-  EXPECT_TRUE(base::FeatureList::IsEnabled(features::kPermissionsBlacklist));
+  PlaceUnderBlacklistEmbargo(content::PermissionType::GEOLOCATION, url);
+  EXPECT_TRUE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
-  // Manually place url under embargo and confirm embargo status.
-  PlaceUnderBlacklistEmbargo(content::PermissionType::GEOLOCATION, url, map,
-                             time_now);
-  EXPECT_TRUE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url, time_now));
-
-  // Check that the origin is not under embargo for another permission.
-  EXPECT_FALSE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::NOTIFICATIONS, profile(), url, time_now));
+  // Check that the origin is not under embargo for a different permission.
+  EXPECT_FALSE(autoblocker()->IsUnderEmbargo(
+      content::PermissionType::NOTIFICATIONS, url));
 
   // Confirm embargo status during the embargo period.
-  EXPECT_TRUE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url,
-      time_now + base::TimeDelta::FromDays(5)));
+  clock()->Advance(base::TimeDelta::FromDays(5));
+  EXPECT_TRUE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
   // Check embargo is lifted on expiry day. A small offset after the exact
   // embargo expiration date has been added to account for any precision errors
   // when removing the date stored as a double from the permission dictionary.
-  EXPECT_FALSE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url,
-      time_now + base::TimeDelta::FromHours(7 * 24 + 1)));
+  clock()->Advance(base::TimeDelta::FromHours(3 * 24 + 1));
+  EXPECT_FALSE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
   // Check embargo is lifted well after the expiry day.
-  EXPECT_FALSE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url,
-      time_now + base::TimeDelta::FromDays(8)));
+  clock()->Advance(base::TimeDelta::FromDays(1));
+  EXPECT_FALSE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
   // Place under embargo again and verify the embargo status.
-  time_now = base::Time::Now();
-  PlaceUnderBlacklistEmbargo(content::PermissionType::NOTIFICATIONS, url, map,
-                             time_now);
-  EXPECT_TRUE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::NOTIFICATIONS, profile(), url, time_now));
+  PlaceUnderBlacklistEmbargo(content::PermissionType::NOTIFICATIONS, url);
+  clock()->Advance(base::TimeDelta::FromDays(1));
+  EXPECT_TRUE(autoblocker()->IsUnderEmbargo(
+      content::PermissionType::NOTIFICATIONS, url));
 }
 
 // Tests the alternating pattern of the block on multiple dismiss behaviour. On
@@ -208,90 +300,95 @@
 // automatically blocked. Each time the embargo is lifted, the site gets another
 // chance to request the permission, but if it is again dismissed it is placed
 // under embargo again and its permission requests blocked.
-TEST_F(PermissionDecisionAutoBlockerUnitTest, TestDismissEmbargo) {
+TEST_F(PermissionDecisionAutoBlockerUnitTest, TestDismissEmbargoBackoff) {
   GURL url("https://www.google.com");
-  base::Time time_now = base::Time::Now();
-  // Enable the autoblocking feature, which is disabled by default.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(features::kBlockPromptsIfDismissedOften);
-
-  EXPECT_TRUE(
-      base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften));
+  clock()->SetNow(base::Time::Now());
 
   // Record some dismisses.
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url, content::PermissionType::GEOLOCATION, time_now));
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url, content::PermissionType::GEOLOCATION, time_now));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url, content::PermissionType::GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url, content::PermissionType::GEOLOCATION));
 
   // A request with < 3 prior dismisses should not be automatically blocked.
-  EXPECT_FALSE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url, time_now));
+  EXPECT_FALSE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
   // After the 3rd dismiss subsequent permission requests should be autoblocked.
-  EXPECT_TRUE(RecordDismissAndEmbargo(url, content::PermissionType::GEOLOCATION,
-                                      time_now));
-  EXPECT_TRUE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url, time_now));
+  EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+      url, content::PermissionType::GEOLOCATION));
+  EXPECT_TRUE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
   // Accelerate time forward, check that the embargo status is lifted and the
   // request won't be automatically blocked.
-  time_now += base::TimeDelta::FromDays(8);
-  EXPECT_FALSE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url, time_now));
+  clock()->Advance(base::TimeDelta::FromDays(8));
+  EXPECT_FALSE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
   // Record another dismiss, subsequent requests should be autoblocked again.
-  EXPECT_TRUE(RecordDismissAndEmbargo(url, content::PermissionType::GEOLOCATION,
-                                      time_now));
-  EXPECT_TRUE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url, time_now));
+  EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+      url, content::PermissionType::GEOLOCATION));
+  EXPECT_TRUE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
   // Accelerate time again, check embargo is lifted and another permission
   // request is let through.
-  time_now += base::TimeDelta::FromDays(8);
-  EXPECT_FALSE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url, time_now));
+  clock()->Advance(base::TimeDelta::FromDays(8));
+  EXPECT_FALSE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 }
 
 // Test the logic for a combination of blacklisting and dismissal embargo.
 TEST_F(PermissionDecisionAutoBlockerUnitTest, TestExpiredBlacklistEmbargo) {
   GURL url("https://www.google.com");
-  base::Time time_now = base::Time::Now();
-  auto* map = HostContentSettingsMapFactory::GetForProfile(profile());
-
-  // Enable both dismissals and permissions blacklisting features.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeatures({features::kBlockPromptsIfDismissedOften,
-                                 features::kPermissionsBlacklist},
-                                {});
-  EXPECT_TRUE(
-      base::FeatureList::IsEnabled(features::kBlockPromptsIfDismissedOften));
-  EXPECT_TRUE(base::FeatureList::IsEnabled(features::kPermissionsBlacklist));
+  clock()->SetNow(base::Time::Now());
 
   // Place under blacklist embargo and check the status.
-  PlaceUnderBlacklistEmbargo(content::PermissionType::GEOLOCATION, url, map,
-                             base::Time::Now());
-
-  time_now += base::TimeDelta::FromDays(5);
-  EXPECT_TRUE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url, time_now));
+  PlaceUnderBlacklistEmbargo(content::PermissionType::GEOLOCATION, url);
+  clock()->Advance(base::TimeDelta::FromDays(5));
+  EXPECT_TRUE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 
   // Record dismisses to place it under dismissal embargo.
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url, content::PermissionType::GEOLOCATION, time_now));
-  EXPECT_FALSE(RecordDismissAndEmbargo(
-      url, content::PermissionType::GEOLOCATION, time_now));
-  EXPECT_TRUE(RecordDismissAndEmbargo(url, content::PermissionType::GEOLOCATION,
-                                      time_now));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url, content::PermissionType::GEOLOCATION));
+  EXPECT_FALSE(autoblocker()->RecordDismissAndEmbargo(
+      url, content::PermissionType::GEOLOCATION));
+  EXPECT_TRUE(autoblocker()->RecordDismissAndEmbargo(
+      url, content::PermissionType::GEOLOCATION));
 
-  // Accelerate time to a point where the blacklist embargo should be expired.
-  time_now += base::TimeDelta::FromDays(3);
-  EXPECT_FALSE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url,
-      time_now + base::TimeDelta::FromDays(5)));
+  // Accelerate time to a point where the blacklist embargo should be expired
+  // and check that dismissal embargo is still set.
+  clock()->Advance(base::TimeDelta::FromDays(3));
+  EXPECT_TRUE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
+}
 
-  // Check that dismissal embargo is still set, even though the blacklisting
-  // embargo has expired.
-  EXPECT_TRUE(PermissionDecisionAutoBlocker::IsUnderEmbargo(
-      content::PermissionType::GEOLOCATION, profile(), url, time_now));
+TEST_F(PermissionDecisionAutoBlockerUnitTest, TestSafeBrowsingTimeout) {
+  GURL url("https://www.google.com");
+  clock()->SetNow(base::Time::Now());
+
+  scoped_refptr<MockSafeBrowsingDatabaseManager> db_manager =
+      new MockSafeBrowsingDatabaseManager(false /* perform_callback */);
+  std::set<std::string> blacklisted_permissions{"GEOLOCATION"};
+  db_manager->BlacklistUrlPermissions(url, blacklisted_permissions);
+  SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
+                                                     0 /* timeout in ms */);
+
+  UpdateEmbargoedStatus(content::PermissionType::GEOLOCATION, url);
+  EXPECT_FALSE(last_embargoed_status());
+  EXPECT_FALSE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
+  db_manager->SetPerformCallback(true);
+  SetSafeBrowsingDatabaseManagerAndTimeoutForTesting(db_manager,
+                                                     2000 /* timeout in ms */);
+
+  clock()->Advance(base::TimeDelta::FromDays(1));
+  UpdateEmbargoedStatus(content::PermissionType::GEOLOCATION, url);
+  EXPECT_TRUE(last_embargoed_status());
+
+  clock()->Advance(base::TimeDelta::FromDays(1));
+  EXPECT_TRUE(
+      autoblocker()->IsUnderEmbargo(content::PermissionType::GEOLOCATION, url));
 }
diff --git a/chrome/browser/permissions/permission_uma_util.cc b/chrome/browser/permissions/permission_uma_util.cc
index 133b5f9..82792775 100644
--- a/chrome/browser/permissions/permission_uma_util.cc
+++ b/chrome/browser/permissions/permission_uma_util.cc
@@ -271,16 +271,16 @@
     PermissionRequestGestureType gesture_type,
     const GURL& requesting_origin,
     Profile* profile) {
+  PermissionDecisionAutoBlocker* autoblocker =
+      PermissionDecisionAutoBlocker::GetForProfile(profile);
   RecordPermissionAction(permission, GRANTED, PermissionSourceUI::PROMPT,
                          gesture_type, requesting_origin, profile);
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptAcceptedPriorDismissCountPrefix,
-      PermissionDecisionAutoBlocker::GetDismissCount(requesting_origin,
-                                                     permission, profile));
+      autoblocker->GetDismissCount(requesting_origin, permission));
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptAcceptedPriorIgnoreCountPrefix,
-      PermissionDecisionAutoBlocker::GetIgnoreCount(requesting_origin,
-                                                    permission, profile));
+      autoblocker->GetIgnoreCount(requesting_origin, permission));
 }
 
 void PermissionUmaUtil::PermissionDenied(
@@ -288,16 +288,16 @@
     PermissionRequestGestureType gesture_type,
     const GURL& requesting_origin,
     Profile* profile) {
+  PermissionDecisionAutoBlocker* autoblocker =
+      PermissionDecisionAutoBlocker::GetForProfile(profile);
   RecordPermissionAction(permission, DENIED, PermissionSourceUI::PROMPT,
                          gesture_type, requesting_origin, profile);
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptDeniedPriorDismissCountPrefix,
-      PermissionDecisionAutoBlocker::GetDismissCount(requesting_origin,
-                                                     permission, profile));
+      autoblocker->GetDismissCount(requesting_origin, permission));
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptDeniedPriorIgnoreCountPrefix,
-      PermissionDecisionAutoBlocker::GetIgnoreCount(requesting_origin,
-                                                    permission, profile));
+      autoblocker->GetIgnoreCount(requesting_origin, permission));
 }
 
 void PermissionUmaUtil::PermissionDismissed(
@@ -305,16 +305,16 @@
     PermissionRequestGestureType gesture_type,
     const GURL& requesting_origin,
     Profile* profile) {
+  PermissionDecisionAutoBlocker* autoblocker =
+      PermissionDecisionAutoBlocker::GetForProfile(profile);
   RecordPermissionAction(permission, DISMISSED, PermissionSourceUI::PROMPT,
                          gesture_type, requesting_origin, profile);
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptDismissedPriorDismissCountPrefix,
-      PermissionDecisionAutoBlocker::GetDismissCount(requesting_origin,
-                                                     permission, profile));
+      autoblocker->GetDismissCount(requesting_origin, permission));
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptDismissedPriorIgnoreCountPrefix,
-      PermissionDecisionAutoBlocker::GetIgnoreCount(requesting_origin,
-                                                    permission, profile));
+      autoblocker->GetIgnoreCount(requesting_origin, permission));
 }
 
 void PermissionUmaUtil::PermissionIgnored(
@@ -322,22 +322,21 @@
     PermissionRequestGestureType gesture_type,
     const GURL& requesting_origin,
     Profile* profile) {
+  PermissionDecisionAutoBlocker* autoblocker =
+      PermissionDecisionAutoBlocker::GetForProfile(profile);
   RecordPermissionAction(permission, IGNORED, PermissionSourceUI::PROMPT,
                          gesture_type, requesting_origin, profile);
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptIgnoredPriorDismissCountPrefix,
-      PermissionDecisionAutoBlocker::GetDismissCount(requesting_origin,
-                                                     permission, profile));
+      autoblocker->GetDismissCount(requesting_origin, permission));
   RecordPermissionPromptPriorCount(
       permission, kPermissionsPromptIgnoredPriorIgnoreCountPrefix,
-      PermissionDecisionAutoBlocker::GetIgnoreCount(requesting_origin,
-                                                    permission, profile));
+      autoblocker->GetIgnoreCount(requesting_origin, permission));
 
   // RecordPermission* methods need to be called before RecordIgnore in the
   // blocker because they record the number of prior ignore and dismiss values,
   // and we don't want to include the current ignore.
-  PermissionDecisionAutoBlocker::RecordIgnore(requesting_origin, permission,
-                                              profile);
+  autoblocker->RecordIgnore(requesting_origin, permission);
 }
 
 void PermissionUmaUtil::PermissionRevoked(PermissionType permission,
@@ -628,14 +627,15 @@
     const GURL& requesting_origin,
     Profile* profile) {
   if (IsOptedIntoPermissionActionReporting(profile)) {
+    PermissionDecisionAutoBlocker* autoblocker =
+        PermissionDecisionAutoBlocker::GetForProfile(profile);
     // TODO(kcarattini): Pass in the actual persist decision when it becomes
     // available.
-    PermissionReportInfo report_info(requesting_origin, permission, action,
-        source_ui, gesture_type, PermissionPersistDecision::UNSPECIFIED,
-        PermissionDecisionAutoBlocker::GetDismissCount(
-            requesting_origin, permission, profile),
-        PermissionDecisionAutoBlocker::GetIgnoreCount(
-            requesting_origin, permission, profile));
+    PermissionReportInfo report_info(
+        requesting_origin, permission, action, source_ui, gesture_type,
+        PermissionPersistDecision::UNSPECIFIED,
+        autoblocker->GetDismissCount(requesting_origin, permission),
+        autoblocker->GetIgnoreCount(requesting_origin, permission));
     g_browser_process->safe_browsing_service()
         ->ui_manager()->ReportPermissionAction(report_info);
   }
diff --git a/chrome/browser/permissions/permission_util.cc b/chrome/browser/permissions/permission_util.cc
index 0643a0f..2e8d507 100644
--- a/chrome/browser/permissions/permission_util.cc
+++ b/chrome/browser/permissions/permission_util.cc
@@ -55,6 +55,38 @@
   return std::string();
 }
 
+std::string PermissionUtil::ConvertPermissionTypeToSafeBrowsingName(
+    const content::PermissionType& permission_type) {
+  switch (permission_type) {
+    case content::PermissionType::GEOLOCATION:
+      return "GEOLOCATION";
+    case content::PermissionType::NOTIFICATIONS:
+      return "NOTIFICATIONS";
+    case content::PermissionType::MIDI_SYSEX:
+      return "MIDI_SYSEX";
+    case content::PermissionType::PUSH_MESSAGING:
+      return "PUSH_MESSAGING";
+    case content::PermissionType::DURABLE_STORAGE:
+      return "DURABLE_STORAGE";
+    case content::PermissionType::PROTECTED_MEDIA_IDENTIFIER:
+      return "PROTECTED_MEDIA_IDENTIFIER";
+    case content::PermissionType::AUDIO_CAPTURE:
+      return "AUDIO_CAPTURE";
+    case content::PermissionType::VIDEO_CAPTURE:
+      return "VIDEO_CAPTURE";
+    case content::PermissionType::MIDI:
+      return "MIDI";
+    case content::PermissionType::BACKGROUND_SYNC:
+      return "BACKGROUND_SYNC";
+    case content::PermissionType::FLASH:
+      return "FLASH";
+    case content::PermissionType::NUM:
+      break;
+  }
+  NOTREACHED();
+  return std::string();
+}
+
 PermissionRequestType PermissionUtil::GetRequestType(
     content::PermissionType type) {
   switch (type) {
diff --git a/chrome/browser/permissions/permission_util.h b/chrome/browser/permissions/permission_util.h
index 7e482d8..2ad0c7ed 100644
--- a/chrome/browser/permissions/permission_util.h
+++ b/chrome/browser/permissions/permission_util.h
@@ -45,6 +45,11 @@
   // Returns the permission string for the given PermissionType.
   static std::string GetPermissionString(content::PermissionType permission);
 
+  // Return the stringified version of the PermissionType enum that Safe
+  // Browsing uses for the API Blacklist.
+  static std::string ConvertPermissionTypeToSafeBrowsingName(
+      const content::PermissionType& permission_type);
+
   // Returns the request type corresponding to a permission type.
   static PermissionRequestType GetRequestType(content::PermissionType type);
 
diff --git a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html
index 4bf94f3..f748bfe 100644
--- a/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html
+++ b/chrome/browser/resources/bluetooth_internals/bluetooth_internals.html
@@ -32,6 +32,7 @@
   <script src="expandable_list.js"></script>
   <script src="characteristic_list.js"></script>
   <script src="service_list.js"></script>
+  <script src="descriptor_list.js"></script>
   <script src="adapter_page.js"></script>
   <script src="device_collection.js"></script>
   <script src="device_details_page.js"></script>
diff --git a/chrome/browser/resources/bluetooth_internals/characteristic_list.js b/chrome/browser/resources/bluetooth_internals/characteristic_list.js
index 7faecde..937b3d7 100644
--- a/chrome/browser/resources/bluetooth_internals/characteristic_list.js
+++ b/chrome/browser/resources/bluetooth_internals/characteristic_list.js
@@ -45,14 +45,22 @@
    * CharacteristicInfo object.
    * @constructor
    * @param {!interfaces.BluetoothDevice.CharacteristicInfo} characteristicInfo
+   * @param {string} deviceAddress
+   * @param {string} serviceId
    */
-  function CharacteristicListItem(characteristicInfo) {
+  function CharacteristicListItem(
+      characteristicInfo, deviceAddress, serviceId) {
     var listItem = new ExpandableListItem();
     listItem.__proto__ = CharacteristicListItem.prototype;
 
+    /** @type {!interfaces.BluetoothDevice.CharacteristicInfo} */
     listItem.info = characteristicInfo;
-    listItem.decorate();
+    /** @private {string} */
+    listItem.deviceAddress_ = deviceAddress;
+    /** @private {string} */
+    listItem.serviceId_ = serviceId;
 
+    listItem.decorate();
     return listItem;
   }
 
@@ -103,6 +111,9 @@
             Property.WRITE_ENCRYPTED_AUTHENTICATED) > 0,
       });
 
+      /** @private {!descriptor_list.DescriptorList} */
+      this.descriptorList_ = new descriptor_list.DescriptorList();
+
       // Create content for display in brief content container.
       var characteristicHeaderText = document.createElement('div');
       characteristicHeaderText.textContent = 'Characteristic:';
@@ -130,15 +141,26 @@
       propertiesDiv.classList.add('flex');
       propertiesDiv.appendChild(this.propertiesFieldSet_);
 
+      var descriptorsHeader = document.createElement('h4');
+      descriptorsHeader.textContent = 'Descriptors';
+
       var infoDiv = document.createElement('div');
       infoDiv.classList.add('info-container');
       infoDiv.appendChild(characteristicInfoHeader);
       infoDiv.appendChild(characteristicDiv);
       infoDiv.appendChild(propertiesHeader);
       infoDiv.appendChild(propertiesDiv);
+      infoDiv.appendChild(descriptorsHeader);
+      infoDiv.appendChild(this.descriptorList_);
 
       this.expandedContent_.appendChild(infoDiv);
     },
+
+    /** @override */
+    onExpandInternal: function(expanded) {
+      this.descriptorList_.load(
+          this.deviceAddress_, this.serviceId_, this.info.id);
+    },
   };
 
   /**
@@ -163,7 +185,8 @@
 
     /** @override */
     createItem: function(data) {
-      return new CharacteristicListItem(data);
+      return new CharacteristicListItem(
+          data, this.deviceAddress_, this.serviceId_);
     },
 
     /**
@@ -174,16 +197,18 @@
      * @param {string} serviceId
      */
     load: function(deviceAddress, serviceId) {
-      if (this.characteristicsRequested_ || !this.isLoading())
+      if (this.characteristicsRequested_ || !this.isSpinnerShowing())
         return;
 
+      this.deviceAddress_ = deviceAddress;
+      this.serviceId_ = serviceId;
       this.characteristicsRequested_ = true;
 
       device_broker.connectToDevice(deviceAddress).then(function(device) {
         return device.getCharacteristics(serviceId);
       }.bind(this)).then(function(response) {
         this.setData(new ArrayDataModel(response.characteristics || []));
-        this.setLoading(false);
+        this.setSpinnerShowing(false);
         this.characteristicsRequested_ = false;
       }.bind(this)).catch(function(error) {
         this.characteristicsRequested_ = false;
@@ -200,4 +225,4 @@
     CharacteristicList: CharacteristicList,
     CharacteristicListItem: CharacteristicListItem,
   };
-});
\ No newline at end of file
+});
diff --git a/chrome/browser/resources/bluetooth_internals/descriptor_list.js b/chrome/browser/resources/bluetooth_internals/descriptor_list.js
new file mode 100644
index 0000000..3b026c30
--- /dev/null
+++ b/chrome/browser/resources/bluetooth_internals/descriptor_list.js
@@ -0,0 +1,153 @@
+// 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.
+
+/**
+ * Javascript for DescriptorList and DescriptorListItem, served from
+ *     chrome://bluetooth-internals/.
+ */
+
+cr.define('descriptor_list', function() {
+  /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
+  /** @const */ var ExpandableList = expandable_list.ExpandableList;
+  /** @const */ var ExpandableListItem = expandable_list.ExpandableListItem;
+  /** @const */ var Snackbar = snackbar.Snackbar;
+  /** @const */ var SnackbarType = snackbar.SnackbarType;
+
+  /** Property names for the DescriptorInfo fieldset */
+  var INFO_PROPERTY_NAMES = {
+    id: 'ID',
+    'uuid.uuid': 'UUID',
+  };
+
+  /**
+   * A list item that displays the properties of a DescriptorInfo object.
+   * A fieldset is created within the element for the primitive
+   * properties, 'id' and 'uuid' within the DescriptorInfo object.
+   * @constructor
+   * @param {!interfaces.BluetoothDevice.DescriptorInfo} descriptorInfo
+   */
+  function DescriptorListItem(descriptorInfo) {
+    var listItem = new ExpandableListItem();
+    listItem.__proto__ = DescriptorListItem.prototype;
+
+    /** @type {!interfaces.BluetoothDevice.DescriptorInfo} */
+    listItem.info = descriptorInfo;
+
+    listItem.decorate();
+    return listItem;
+  }
+
+  DescriptorListItem.prototype = {
+    __proto__: ExpandableListItem.prototype,
+
+    /**
+     * Decorates the element as a descriptor list item. Creates and caches
+     * a fieldset for displaying property values.
+     * @override
+     */
+    decorate: function() {
+      this.classList.add('descriptor-list-item');
+
+      /** @private {!object_fieldset.ObjectFieldSet} */
+      this.descriptorFieldSet_ = object_fieldset.ObjectFieldSet();
+      this.descriptorFieldSet_.setPropertyDisplayNames(INFO_PROPERTY_NAMES);
+      this.descriptorFieldSet_.setObject({
+        id: this.info.id,
+        'uuid.uuid': this.info.uuid.uuid,
+      });
+
+      // Create content for display in brief content container.
+      var descriptorHeaderText = document.createElement('div');
+      descriptorHeaderText.textContent = 'Descriptor:';
+
+      var descriptorHeaderValue = document.createElement('div');
+      descriptorHeaderValue.textContent = this.info.uuid.uuid;
+
+      var descriptorHeader = document.createElement('div');
+      descriptorHeader.appendChild(descriptorHeaderText);
+      descriptorHeader.appendChild(descriptorHeaderValue);
+      this.briefContent_.appendChild(descriptorHeader);
+
+      // Create content for display in expanded content container.
+      var descriptorInfoHeader = document.createElement('h4');
+      descriptorInfoHeader.textContent = 'Descriptor Info';
+
+      var descriptorDiv = document.createElement('div');
+      descriptorDiv.classList.add('flex');
+      descriptorDiv.appendChild(this.descriptorFieldSet_);
+
+      var infoDiv = document.createElement('div');
+      infoDiv.classList.add('info-container');
+      infoDiv.appendChild(descriptorInfoHeader);
+      infoDiv.appendChild(descriptorDiv);
+
+      this.expandedContent_.appendChild(infoDiv);
+    },
+  };
+
+  /**
+   * A list that displays DescriptorListItems.
+   * @constructor
+   */
+  var DescriptorList = cr.ui.define('list');
+
+  DescriptorList.prototype = {
+    __proto__: ExpandableList.prototype,
+
+    /** @override */
+    decorate: function() {
+      ExpandableList.prototype.decorate.call(this);
+
+      /** @private {boolean} */
+      this.descriptorsRequested_ = false;
+
+      this.classList.add('descriptor-list');
+      this.setEmptyMessage('No Descriptors Found');
+    },
+
+    /** @override */
+    createItem: function(data) {
+      return new DescriptorListItem(data);
+    },
+
+    /**
+     * Loads the descriptor list with an array of DescriptorInfo from
+     * the device with |deviceAddress|, service with |serviceId|, and
+     * characteristic with |characteristicId|. If no active connection to the
+     * device exists, one is created.
+     * @param {string} deviceAddress
+     * @param {string} serviceId
+     * @param {string} characteristicId
+     */
+    load: function(deviceAddress, serviceId, characteristicId) {
+      if (this.descriptorsRequested_ || !this.isSpinnerShowing())
+        return;
+
+      this.descriptorsRequested_ = true;
+
+      device_broker.connectToDevice(deviceAddress)
+          .then(function(device) {
+            return device.getDescriptors(serviceId, characteristicId);
+          }.bind(this))
+          .then(function(response) {
+            this.setData(new ArrayDataModel(response.descriptors || []));
+            this.setSpinnerShowing(false);
+            this.descriptorsRequested_ = false;
+          }.bind(this))
+          .catch(function(error) {
+            this.descriptorsRequested_ = false;
+            Snackbar.show(
+                deviceAddress + ': ' + error.message, SnackbarType.ERROR,
+                'Retry', function() {
+                  this.load(deviceAddress, serviceId, characteristicId);
+                }.bind(this));
+          }.bind(this));
+    },
+  };
+
+  return {
+    DescriptorList: DescriptorList,
+    DescriptorListItem: DescriptorListItem,
+  };
+});
\ No newline at end of file
diff --git a/chrome/browser/resources/bluetooth_internals/expandable_list.js b/chrome/browser/resources/bluetooth_internals/expandable_list.js
index 81de23f..5f543e68 100644
--- a/chrome/browser/resources/bluetooth_internals/expandable_list.js
+++ b/chrome/browser/resources/bluetooth_internals/expandable_list.js
@@ -84,7 +84,7 @@
 
       this.autoExpands = true;
       this.boundUpdateMessage_ = this.updateMessageDisplay_.bind(this);
-      this.setLoading(true);
+      this.setSpinnerShowing(true);
     },
 
     /**
@@ -109,19 +109,19 @@
     },
 
     /**
-     * Sets the loading state of the list. If |loading| is true, the loading
+     * Sets the spinner display state. If |showing| is true, the loading
      * spinner is dispayed.
-     * @param {boolean} loading
+     * @param {boolean} showing
      */
-    setLoading: function(loading) {
-      this.spinner_.hidden = !loading;
+    setSpinnerShowing: function(showing) {
+      this.spinner_.hidden = !showing;
     },
 
     /**
-     * Gets the loading state of the list. Returns true if the list is loading.
+     * Gets the spinner display state. Returns true if the spinner is showing.
      * @return {boolean}
      */
-    isLoading: function() {
+    isSpinnerShowing: function() {
       return !this.spinner_.hidden;
     },
 
diff --git a/chrome/browser/resources/bluetooth_internals/service_list.js b/chrome/browser/resources/bluetooth_internals/service_list.js
index 2fce378..0512e54 100644
--- a/chrome/browser/resources/bluetooth_internals/service_list.js
+++ b/chrome/browser/resources/bluetooth_internals/service_list.js
@@ -38,10 +38,12 @@
     var listItem = new ExpandableListItem();
     listItem.__proto__ = ServiceListItem.prototype;
 
+    /** @type {!interfaces.BluetoothDevice.ServiceInfo} */
     listItem.info = serviceInfo;
+    /** @private {string} */
     listItem.deviceAddress_ = deviceAddress;
-    listItem.decorate();
 
+    listItem.decorate();
     return listItem;
   }
 
@@ -139,7 +141,7 @@
      * @param {string} deviceAddress
      */
     load: function(deviceAddress) {
-      if (this.servicesRequested_ || !this.isLoading())
+      if (this.servicesRequested_ || !this.isSpinnerShowing())
         return;
 
       this.deviceAddress_ = deviceAddress;
@@ -150,7 +152,7 @@
             return device.getServices();
           }.bind(this)).then(function(response) {
             this.setData(new ArrayDataModel(response.services));
-            this.setLoading(false);
+            this.setSpinnerShowing(false);
             this.servicesRequested_ = false;
           }.bind(this)).catch(function(error) {
             this.servicesRequested_ = false;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index 805f001..e8379140 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -587,7 +587,7 @@
   Init();
 
   // Don't let the bookmarks show on top of the location bar while animating.
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetMasksToBounds(true);
   layer()->SetFillsBoundsOpaquely(false);
 
diff --git a/chrome/browser/ui/views/dropdown_bar_host.cc b/chrome/browser/ui/views/dropdown_bar_host.cc
index d954866..5a4d93b 100644
--- a/chrome/browser/ui/views/dropdown_bar_host.cc
+++ b/chrome/browser/ui/views/dropdown_bar_host.cc
@@ -43,7 +43,7 @@
   // The |clip_view| exists to paint to a layer so that it can clip descendent
   // Views which also paint to a Layer. See http://crbug.com/589497
   std::unique_ptr<views::View> clip_view(new views::View());
-  clip_view->SetPaintToLayer(true);
+  clip_view->SetPaintToLayer();
   clip_view->layer()->SetFillsBoundsOpaquely(false);
   clip_view->layer()->SetMasksToBounds(true);
   clip_view->AddChildView(view_);
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 9ebe8c7..0bf90ce 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -415,6 +415,10 @@
   // OS with some tabs than the NativeBrowserFrame should have destroyed them.
   DCHECK_EQ(0, browser_->tab_strip_model()->count());
 
+  // Stop the animation timer explicitly here to avoid running it in a nested
+  // message loop, which may run by Browser destructor.
+  loading_animation_timer_.Stop();
+
   // Immersive mode may need to reparent views before they are removed/deleted.
   immersive_mode_controller_.reset();
 
diff --git a/chrome/browser/ui/views/frame/contents_web_view.cc b/chrome/browser/ui/views/frame/contents_web_view.cc
index 151823ad..fab9246 100644
--- a/chrome/browser/ui/views/frame/contents_web_view.cc
+++ b/chrome/browser/ui/views/frame/contents_web_view.cc
@@ -105,7 +105,7 @@
     return;
   }
 
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   set_layer_owner_delegate(this);
 
   // The cloned layer is in a different coordinate system them our layer (which
@@ -120,7 +120,7 @@
 
 void ContentsWebView::DestroyClonedLayer() {
   cloned_layer_tree_.reset();
-  SetPaintToLayer(false);
+  DestroyLayer();
   set_layer_owner_delegate(nullptr);
 }
 
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index 763a15a..d7da4c5 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -260,7 +260,7 @@
   DestroyMashRevealWidget();
 
   visible_fraction_ = 0;
-  browser_view_->top_container()->SetPaintToLayer(true);
+  browser_view_->top_container()->SetPaintToLayer();
   browser_view_->top_container()->layer()->SetFillsBoundsOpaquely(false);
   UpdateTabIndicators();
   LayoutBrowserRootView();
@@ -272,14 +272,14 @@
 void ImmersiveModeControllerAsh::OnImmersiveRevealEnded() {
   DestroyMashRevealWidget();
   visible_fraction_ = 0;
-  browser_view_->top_container()->SetPaintToLayer(false);
+  browser_view_->top_container()->DestroyLayer();
   UpdateTabIndicators();
   LayoutBrowserRootView();
 }
 
 void ImmersiveModeControllerAsh::OnImmersiveFullscreenExited() {
   DestroyMashRevealWidget();
-  browser_view_->top_container()->SetPaintToLayer(false);
+  browser_view_->top_container()->DestroyLayer();
   UpdateTabIndicators();
   LayoutBrowserRootView();
 }
diff --git a/chrome/browser/ui/views/infobars/infobar_container_view.cc b/chrome/browser/ui/views/infobars/infobar_container_view.cc
index 988833e..82f94a0e 100644
--- a/chrome/browser/ui/views/infobars/infobar_container_view.cc
+++ b/chrome/browser/ui/views/infobars/infobar_container_view.cc
@@ -26,7 +26,7 @@
 class ContentShadow : public views::View {
  public:
   ContentShadow() {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
   }
   ~ContentShadow() override {}
diff --git a/chrome/browser/ui/views/infobars/infobar_view.cc b/chrome/browser/ui/views/infobars/infobar_view.cc
index 8d14d2f..5fffc58 100644
--- a/chrome/browser/ui/views/infobars/infobar_view.cc
+++ b/chrome/browser/ui/views/infobars/infobar_view.cc
@@ -80,10 +80,10 @@
 
   AddChildView(child_container_);
 
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 
-  child_container_->SetPaintToLayer(true);
+  child_container_->SetPaintToLayer();
   child_container_->layer()->SetMasksToBounds(true);
   child_container_->set_background(views::Background::CreateSolidBackground(
       infobars::InfoBar::GetBackgroundColor(
diff --git a/chrome/browser/ui/views/location_bar/bubble_icon_view.cc b/chrome/browser/ui/views/location_bar/bubble_icon_view.cc
index 84b8e234..a87aab9 100644
--- a/chrome/browser/ui/views/location_bar/bubble_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/bubble_icon_view.cc
@@ -131,14 +131,14 @@
 }
 
 void BubbleIconView::AddInkDropLayer(ui::Layer* ink_drop_layer) {
-  image_->SetPaintToLayer(true);
+  image_->SetPaintToLayer();
   image_->layer()->SetFillsBoundsOpaquely(false);
   views::InkDropHostView::AddInkDropLayer(ink_drop_layer);
 }
 
 void BubbleIconView::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
   views::InkDropHostView::RemoveInkDropLayer(ink_drop_layer);
-  image_->SetPaintToLayer(false);
+  image_->DestroyLayer();
 }
 
 std::unique_ptr<views::InkDrop> BubbleIconView::CreateInkDrop() {
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index 9d1ff7d..eb02b9e0 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -143,14 +143,14 @@
 }
 
 void IconLabelBubbleView::AddInkDropLayer(ui::Layer* ink_drop_layer) {
-  image()->SetPaintToLayer(true);
+  image()->SetPaintToLayer();
   image()->layer()->SetFillsBoundsOpaquely(false);
   InkDropHostView::AddInkDropLayer(ink_drop_layer);
 }
 
 void IconLabelBubbleView::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
   InkDropHostView::RemoveInkDropLayer(ink_drop_layer);
-  image()->SetPaintToLayer(false);
+  image()->DestroyLayer();
 }
 
 std::unique_ptr<views::InkDropHighlight>
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index dfd5d80..8b2f203 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -176,7 +176,7 @@
   DCHECK(GetWidget());
 
   // Make sure children with layers are clipped. See http://crbug.com/589497
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   layer()->SetMasksToBounds(true);
 
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.cc b/chrome/browser/ui/views/payments/payment_request_views_util.cc
index 662fe9f0..6e7643ab 100644
--- a/chrome/browser/ui/views/payments/payment_request_views_util.cc
+++ b/chrome/browser/ui/views/payments/payment_request_views_util.cc
@@ -90,7 +90,7 @@
 
   // Paint the sheets to layers, otherwise the MD buttons (which do paint to a
   // layer) won't do proper clipping.
-  view->SetPaintToLayer(true);
+  view->SetPaintToLayer();
 
   views::GridLayout* layout = new views::GridLayout(view.get());
   view->SetLayoutManager(layout);
diff --git a/chrome/browser/ui/views/payments/view_stack.cc b/chrome/browser/ui/views/payments/view_stack.cc
index 13367452..e9780f3b 100644
--- a/chrome/browser/ui/views/payments/view_stack.cc
+++ b/chrome/browser/ui/views/payments/view_stack.cc
@@ -16,7 +16,7 @@
   // Paint to a layer and Mask to Bounds, otherwise descendant views that paint
   // to a layer themselves will still paint while they're being animated out and
   // are out of bounds of their parent.
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetMasksToBounds(true);
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index c209ad6a..39c0a44f 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -1458,10 +1458,12 @@
   // when possible to reduce repaint overhead.
   const bool paint_to_layer = controller_->CanPaintThrobberToLayer();
   if (paint_to_layer != !!throbber_->layer()) {
-    throbber_->SetPaintToLayer(paint_to_layer);
     if (paint_to_layer) {
+      throbber_->SetPaintToLayer();
       throbber_->layer()->SetFillsBoundsOpaquely(false);
       ScheduleIconPaint();  // Ensure the non-layered throbber goes away.
+    } else {
+      throbber_->DestroyLayer();
     }
   }
   if (!throbber_->visible()) {
diff --git a/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc b/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
index 9ef6db5..608e40e 100644
--- a/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
+++ b/chrome/browser/ui/webui/bluetooth_internals/bluetooth_internals_ui.cc
@@ -26,6 +26,8 @@
                                IDR_BLUETOOTH_INTERNALS_JS);
   html_source->AddResourcePath("characteristic_list.js",
                                IDR_BLUETOOTH_INTERNALS_CHARACTERISTIC_LIST_JS);
+  html_source->AddResourcePath("descriptor_list.js",
+                               IDR_BLUETOOTH_INTERNALS_DESCRIPTOR_LIST_JS);
   html_source->AddResourcePath("device_broker.js",
                                IDR_BLUETOOTH_INTERNALS_DEVICE_BROKER_JS);
   html_source->AddResourcePath("device_collection.js",
diff --git a/chrome/test/data/banners/manifest_data_url_icon.json b/chrome/test/data/banners/manifest_data_url_icon.json
new file mode 100644
index 0000000..3a435bd1
--- /dev/null
+++ b/chrome/test/data/banners/manifest_data_url_icon.json
@@ -0,0 +1,13 @@
+{
+  "name": "Manifest test app",
+  "icons": [
+    {
+      "src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAACQAQMAAADdiHD7AAAABlBMVEUAAAAAAAClZ7nPAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAGklEQVRIie3BMQEAAADCoPVPbQ0PoAAA4NcACrAAAcLJlncAAAAASUVORK5CYII=",
+      "type": "image/png",
+      "sizes": "192x192"
+    }
+  ],
+  "start_url": "manifest_test_page.html",
+  "display": "standalone",
+  "orientation": "landscape"
+}
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index d2a931c..88096ec 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -481,7 +481,7 @@
     if (credit_card->record_type() == CreditCard::LOCAL_CARD)
       database_->UpdateCreditCard(*credit_card);
     else
-      database_->UpdateServerCardUsageStats(*credit_card);
+      database_->UpdateServerCardMetadata(*credit_card);
 
     Refresh();
     return;
@@ -494,7 +494,7 @@
     if (profile->record_type() == AutofillProfile::LOCAL_PROFILE)
       database_->UpdateAutofillProfile(*profile);
     else if (profile->record_type() == AutofillProfile::SERVER_PROFILE)
-      database_->UpdateServerAddressUsageStats(*profile);
+      database_->UpdateServerAddressMetadata(*profile);
 
     Refresh();
   }
@@ -649,25 +649,10 @@
     const CreditCard& credit_card) {
   DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
 
-  if (!database_.get())
+  if (is_off_the_record_ || !database_.get())
     return;
 
-  CreditCard* existing_credit_card = nullptr;
-  for (auto& server_card : server_credit_cards_) {
-    if (credit_card.server_id() == server_card->server_id()) {
-      existing_credit_card = server_card.get();
-      break;
-    }
-  }
-  if (!existing_credit_card
-      || existing_credit_card->billing_address_id() ==
-          credit_card.billing_address_id()) {
-    return;
-  }
-
-  existing_credit_card->set_billing_address_id(
-      credit_card.billing_address_id());
-  database_->UpdateServerCardBillingAddress(*existing_credit_card);
+  database_->UpdateServerCardMetadata(credit_card);
 
   Refresh();
 }
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc
index 8d03d93..503eb0f 100644
--- a/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_table.cc
@@ -471,6 +471,9 @@
     case 70:
       *update_compatible_version = false;
       return MigrateToVersion70AddSyncMetadata();
+    case 71:
+      *update_compatible_version = true;
+      return MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata();
   }
   return true;
 }
@@ -1210,17 +1213,17 @@
 
   sql::Statement s(db_->GetUniqueStatement(
       "SELECT "
-      "card_number_encrypted, "  // 0
-      "last_four,"     // 1
-      "masked.id,"     // 2
-      "metadata.use_count,"     // 3
-      "metadata.use_date,"      // 4
-      "type,"          // 5
-      "status,"        // 6
-      "name_on_card,"  // 7
-      "exp_month,"     // 8
-      "exp_year,"      // 9
-      "billing_address_id "     // 10
+      "card_number_encrypted, "       // 0
+      "last_four,"                    // 1
+      "masked.id,"                    // 2
+      "metadata.use_count,"           // 3
+      "metadata.use_date,"            // 4
+      "type,"                         // 5
+      "status,"                       // 6
+      "name_on_card,"                 // 7
+      "exp_month,"                    // 8
+      "exp_year,"                     // 9
+      "metadata.billing_address_id "  // 10
       "FROM masked_credit_cards masked "
       "LEFT OUTER JOIN unmasked_credit_cards USING (id) "
       "LEFT OUTER JOIN server_card_metadata metadata USING (id)"));
@@ -1279,17 +1282,16 @@
       "DELETE FROM masked_credit_cards"));
   masked_delete.Run();
 
-  sql::Statement masked_insert(db_->GetUniqueStatement(
-      "INSERT INTO masked_credit_cards("
-      "id,"            // 0
-      "type,"          // 1
-      "status,"        // 2
-      "name_on_card,"  // 3
-      "last_four,"     // 4
-      "exp_month,"     // 5
-      "exp_year,"      // 6
-      "billing_address_id) "  // 7
-      "VALUES (?,?,?,?,?,?,?,?)"));
+  sql::Statement masked_insert(
+      db_->GetUniqueStatement("INSERT INTO masked_credit_cards("
+                              "id,"            // 0
+                              "type,"          // 1
+                              "status,"        // 2
+                              "name_on_card,"  // 3
+                              "last_four,"     // 4
+                              "exp_month,"     // 5
+                              "exp_year)"      // 6
+                              "VALUES (?,?,?,?,?,?,?)"));
   for (const CreditCard& card : credit_cards) {
     DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
 
@@ -1302,13 +1304,12 @@
     masked_insert.BindString16(5, card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
     masked_insert.BindString16(6,
                                card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
-    masked_insert.BindString(7, card.billing_address_id());
 
     masked_insert.Run();
     masked_insert.Reset(true);
 
     // Save the use count and use date of the card.
-    UpdateServerCardUsageStats(card);
+    UpdateServerCardMetadata(card);
   }
 
   // Delete all items in the unmasked table that aren't in the new set.
@@ -1349,7 +1350,7 @@
   unmasked.set_record_type(CreditCard::FULL_SERVER_CARD);
   unmasked.SetNumber(full_number);
   unmasked.RecordAndLogUse();
-  UpdateServerCardUsageStats(unmasked);
+  UpdateServerCardMetadata(unmasked);
 
   return db_->GetLastChangeCount() > 0;
 }
@@ -1362,8 +1363,7 @@
   return db_->GetLastChangeCount() > 0;
 }
 
-bool AutofillTable::UpdateServerCardUsageStats(
-    const CreditCard& credit_card) {
+bool AutofillTable::UpdateServerCardMetadata(const CreditCard& credit_card) {
   DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
   sql::Transaction transaction(db_);
   if (!transaction.Begin())
@@ -1374,12 +1374,14 @@
   remove.BindString(0, credit_card.server_id());
   remove.Run();
 
-  sql::Statement s(db_->GetUniqueStatement(
-      "INSERT INTO server_card_metadata(use_count, use_date, id)"
-      "VALUES (?,?,?)"));
+  sql::Statement s(
+      db_->GetUniqueStatement("INSERT INTO server_card_metadata(use_count, "
+                              "use_date, billing_address_id, id)"
+                              "VALUES (?,?,?,?)"));
   s.BindInt64(0, credit_card.use_count());
   s.BindInt64(1, credit_card.use_date().ToInternalValue());
-  s.BindString(2, credit_card.server_id());
+  s.BindString(2, credit_card.billing_address_id());
+  s.BindString(3, credit_card.server_id());
   s.Run();
 
   transaction.Commit();
@@ -1387,7 +1389,9 @@
   return db_->GetLastChangeCount() > 0;
 }
 
-bool AutofillTable::UpdateServerAddressUsageStats(
+// TODO(crbug.com/680182): Record the address conversion status when a server
+// address gets converted.
+bool AutofillTable::UpdateServerAddressMetadata(
     const AutofillProfile& profile) {
   DCHECK_EQ(AutofillProfile::SERVER_PROFILE, profile.record_type());
 
@@ -1400,12 +1404,14 @@
   remove.BindString(0, profile.server_id());
   remove.Run();
 
-  sql::Statement s(db_->GetUniqueStatement(
-      "INSERT INTO server_address_metadata(use_count, use_date, id)"
-      "VALUES (?,?,?)"));
+  sql::Statement s(
+      db_->GetUniqueStatement("INSERT INTO server_address_metadata(use_count, "
+                              "use_date, has_converted, id)"
+                              "VALUES (?,?,?,?)"));
   s.BindInt64(0, profile.use_count());
   s.BindInt64(1, profile.use_date().ToInternalValue());
-  s.BindString(2, profile.server_id());
+  s.BindBool(2, false);
+  s.BindString(3, profile.server_id());
   s.Run();
 
   transaction.Commit();
@@ -1413,21 +1419,6 @@
   return db_->GetLastChangeCount() > 0;
 }
 
-bool AutofillTable::UpdateServerCardBillingAddress(
-    const CreditCard& credit_card) {
-  DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
-
-  sql::Statement update(db_->GetUniqueStatement(
-      "UPDATE masked_credit_cards SET billing_address_id = ? "
-      "WHERE id = ?"));
-  update.BindString(0, credit_card.billing_address_id());
-  update.BindString(1, credit_card.server_id());
-  if (!update.Run())
-    return false;
-
-  return db_->GetLastChangeCount() > 0;
-}
-
 bool AutofillTable::ClearAllServerData() {
   sql::Transaction transaction(db_);
   if (!transaction.Begin())
@@ -1928,8 +1919,7 @@
                       "type VARCHAR,"
                       "last_four VARCHAR,"
                       "exp_month INTEGER DEFAULT 0,"
-                      "exp_year INTEGER DEFAULT 0, "
-                      "billing_address_id VARCHAR)")) {
+                      "exp_year INTEGER DEFAULT 0)")) {
       NOTREACHED();
       return false;
     }
@@ -1957,7 +1947,8 @@
     if (!db_->Execute("CREATE TABLE server_card_metadata ("
                       "id VARCHAR NOT NULL,"
                       "use_count INTEGER NOT NULL DEFAULT 0, "
-                      "use_date INTEGER NOT NULL DEFAULT 0)")) {
+                      "use_date INTEGER NOT NULL DEFAULT 0, "
+                      "billing_address_id VARCHAR)")) {
       NOTREACHED();
       return false;
     }
@@ -1995,7 +1986,8 @@
     if (!db_->Execute("CREATE TABLE server_address_metadata ("
                       "id VARCHAR NOT NULL,"
                       "use_count INTEGER NOT NULL DEFAULT 0, "
-                      "use_date INTEGER NOT NULL DEFAULT 0)")) {
+                      "use_date INTEGER NOT NULL DEFAULT 0, "
+                      "has_converted BOOL NOT NULL DEFAULT FALSE)")) {
       NOTREACHED();
       return false;
     }
@@ -2473,4 +2465,66 @@
       "BLOB)");
 }
 
+bool AutofillTable::
+    MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata() {
+  sql::Transaction transaction(db_);
+  if (!transaction.Begin())
+    return false;
+
+  // Add the new has_converted column to the server_address_metadata table.
+  if (!db_->DoesColumnExist("server_address_metadata", "has_converted") &&
+      !db_->Execute("ALTER TABLE server_address_metadata ADD COLUMN "
+                    "has_converted BOOL NOT NULL DEFAULT FALSE")) {
+    return false;
+  }
+
+  // Add the new billing_address_id column to the server_card_metadata table.
+  if (!db_->DoesColumnExist("server_card_metadata", "billing_address_id") &&
+      !db_->Execute("ALTER TABLE server_card_metadata ADD COLUMN "
+                    "billing_address_id VARCHAR")) {
+    return false;
+  }
+
+  // Copy over the billing_address_id from the masked_server_cards to
+  // server_card_metadata.
+  if (!db_->Execute("UPDATE server_card_metadata "
+                    "SET billing_address_id = "
+                    "(SELECT billing_address_id "
+                    "FROM masked_credit_cards "
+                    "WHERE id = server_card_metadata.id)")) {
+    return false;
+  }
+
+  // Remove the billing_address_id column from the masked_credit_cards table.
+  // Create a temporary table that is a copy of masked_credit_cards but without
+  // the billing_address_id column.
+  if (db_->DoesTableExist("masked_credit_cards_temp") ||
+      !db_->Execute("CREATE TABLE masked_credit_cards_temp ("
+                    "id VARCHAR,"
+                    "status VARCHAR,"
+                    "name_on_card VARCHAR,"
+                    "type VARCHAR,"
+                    "last_four VARCHAR,"
+                    "exp_month INTEGER DEFAULT 0,"
+                    "exp_year INTEGER DEFAULT 0)")) {
+    return false;
+  }
+  // Copy over the data from the original masked_credit_cards table.
+  if (!db_->Execute("INSERT INTO masked_credit_cards_temp "
+                    "SELECT id, status, name_on_card, type, last_four, "
+                    "exp_month, exp_year "
+                    "FROM masked_credit_cards")) {
+    return false;
+  }
+  // Delete the existing table and replace it with the contents of the
+  // temporary table.
+  if (!db_->Execute("DROP TABLE masked_credit_cards") ||
+      !db_->Execute("ALTER TABLE masked_credit_cards_temp "
+                    "RENAME TO masked_credit_cards")) {
+    return false;
+  }
+
+  return transaction.Commit();
+}
+
 }  // namespace autofill
\ No newline at end of file
diff --git a/components/autofill/core/browser/webdata/autofill_table.h b/components/autofill/core/browser/webdata/autofill_table.h
index c01775f..2f5fff15 100644
--- a/components/autofill/core/browser/webdata/autofill_table.h
+++ b/components/autofill/core/browser/webdata/autofill_table.h
@@ -167,10 +167,6 @@
 //                      with locally stored cards and generating descriptions.
 //   exp_month          Expiration month: 1-12
 //   exp_year           Four-digit year: 2017
-//   billing_address_id The string that identifies the local or server profile
-//                      which is the billing address for this card. Can be null
-//                      in the database, but always returned as an empty string
-//                      in CreditCard. Added in version 67.
 //
 // unmasked_credit_cards
 //                      When a masked credit credit card is unmasked and the
@@ -182,6 +178,7 @@
 //                      Full card number, encrypted.
 //   use_count          DEPRECATED in version 65. See server_card_metadata.
 //   use_date           DEPRECATED in version 65. See server_card_metadata.
+//                      TODO(crbug.com/682326): Remove deprecated columns.
 //   unmask_date        The date this card was unmasked in units of
 //                      Time::ToInternalValue. Added in version 64.
 //
@@ -195,6 +192,10 @@
 //                      a form.
 //   use_date           The date this card was last used to fill a form,
 //                      in internal t.
+//   billing_address_id The string that identifies the profile which is the
+//                      billing address for this card. Can be null in the
+//                      database, but always returned as an empty string in
+//                      CreditCard. Added in version 71.
 //
 // server_addresses     This table contains Autofill address data synced from
 //                      the wallet server. It's basically the same as the
@@ -235,6 +236,9 @@
 //                      a form.
 //   use_date           The date this address was last used to fill a form,
 //                      in internal t.
+//   has_converted      Whether this server address has been converted to a
+//                      local autofill profile.
+//
 // autofill_sync_metadata
 //                      Sync-specific metadata for autofill records.
 //
@@ -375,10 +379,8 @@
                               const base::string16& full_number);
   bool MaskServerCreditCard(const std::string& id);
 
-  bool UpdateServerCardUsageStats(const CreditCard& credit_card);
-  bool UpdateServerAddressUsageStats(const AutofillProfile& profile);
-
-  bool UpdateServerCardBillingAddress(const CreditCard& credit_card);
+  bool UpdateServerCardMetadata(const CreditCard& credit_card);
+  bool UpdateServerAddressMetadata(const AutofillProfile& profile);
 
   // Deletes all data from the server card and profile tables. Returns true if
   // any data was deleted, false if not (so false means "commit not needed"
@@ -461,6 +463,7 @@
   bool MigrateToVersion66AddCardBillingAddress();
   bool MigrateToVersion67AddMaskedCardBillingAddress();
   bool MigrateToVersion70AddSyncMetadata();
+  bool MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata();
 
   // Max data length saved in the table, AKA the maximum length allowed for
   // form data.
diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index d5a80c0..cd93d29 100644
--- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -1743,7 +1743,7 @@
   outputs.clear();
 }
 
-TEST_F(AutofillTableTest, SetServerCardUpdateUsageStats) {
+TEST_F(AutofillTableTest, SetServerCardUpdateUsageStatsAndBillingAddress) {
   // Add a masked card.
   CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
   masked_card.SetRawInfo(CREDIT_CARD_NAME_FULL,
@@ -1751,6 +1751,7 @@
   masked_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("1"));
   masked_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2020"));
   masked_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1111"));
+  masked_card.set_billing_address_id("1");
   masked_card.SetTypeForMaskedCard(kVisaCard);
 
   std::vector<CreditCard> inputs;
@@ -1771,13 +1772,15 @@
   // Update the usage stats; make sure they're reflected in GetServerProfiles.
   inputs.back().set_use_count(4U);
   inputs.back().set_use_date(base::Time());
-  table_->UpdateServerCardUsageStats(inputs.back());
+  inputs.back().set_billing_address_id("2");
+  table_->UpdateServerCardMetadata(inputs.back());
   table_->GetServerCreditCards(&outputs);
   ASSERT_EQ(1u, outputs.size());
   EXPECT_EQ(masked_card.server_id(), outputs[0]->server_id());
   EXPECT_EQ(4U, outputs[0]->use_count());
   EXPECT_EQ(base::Time(), outputs[0]->use_date());
   EXPECT_EQ(base::Time(), outputs[0]->modification_date());
+  EXPECT_EQ("2", outputs[0]->billing_address_id());
   outputs.clear();
 
   // Setting the cards again shouldn't delete the usage stats.
@@ -1788,6 +1791,7 @@
   EXPECT_EQ(4U, outputs[0]->use_count());
   EXPECT_EQ(base::Time(), outputs[0]->use_date());
   EXPECT_EQ(base::Time(), outputs[0]->modification_date());
+  EXPECT_EQ("2", outputs[0]->billing_address_id());
   outputs.clear();
 
   // Set a card list where the card is missing --- this should clear metadata.
@@ -1804,36 +1808,10 @@
   EXPECT_EQ(1U, outputs[0]->use_count());
   EXPECT_NE(base::Time(), outputs[0]->use_date());
   EXPECT_EQ(base::Time(), outputs[0]->modification_date());
+  EXPECT_EQ("1", outputs[0]->billing_address_id());
   outputs.clear();
 }
 
-TEST_F(AutofillTableTest, UpdateServerCardBillingAddress) {
-  // Add a masked card.
-  CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
-  masked_card.SetRawInfo(CREDIT_CARD_NAME_FULL,
-                         ASCIIToUTF16("Paul F. Tompkins"));
-  masked_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("1"));
-  masked_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2020"));
-  masked_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1111"));
-  masked_card.set_billing_address_id("billing-address-id-1");
-  masked_card.SetTypeForMaskedCard(kVisaCard);
-  test::SetServerCreditCards(table_.get(),
-                             std::vector<CreditCard>(1, masked_card));
-  std::vector<std::unique_ptr<CreditCard>> outputs;
-  table_->GetServerCreditCards(&outputs);
-  ASSERT_EQ(1u, outputs.size());
-
-  EXPECT_EQ("billing-address-id-1", outputs[0]->billing_address_id());
-
-  masked_card.set_billing_address_id("billing-address-id-2");
-  table_->UpdateServerCardBillingAddress(masked_card);
-  outputs.clear();
-  table_->GetServerCreditCards(&outputs);
-  ASSERT_EQ(1u, outputs.size());
-
-  EXPECT_EQ("billing-address-id-2", outputs[0]->billing_address_id());
-}
-
 TEST_F(AutofillTableTest, SetServerProfile) {
   AutofillProfile one(AutofillProfile::SERVER_PROFILE, "a123");
   std::vector<AutofillProfile> inputs;
@@ -1880,7 +1858,7 @@
   // Update the usage stats; make sure they're reflected in GetServerProfiles.
   inputs.back().set_use_count(4U);
   inputs.back().set_use_date(base::Time::Now());
-  table_->UpdateServerAddressUsageStats(inputs.back());
+  table_->UpdateServerAddressMetadata(inputs.back());
   table_->GetServerProfiles(&outputs);
   ASSERT_EQ(1u, outputs.size());
   EXPECT_EQ(one.server_id(), outputs[0]->server_id());
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
index 49ee726..326ddc4 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
@@ -506,13 +506,13 @@
 bool AutofillWalletMetadataSyncableService::UpdateAddressStats(
     const AutofillProfile& profile) {
   return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase())
-      ->UpdateServerAddressUsageStats(profile);
+      ->UpdateServerAddressMetadata(profile);
 }
 
 bool AutofillWalletMetadataSyncableService::UpdateCardStats(
     const CreditCard& credit_card) {
   return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase())
-      ->UpdateServerCardUsageStats(credit_card);
+      ->UpdateServerCardMetadata(credit_card);
 }
 
 syncer::SyncError
diff --git a/components/autofill/core/browser/webdata/autofill_webdata.h b/components/autofill/core/browser/webdata/autofill_webdata.h
index 919853d..eb57387 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata.h
+++ b/components/autofill/core/browser/webdata/autofill_webdata.h
@@ -109,16 +109,11 @@
                                       const base::string16& full_number) = 0;
   virtual void MaskServerCreditCard(const std::string& id) = 0;
 
-  // Updates the use count and last use date for a server card (masked or not).
-  virtual void UpdateServerCardUsageStats(const CreditCard& credit_card) = 0;
+  // Updates the metadata for a server card (masked or not).
+  virtual void UpdateServerCardMetadata(const CreditCard& credit_card) = 0;
 
-  // Updates the use count and last use date for a server address.
-  virtual void UpdateServerAddressUsageStats(const AutofillProfile& profile)
-      = 0;
-
-  // Updates the billing address for a server card (masked or not).
-  virtual void UpdateServerCardBillingAddress(const CreditCard& credit_card)
-      = 0;
+  // Updates the metadata for a server address.
+  virtual void UpdateServerAddressMetadata(const AutofillProfile& profile) = 0;
 
   // Removes Autofill records from the database.
   virtual void RemoveAutofillDataModifiedBetween(
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
index bcb9cf5..2129da0 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
+++ b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
@@ -370,11 +370,11 @@
   return WebDatabase::COMMIT_NOT_NEEDED;
 }
 
-WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardUsageStats(
+WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardMetadata(
     const CreditCard& card,
     WebDatabase* db) {
   DCHECK(db_thread_->BelongsToCurrentThread());
-  if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardUsageStats(card))
+  if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardMetadata(card))
     return WebDatabase::COMMIT_NOT_NEEDED;
 
   for (auto& db_observer : db_observer_list_) {
@@ -385,11 +385,11 @@
   return WebDatabase::COMMIT_NEEDED;
 }
 
-WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressUsageStats(
+WebDatabase::State AutofillWebDataBackendImpl::UpdateServerAddressMetadata(
     const AutofillProfile& profile,
     WebDatabase* db) {
   DCHECK(db_thread_->BelongsToCurrentThread());
-  if (!AutofillTable::FromWebDatabase(db)->UpdateServerAddressUsageStats(
+  if (!AutofillTable::FromWebDatabase(db)->UpdateServerAddressMetadata(
           profile)) {
     return WebDatabase::COMMIT_NOT_NEEDED;
   }
@@ -402,23 +402,6 @@
   return WebDatabase::COMMIT_NEEDED;
 }
 
-WebDatabase::State AutofillWebDataBackendImpl::UpdateServerCardBillingAddress(
-    const CreditCard& card,
-    WebDatabase* db) {
-  DCHECK(db_thread_->BelongsToCurrentThread());
-  if (!AutofillTable::FromWebDatabase(db)->UpdateServerCardBillingAddress(
-          card)) {
-    return WebDatabase::COMMIT_NOT_NEEDED;
-  }
-
-  for (auto& db_observer : db_observer_list_) {
-    db_observer.CreditCardChanged(
-        CreditCardChange(CreditCardChange::UPDATE, card.guid(), &card));
-  }
-
-  return WebDatabase::COMMIT_NEEDED;
-}
-
 WebDatabase::State AutofillWebDataBackendImpl::ClearAllServerData(
     WebDatabase* db) {
   DCHECK(db_thread_->BelongsToCurrentThread());
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
index a9e2d78..9badefe 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
+++ b/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
@@ -152,17 +152,11 @@
   WebDatabase::State MaskServerCreditCard(const std::string& id,
                                           WebDatabase* db);
 
-  WebDatabase::State UpdateServerCardUsageStats(
-      const CreditCard& credit_card,
-      WebDatabase* db);
+  WebDatabase::State UpdateServerCardMetadata(const CreditCard& credit_card,
+                                              WebDatabase* db);
 
-  WebDatabase::State UpdateServerAddressUsageStats(
-      const AutofillProfile& profile,
-      WebDatabase* db);
-
-  WebDatabase::State UpdateServerCardBillingAddress(
-      const CreditCard& card,
-      WebDatabase* db);
+  WebDatabase::State UpdateServerAddressMetadata(const AutofillProfile& profile,
+                                                 WebDatabase* db);
 
   WebDatabase::State ClearAllServerData(WebDatabase* db);
 
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_service.cc b/components/autofill/core/browser/webdata/autofill_webdata_service.cc
index 22377e9..d8d072f 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_webdata_service.cc
@@ -212,28 +212,18 @@
            autofill_backend_));
 }
 
-void AutofillWebDataService::UpdateServerCardUsageStats(
+void AutofillWebDataService::UpdateServerCardMetadata(
     const CreditCard& credit_card) {
   wdbs_->ScheduleDBTask(
-      FROM_HERE,
-      Bind(&AutofillWebDataBackendImpl::UpdateServerCardUsageStats,
-           autofill_backend_, credit_card));
+      FROM_HERE, Bind(&AutofillWebDataBackendImpl::UpdateServerCardMetadata,
+                      autofill_backend_, credit_card));
 }
 
-void AutofillWebDataService::UpdateServerAddressUsageStats(
+void AutofillWebDataService::UpdateServerAddressMetadata(
     const AutofillProfile& profile) {
   wdbs_->ScheduleDBTask(
-      FROM_HERE,
-      Bind(&AutofillWebDataBackendImpl::UpdateServerAddressUsageStats,
-           autofill_backend_, profile));
-}
-
-void AutofillWebDataService::UpdateServerCardBillingAddress(
-    const CreditCard& credit_card) {
-  wdbs_->ScheduleDBTask(
-      FROM_HERE,
-      Bind(&AutofillWebDataBackendImpl::UpdateServerCardBillingAddress,
-           autofill_backend_, credit_card));
+      FROM_HERE, Bind(&AutofillWebDataBackendImpl::UpdateServerAddressMetadata,
+                      autofill_backend_, profile));
 }
 
 void AutofillWebDataService::RemoveAutofillDataModifiedBetween(
diff --git a/components/autofill/core/browser/webdata/autofill_webdata_service.h b/components/autofill/core/browser/webdata/autofill_webdata_service.h
index 44f57418..dfc35af 100644
--- a/components/autofill/core/browser/webdata/autofill_webdata_service.h
+++ b/components/autofill/core/browser/webdata/autofill_webdata_service.h
@@ -97,9 +97,8 @@
 
   void ClearAllServerData();
 
-  void UpdateServerCardUsageStats(const CreditCard& credit_card) override;
-  void UpdateServerAddressUsageStats(const AutofillProfile& profile) override;
-  void UpdateServerCardBillingAddress(const CreditCard& credit_card) override;
+  void UpdateServerCardMetadata(const CreditCard& credit_card) override;
+  void UpdateServerAddressMetadata(const AutofillProfile& profile) override;
 
   void RemoveAutofillDataModifiedBetween(const base::Time& delete_begin,
                                          const base::Time& delete_end) override;
diff --git a/components/filesystem/BUILD.gn b/components/filesystem/BUILD.gn
index 78ce4515..dd20c8c8 100644
--- a/components/filesystem/BUILD.gn
+++ b/components/filesystem/BUILD.gn
@@ -5,6 +5,7 @@
 import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
 import("//testing/test.gni")
 
 static_library("lib") {
@@ -63,31 +64,28 @@
   source = "manifest.json"
 }
 
-test("filesystem_service_unittests") {
+service_test("filesystem_service_unittests") {
   sources = [
     "directory_impl_unittest.cc",
     "file_impl_unittest.cc",
     "files_test_base.cc",
     "files_test_base.h",
-    "run_all_unittests.cc",
   ]
 
+  catalog = ":filesystem_service_unittests_catalog"
+
   deps = [
     "//base",
-    "//base/test:test_support",
     "//components/filesystem/public/interfaces",
     "//mojo/common",
-    "//mojo/edk/system",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
-    "//services/catalog:lib",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/service_manager/public/cpp:sources",
   ]
 
   data_deps = [
     ":filesystem",
-    ":filesystem_service_unittests_catalog_copy",
   ]
 }
 
@@ -100,13 +98,3 @@
   embedded_services = [ ":test_manifest" ]
   standalone_services = [ ":manifest" ]
 }
-
-copy("filesystem_service_unittests_catalog_copy") {
-  sources = get_target_outputs(":filesystem_service_unittests_catalog")
-  outputs = [
-    "${root_out_dir}/filesystem_service_unittests_catalog.json",
-  ]
-  deps = [
-    ":filesystem_service_unittests_catalog",
-  ]
-}
diff --git a/components/filesystem/DEPS b/components/filesystem/DEPS
index 1be3376..26e54241 100644
--- a/components/filesystem/DEPS
+++ b/components/filesystem/DEPS
@@ -6,10 +6,3 @@
   "+services/service_manager",
   "+services/tracing/public/cpp",
 ]
-
-specific_include_rules = {
-  "run_all_unittests.cc": [
-    "+mojo/edk/embedder",
-    "+services/catalog",
-  ]
-}
diff --git a/components/filesystem/run_all_unittests.cc b/components/filesystem/run_all_unittests.cc
deleted file mode 100644
index 3cb9069..0000000
--- a/components/filesystem/run_all_unittests.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/test_suite.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "services/catalog/catalog.h"
-
-namespace {
-
-const base::FilePath::CharType kCatalogFilename[] =
-    FILE_PATH_LITERAL("filesystem_service_unittests_catalog.json");
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::TestSuite test_suite(argc, argv);
-
-  catalog::Catalog::LoadDefaultCatalogManifest(
-      base::FilePath(kCatalogFilename));
-
-  mojo::edk::Init();
-  base::Thread ipc_thread("IPC thread");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  return base::LaunchUnitTests(
-      argc, argv,
-      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
-}
diff --git a/components/leveldb/BUILD.gn b/components/leveldb/BUILD.gn
index 5930be9..ed49e2a5 100644
--- a/components/leveldb/BUILD.gn
+++ b/components/leveldb/BUILD.gn
@@ -5,6 +5,7 @@
 import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
 import("//testing/test.gni")
 
 static_library("lib") {
@@ -60,24 +61,22 @@
   source = "manifest.json"
 }
 
-test("leveldb_service_unittests") {
+service_test("leveldb_service_unittests") {
   sources = [
     "leveldb_service_unittest.cc",
     "remote_iterator_unittest.cc",
-    "run_all_unittests.cc",
   ]
 
+  catalog = ":leveldb_service_unittests_catalog"
+
   deps = [
     "//base",
-    "//base/test:test_support",
     "//components/filesystem/public/interfaces",
     "//components/leveldb/public/cpp",
     "//components/leveldb/public/interfaces",
     "//mojo/common",
-    "//mojo/edk/system",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/system",
-    "//services/catalog:lib",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/service_manager/public/cpp:sources",
     "//third_party/leveldatabase",
@@ -85,7 +84,6 @@
 
   data_deps = [
     ":leveldb",
-    ":leveldb_service_unittests_catalog_copy",
     "//components/filesystem:filesystem",
   ]
 }
@@ -102,13 +100,3 @@
     "//components/filesystem:manifest",
   ]
 }
-
-copy("leveldb_service_unittests_catalog_copy") {
-  sources = get_target_outputs(":leveldb_service_unittests_catalog")
-  outputs = [
-    "${root_out_dir}/leveldb_service_unittests_catalog.json",
-  ]
-  deps = [
-    ":leveldb_service_unittests_catalog",
-  ]
-}
diff --git a/components/leveldb/DEPS b/components/leveldb/DEPS
index dbf83bfd..af1a7ae 100644
--- a/components/leveldb/DEPS
+++ b/components/leveldb/DEPS
@@ -7,10 +7,3 @@
   "+services/tracing/public/cpp",
   "+third_party/leveldatabase",
 ]
-
-specific_include_rules = {
-  "run_all_unittests.cc": [
-    "+mojo/edk/embedder",
-    "+services/catalog",
-  ]
-}
diff --git a/components/leveldb/run_all_unittests.cc b/components/leveldb/run_all_unittests.cc
deleted file mode 100644
index 87c2f3a..0000000
--- a/components/leveldb/run_all_unittests.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/test_suite.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "services/catalog/catalog.h"
-
-namespace {
-
-const base::FilePath::CharType kCatalogFilename[] =
-    FILE_PATH_LITERAL("leveldb_service_unittests_catalog.json");
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::TestSuite test_suite(argc, argv);
-
-  catalog::Catalog::LoadDefaultCatalogManifest(
-      base::FilePath(kCatalogFilename));
-
-  mojo::edk::Init();
-  base::Thread ipc_thread("IPC thread");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  return base::LaunchUnitTests(
-      argc, argv,
-      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
-}
diff --git a/components/test/data/web_database/version_70.sql b/components/test/data/web_database/version_70.sql
new file mode 100644
index 0000000..0d80e575
--- /dev/null
+++ b/components/test/data/web_database/version_70.sql
@@ -0,0 +1,34 @@
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO "meta" VALUES('mmap_status','-1');
+INSERT INTO "meta" VALUES('version','70');
+INSERT INTO "meta" VALUES('last_compatible_version','70');
+INSERT INTO "meta" VALUES('Builtin Keyword Version','96');
+CREATE TABLE token_service (service VARCHAR PRIMARY KEY NOT NULL,encrypted_token BLOB);
+CREATE TABLE keywords (id INTEGER PRIMARY KEY,short_name VARCHAR NOT NULL,keyword VARCHAR NOT NULL,favicon_url VARCHAR NOT NULL,url VARCHAR NOT NULL,safe_for_autoreplace INTEGER,originating_url VARCHAR,date_created INTEGER DEFAULT 0,usage_count INTEGER DEFAULT 0,input_encodings VARCHAR,suggest_url VARCHAR,prepopulate_id INTEGER DEFAULT 0,created_by_policy INTEGER DEFAULT 0,instant_url VARCHAR,last_modified INTEGER DEFAULT 0,sync_guid VARCHAR,alternate_urls VARCHAR,search_terms_replacement_key VARCHAR,image_url VARCHAR,search_url_post_params VARCHAR,suggest_url_post_params VARCHAR,instant_url_post_params VARCHAR,image_url_post_params VARCHAR,new_tab_url VARCHAR, last_visited INTEGER DEFAULT 0);
+INSERT INTO "keywords" VALUES(2,'Google','google.com','http://www.google.com/favicon.ico','{google:baseURL}search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}{google:contextualSearchVersion}ie={inputEncoding}',1,'',0,0,'UTF-8','{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&gs_ri={google:suggestRid}&xssi=t&q={searchTerms}&{google:inputType}{google:cursorPosition}{google:currentPageUrl}{google:pageClassification}{google:searchVersion}{google:sessionToken}{google:prefetchQuery}sugkey={google:suggestAPIKeyParameter}',1,0,'{google:baseURL}webhp?sourceid=chrome-instant&{google:RLZ}{google:forceInstantResults}{google:instantExtendedEnabledParameter}ie={inputEncoding}',0,'3967f1bd-7913-440d-aab5-319ae178837a','["{google:baseURL}#q={searchTerms}","{google:baseURL}search#q={searchTerms}","{google:baseURL}webhp#q={searchTerms}","{google:baseURL}s#q={searchTerms}","{google:baseURL}s?q={searchTerms}"]','espv','{google:baseURL}searchbyimage/upload','','','','encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight}','{google:baseURL}_/chrome/newtab?{google:RLZ}{google:instantExtendedEnabledParameter}ie={inputEncoding}',0);
+INSERT INTO "keywords" VALUES(3,'Bing','bing.com','https://www.bing.com/s/a/bing_p.ico','https://www.bing.com/search?q={searchTerms}&PC=U316&FORM=CHROMN',1,'',0,0,'UTF-8','https://www.bing.com/osjson.aspx?query={searchTerms}&language={language}&PC=U316',3,0,'',0,'edf5b23b-f8c6-4235-80a2-c686b9de3e37','[]','','https://www.bing.com/images/detail/search?iss=sbi&FORM=CHROMI#enterInsights','','','','imgurl={google:imageURL}','https://www.bing.com/chrome/newtab',0);
+INSERT INTO "keywords" VALUES(4,'Yahoo!','yahoo.com','https://search.yahoo.com/favicon.ico','https://search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}',1,'',0,0,'UTF-8','https://search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}',2,0,'',0,'56b59942-da06-4f53-841b-a9ef28f50ac8','[]','','','','','','','',0);
+INSERT INTO "keywords" VALUES(5,'AOL','aol.com','http://search.aol.com/favicon.ico','http://search.aol.com/aol/search?q={searchTerms}',1,'',0,0,'UTF-8','http://autocomplete.search.aol.com/autocomplete/get?output=json&it=&q={searchTerms}',35,0,'',0,'1a485f3a-b545-4265-8515-d49914865ebd','[]','','','','','','','',0);
+INSERT INTO "keywords" VALUES(6,'Ask','ask.com','http://sp.ask.com/sh/i/a16/favicon/favicon.ico','http://www.ask.com/web?q={searchTerms}',1,'',0,0,'UTF-8','http://ss.ask.com/query?q={searchTerms}&li=ff',4,0,'',0,'6725c539-3c39-4518-b58a-2a0623007920','[]','','','','','','','',0);
+CREATE TABLE autofill (name VARCHAR, value VARCHAR, value_lower VARCHAR, date_created INTEGER DEFAULT 0, date_last_used INTEGER DEFAULT 0, count INTEGER DEFAULT 1, PRIMARY KEY (name, value));
+CREATE TABLE credit_cards ( guid VARCHAR PRIMARY KEY, name_on_card VARCHAR, expiration_month INTEGER, expiration_year INTEGER, card_number_encrypted BLOB, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
+CREATE TABLE autofill_profiles ( guid VARCHAR PRIMARY KEY, company_name VARCHAR, street_address VARCHAR, dependent_locality VARCHAR, city VARCHAR, state VARCHAR, zipcode VARCHAR, sorting_code VARCHAR, country_code VARCHAR, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', language_code VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
+CREATE TABLE autofill_profile_names ( guid VARCHAR, first_name VARCHAR, middle_name VARCHAR, last_name VARCHAR, full_name VARCHAR);
+CREATE TABLE autofill_profile_emails ( guid VARCHAR, email VARCHAR);
+CREATE TABLE autofill_profile_phones ( guid VARCHAR, number VARCHAR);
+CREATE TABLE autofill_profiles_trash ( guid VARCHAR);
+CREATE TABLE masked_credit_cards (id VARCHAR,status VARCHAR,name_on_card VARCHAR,type VARCHAR,last_four VARCHAR,exp_month INTEGER DEFAULT 0,exp_year INTEGER DEFAULT 0, billing_address_id VARCHAR);
+INSERT INTO "masked_credit_cards" VALUES('card_1','status','bob','MASKED','1234',12,2050,'address_1');
+CREATE TABLE unmasked_credit_cards (id VARCHAR,card_number_encrypted VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, unmask_date INTEGER NOT NULL DEFAULT 0);
+CREATE TABLE server_card_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
+INSERT INTO "server_card_metadata" VALUES('card_1', 0, 0);
+CREATE TABLE server_addresses (id VARCHAR,company_name VARCHAR,street_address VARCHAR,address_1 VARCHAR,address_2 VARCHAR,address_3 VARCHAR,address_4 VARCHAR,postal_code VARCHAR,sorting_code VARCHAR,country_code VARCHAR,language_code VARCHAR, recipient_name VARCHAR, phone_number VARCHAR);
+CREATE TABLE server_address_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
+INSERT INTO "server_address_metadata" VALUES('address_1', 0, 0);
+CREATE TABLE autofill_sync_metadata (storage_key VARCHAR PRIMARY KEY NOT NULL,value BLOB);
+CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value BLOB);
+CREATE INDEX autofill_name ON autofill (name);
+CREATE INDEX autofill_name_value_lower ON autofill (name, value_lower);
+COMMIT;
diff --git a/components/webdata/common/BUILD.gn b/components/webdata/common/BUILD.gn
index bf67150..7de1b1c 100644
--- a/components/webdata/common/BUILD.gn
+++ b/components/webdata/common/BUILD.gn
@@ -56,6 +56,7 @@
     "//components/test/data/web_database/version_67.sql",
     "//components/test/data/web_database/version_68.sql",
     "//components/test/data/web_database/version_69.sql",
+    "//components/test/data/web_database/version_70.sql",
   ]
   outputs = [
     "{{bundle_resources_dir}}/" +
diff --git a/components/webdata/common/web_database.cc b/components/webdata/common/web_database.cc
index b7dc2818..19231760 100644
--- a/components/webdata/common/web_database.cc
+++ b/components/webdata/common/web_database.cc
@@ -13,13 +13,13 @@
 // corresponding changes must happen in the unit tests, and new migration test
 // added.  See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
 // static
-const int WebDatabase::kCurrentVersionNumber = 70;
+const int WebDatabase::kCurrentVersionNumber = 71;
 
 const int WebDatabase::kDeprecatedVersionNumber = 51;
 
 namespace {
 
-const int kCompatibleVersionNumber = 70;
+const int kCompatibleVersionNumber = 71;
 
 // Change the version number and possibly the compatibility version of
 // |meta_table_|.
diff --git a/components/webdata/common/web_database_migration_unittest.cc b/components/webdata/common/web_database_migration_unittest.cc
index 82aa0c61..3229f86e 100644
--- a/components/webdata/common/web_database_migration_unittest.cc
+++ b/components/webdata/common/web_database_migration_unittest.cc
@@ -130,7 +130,7 @@
   DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
 };
 
-const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 70;
+const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 71;
 
 void WebDatabaseMigrationTest::LoadDatabase(
     const base::FilePath::StringType& file) {
@@ -1029,6 +1029,7 @@
 }
 
 // Tests addition of masked server credit card billing address.
+// That column was moved to server_card_metadata in version 71.
 TEST_F(WebDatabaseMigrationTest, MigrateVersion66ToCurrent) {
   ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_66.sql")));
 
@@ -1060,14 +1061,11 @@
     // Check version.
     EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
 
-    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards",
+    // The column was moved to server_card_metadata in version 71.
+    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
+                                            "billing_address_id"));
+    EXPECT_TRUE(connection.DoesColumnExist("server_card_metadata",
                                            "billing_address_id"));
-
-    sql::Statement read_masked(connection.GetUniqueStatement(
-        "SELECT name_on_card, billing_address_id FROM masked_credit_cards"));
-    ASSERT_TRUE(read_masked.Step());
-    EXPECT_EQ("Alice", read_masked.ColumnString(0));
-    EXPECT_TRUE(read_masked.ColumnString(1).empty());
   }
 }
 
@@ -1166,3 +1164,81 @@
     EXPECT_TRUE(connection.DoesTableExist("autofill_model_type_state"));
   }
 }
+
+// Tests addition of billing_address_id to server_card_metadata and
+// has_converted to server_profile_metadata and tests that the
+// billing_address_id values were moved from the masked_credit_cards table to
+// the server_card_metadata table.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion70ToCurrent) {
+  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_70.sql")));
+
+  // Verify pre-conditions.
+  {
+    sql::Connection connection;
+    ASSERT_TRUE(connection.Open(GetDatabasePath()));
+    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+    sql::MetaTable meta_table;
+    ASSERT_TRUE(meta_table.Init(&connection, 70, 70));
+
+    EXPECT_FALSE(connection.DoesColumnExist("server_card_metadata",
+                                            "billing_address_id"));
+    EXPECT_FALSE(
+        connection.DoesColumnExist("server_address_metadata", "has_converted"));
+  }
+
+  DoMigration();
+
+  // Verify post-conditions.
+  {
+    sql::Connection connection;
+    ASSERT_TRUE(connection.Open(GetDatabasePath()));
+    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+    // Check version.
+    EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+    // The billing_address_id column should have moved from masked_credit_cards
+    // to server_card_metadata.
+    EXPECT_FALSE(connection.DoesColumnExist("masked_credit_cards",
+                                            "billing_address_id"));
+    EXPECT_TRUE(connection.DoesColumnExist("server_card_metadata",
+                                           "billing_address_id"));
+
+    // The has_converted column should have been added in
+    // server_address_metadata.
+    EXPECT_TRUE(
+        connection.DoesColumnExist("server_address_metadata", "has_converted"));
+
+    // Make sure that the billing_address_id was moved from the
+    // masked_credit_cards table to the server_card_metadata table. The values
+    // are added to the table in version_70.sql.
+    sql::Statement s_cards_metadata(connection.GetUniqueStatement(
+        "SELECT id, billing_address_id FROM server_card_metadata"));
+    ASSERT_TRUE(s_cards_metadata.Step());
+    EXPECT_EQ("card_1", s_cards_metadata.ColumnString(0));
+    EXPECT_EQ("address_1", s_cards_metadata.ColumnString(1));
+
+    // Make sure that the has_converted column was set to false.
+    sql::Statement s_addresses_metadata(connection.GetUniqueStatement(
+        "SELECT id, has_converted FROM server_address_metadata"));
+    ASSERT_TRUE(s_addresses_metadata.Step());
+    EXPECT_EQ("address_1", s_addresses_metadata.ColumnString(0));
+    EXPECT_FALSE(s_addresses_metadata.ColumnBool(1));
+
+    // Make sure that the values in masked_credit_cards are still present except
+    // for the billing_address_id. The values are added to the table in
+    // version_70.sql.
+    sql::Statement s_masked_cards(connection.GetUniqueStatement(
+        "SELECT id, status, name_on_card, type, last_four, exp_month, exp_year "
+        "FROM masked_credit_cards"));
+    ASSERT_TRUE(s_masked_cards.Step());
+    EXPECT_EQ("card_1", s_masked_cards.ColumnString(0));
+    EXPECT_EQ("status", s_masked_cards.ColumnString(1));
+    EXPECT_EQ("bob", s_masked_cards.ColumnString(2));
+    EXPECT_EQ("MASKED", s_masked_cards.ColumnString(3));
+    EXPECT_EQ("1234", s_masked_cards.ColumnString(4));
+    EXPECT_EQ(12, s_masked_cards.ColumnInt(5));
+    EXPECT_EQ(2050, s_masked_cards.ColumnInt(6));
+  }
+}
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 826786ab..4a764be9 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -113,7 +113,8 @@
     "//services/service_manager/public/interfaces",
     "//services/service_manager/runner/common",
     "//services/service_manager/runner/host:lib",
-    "//services/shape_detection/public/interfaces:interfaces",
+    "//services/shape_detection:lib",
+    "//services/shape_detection/public/interfaces",
     "//services/ui/gpu/interfaces",
     "//services/ui/public/cpp/gpu",
     "//skia",
@@ -1305,6 +1306,7 @@
     "service_worker/service_worker_version.h",
     "service_worker/service_worker_write_to_cache_job.cc",
     "service_worker/service_worker_write_to_cache_job.h",
+    "shapedetection/face_detection_service_dispatcher.h",
     "shared_worker/shared_worker_host.cc",
     "shared_worker/shared_worker_host.h",
     "shared_worker/shared_worker_instance.cc",
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 6cfdebc..6f3c7f8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -220,6 +220,7 @@
 #if defined(OS_MACOSX)
 #include "content/browser/bootstrap_sandbox_manager_mac.h"
 #include "content/browser/mach_broker_mac.h"
+#include "content/browser/shapedetection/face_detection_service_dispatcher.h"
 #endif
 
 #if defined(OS_POSIX)
@@ -1196,6 +1197,12 @@
   channel_->AddAssociatedInterfaceForIOThread(
       base::Bind(&IndexedDBDispatcherHost::AddBinding, indexed_db_factory_));
 
+#if defined(OS_MACOSX)
+  AddUIThreadInterface(
+      registry.get(),
+      base::Bind(&FaceDetectionServiceDispatcher::CreateMojoService));
+#endif
+
 #if defined(OS_ANDROID)
   AddUIThreadInterface(registry.get(),
                        GetGlobalJavaInterfaces()
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index ea33bd05..0cad78c 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -14,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/utf_string_conversions.h"
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/browser/service_manager/merge_dictionary.h"
 #include "content/common/service_manager/service_manager_connection_impl.h"
@@ -38,6 +39,7 @@
 #include "services/service_manager/public/interfaces/service.mojom.h"
 #include "services/service_manager/runner/common/client_util.h"
 #include "services/service_manager/service_manager.h"
+#include "services/shape_detection/public/interfaces/constants.mojom.h"
 
 namespace content {
 
@@ -311,6 +313,9 @@
   GetContentClient()
       ->browser()
       ->RegisterUnsandboxedOutOfProcessServices(&unsandboxed_services);
+  unsandboxed_services.insert(
+      std::make_pair(shape_detection::mojom::kServiceName,
+                     base::ASCIIToUTF16("Shape Detection Service")));
   for (const auto& service : unsandboxed_services) {
     ServiceManagerConnection::GetForProcess()->AddServiceRequestHandler(
         service.first,
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index 988e693..122ceda7 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -73,8 +73,11 @@
 // wrapped client.
 class DelegatingURLLoaderClient final : public mojom::URLLoaderClient {
  public:
-  explicit DelegatingURLLoaderClient(mojom::URLLoaderClientPtr client)
-      : binding_(this), client_(std::move(client)) {}
+  explicit DelegatingURLLoaderClient(mojom::URLLoaderClientPtr client,
+                                     base::OnceClosure on_response)
+      : binding_(this),
+        client_(std::move(client)),
+        on_response_(std::move(on_response)) {}
   ~DelegatingURLLoaderClient() override {
     if (!completed_) {
       // Let the service worker know that the request has been canceled.
@@ -102,6 +105,8 @@
       const ResourceResponseHead& head,
       mojom::DownloadedTempFileAssociatedPtrInfo downloaded_file) override {
     client_->OnReceiveResponse(head, std::move(downloaded_file));
+    DCHECK(on_response_);
+    std::move(on_response_).Run();
   }
   void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
                          const ResourceResponseHead& head) override {
@@ -125,6 +130,7 @@
  private:
   mojo::AssociatedBinding<mojom::URLLoaderClient> binding_;
   mojom::URLLoaderClientPtr client_;
+  base::OnceClosure on_response_;
   bool completed_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(DelegatingURLLoaderClient);
@@ -407,7 +413,8 @@
 }
 
 bool ServiceWorkerFetchDispatcher::MaybeStartNavigationPreload(
-    net::URLRequest* original_request) {
+    net::URLRequest* original_request,
+    base::OnceClosure on_response) {
   if (resource_type_ != RESOURCE_TYPE_MAIN_FRAME &&
       resource_type_ != RESOURCE_TYPE_SUB_FRAME) {
     return false;
@@ -484,7 +491,7 @@
   preload_handle_->url_loader_client_request =
       mojo::MakeRequest(&url_loader_client_ptr);
   auto url_loader_client = base::MakeUnique<DelegatingURLLoaderClient>(
-      std::move(url_loader_client_ptr));
+      std::move(url_loader_client_ptr), std::move(on_response));
   mojom::URLLoaderClientAssociatedPtrInfo url_loader_client_associated_ptr_info;
   url_loader_client->Bind(&url_loader_client_associated_ptr_info,
                           url_loader_factory.associated_group());
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h
index 3064d7d..192e17b 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.h
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -52,7 +52,9 @@
 
   // If appropriate, starts the navigation preload request and creates
   // |preload_handle_|. Returns true if it started navigation preload.
-  bool MaybeStartNavigationPreload(net::URLRequest* original_request);
+  // |on_response| is invoked in OnReceiveResponse().
+  bool MaybeStartNavigationPreload(net::URLRequest* original_request,
+                                   base::OnceClosure on_response);
 
   // Dispatches a fetch event to the |version| given in ctor, and fires
   // |fetch_callback| (also given in ctor) when finishes. It runs
diff --git a/content/browser/service_worker/service_worker_metrics.cc b/content/browser/service_worker/service_worker_metrics.cc
index 720d5f9..363d219 100644
--- a/content/browser/service_worker/service_worker_metrics.cc
+++ b/content/browser/service_worker/service_worker_metrics.cc
@@ -837,6 +837,84 @@
                               size);
 }
 
+void ServiceWorkerMetrics::RecordNavigationPreloadResponse(
+    base::TimeDelta worker_start,
+    base::TimeDelta response_start,
+    EmbeddedWorkerStatus initial_worker_status,
+    StartSituation start_situation) {
+  DCHECK_GE(worker_start.ToInternalValue(), 0);
+  DCHECK_GE(response_start.ToInternalValue(), 0);
+
+  UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.NavigationPreload.ResponseTime",
+                             response_start);
+
+  const bool nav_preload_finished_first = response_start < worker_start;
+  UMA_HISTOGRAM_BOOLEAN(
+      "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker",
+      nav_preload_finished_first);
+
+  const bool existing_process_startup =
+      (initial_worker_status == EmbeddedWorkerStatus::STOPPED &&
+       start_situation ==
+           ServiceWorkerMetrics::StartSituation::EXISTING_PROCESS);
+  if (existing_process_startup) {
+    UMA_HISTOGRAM_BOOLEAN(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker"
+        "_StartWorkerExistingProcess",
+        nav_preload_finished_first);
+  }
+
+  UMA_HISTOGRAM_MEDIUM_TIMES(
+      "ServiceWorker.NavigationPreload.ConcurrentTime",
+      nav_preload_finished_first ? response_start : worker_start);
+
+  if (nav_preload_finished_first) {
+    UMA_HISTOGRAM_MEDIUM_TIMES(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_NavPreloadFirst",
+        response_start);
+    UMA_HISTOGRAM_MEDIUM_TIMES(
+        "ServiceWorker.NavigationPreload.SWStartAfterNavPreload",
+        worker_start - response_start);
+
+    if (existing_process_startup) {
+      UMA_HISTOGRAM_MEDIUM_TIMES(
+          "ServiceWorker.NavigationPreload.ConcurrentTime_"
+          "StartWorkerExistingProcess",
+          response_start);
+      UMA_HISTOGRAM_MEDIUM_TIMES(
+          "ServiceWorker.NavigationPreload.ConcurrentTime_"
+          "NavPreloadFirst_StartWorkerExistingProcess",
+          response_start);
+      UMA_HISTOGRAM_MEDIUM_TIMES(
+          "ServiceWorker.NavigationPreload.SWStartAfterNavPreload_"
+          "StartWorkerExistingProcess",
+          worker_start - response_start);
+    }
+  } else {
+    UMA_HISTOGRAM_MEDIUM_TIMES(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_SWStartFirst",
+        worker_start);
+    UMA_HISTOGRAM_MEDIUM_TIMES(
+        "ServiceWorker.NavigationPreload.NavPreloadAfterSWStart",
+        response_start - worker_start);
+
+    if (existing_process_startup) {
+      UMA_HISTOGRAM_MEDIUM_TIMES(
+          "ServiceWorker.NavigationPreload.ConcurrentTime_"
+          "StartWorkerExistingProcess",
+          worker_start);
+      UMA_HISTOGRAM_MEDIUM_TIMES(
+          "ServiceWorker.NavigationPreload.ConcurrentTime_"
+          "SWStartFirst_StartWorkerExistingProcess",
+          worker_start);
+      UMA_HISTOGRAM_MEDIUM_TIMES(
+          "ServiceWorker.NavigationPreload.NavPreloadAfterSWStart_"
+          "StartWorkerExistingProcess",
+          response_start - worker_start);
+    }
+  }
+}
+
 void ServiceWorkerMetrics::RecordContextRequestHandlerStatus(
     ServiceWorkerContextRequestHandler::CreateJobStatus status,
     bool is_installed,
diff --git a/content/browser/service_worker/service_worker_metrics.h b/content/browser/service_worker/service_worker_metrics.h
index 4d75b4d..934e4ee 100644
--- a/content/browser/service_worker/service_worker_metrics.h
+++ b/content/browser/service_worker/service_worker_metrics.h
@@ -317,6 +317,19 @@
   // navigation preload request is to be sent.
   static void RecordNavigationPreloadRequestHeaderSize(size_t size);
 
+  // Records timings for the navigation preload response and how
+  // it compares to starting the worker.
+  // |worker_start| is the time it took to prepare an activated and running
+  // worker to receive the fetch event. |initial_worker_status| and
+  // |start_situation| describe the preparation needed.
+  // |response_start| is the time it took until the navigation preload response
+  // started.
+  CONTENT_EXPORT static void RecordNavigationPreloadResponse(
+      base::TimeDelta worker_start,
+      base::TimeDelta response_start,
+      EmbeddedWorkerStatus initial_worker_status,
+      StartSituation start_situation);
+
   // Records the result of trying to handle a request for a service worker
   // script.
   static void RecordContextRequestHandlerStatus(
diff --git a/content/browser/service_worker/service_worker_metrics_unittest.cc b/content/browser/service_worker/service_worker_metrics_unittest.cc
index 3348d6c..6858630 100644
--- a/content/browser/service_worker/service_worker_metrics_unittest.cc
+++ b/content/browser/service_worker/service_worker_metrics_unittest.cc
@@ -103,4 +103,150 @@
   }
 }
 
+TEST(ServiceWorkerMetricsTest, NavigationPreloadResponse) {
+  // The worker was already running.
+  {
+    base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(123);
+    base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(789);
+    EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::RUNNING;
+    ServiceWorkerMetrics::StartSituation start_situation =
+        ServiceWorkerMetrics::StartSituation::UNKNOWN;
+    base::HistogramTester histogram_tester;
+    ServiceWorkerMetrics::RecordNavigationPreloadResponse(
+        worker_start, response_start, initial_worker_status, start_situation);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ResponseTime", response_start, 1);
+    histogram_tester.ExpectUniqueSample(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker", false, 1);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker_"
+        "StartWorkerExistingProcess",
+        0);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime", worker_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_SWStartFirst",
+        worker_start, 1);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_SWStartFirst_"
+        "StartWorkerExistingProcess",
+        0);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.NavPreloadAfterSWStart",
+        response_start - worker_start, 1);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.NavigationPreload.NavPreloadAfterSWStart_"
+        "StartWorkerExistingProcess",
+        0);
+  }
+
+  // The worker had to start up.
+  {
+    base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(234);
+    base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(789);
+    EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPED;
+    ServiceWorkerMetrics::StartSituation start_situation =
+        ServiceWorkerMetrics::StartSituation::EXISTING_PROCESS;
+    base::HistogramTester histogram_tester;
+    ServiceWorkerMetrics::RecordNavigationPreloadResponse(
+        worker_start, response_start, initial_worker_status, start_situation);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ResponseTime", response_start, 1);
+    histogram_tester.ExpectUniqueSample(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker", false, 1);
+    histogram_tester.ExpectUniqueSample(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker_"
+        "StartWorkerExistingProcess",
+        false, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime", worker_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_SWStartFirst",
+        worker_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_SWStartFirst_"
+        "StartWorkerExistingProcess",
+        worker_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.NavPreloadAfterSWStart",
+        response_start - worker_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.NavPreloadAfterSWStart_"
+        "StartWorkerExistingProcess",
+        response_start - worker_start, 1);
+  }
+
+  // The worker had to start up. But it happened during browser startup.
+  {
+    base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(456);
+    base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(789);
+    EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPED;
+    ServiceWorkerMetrics::StartSituation start_situation =
+        ServiceWorkerMetrics::StartSituation::DURING_STARTUP;
+    base::HistogramTester histogram_tester;
+    ServiceWorkerMetrics::RecordNavigationPreloadResponse(
+        worker_start, response_start, initial_worker_status, start_situation);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ResponseTime", response_start, 1);
+    histogram_tester.ExpectUniqueSample(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker", false, 1);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker_"
+        "StartWorkerExistingProcess",
+        0);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime", worker_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_SWStartFirst",
+        worker_start, 1);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_SWStartFirst_"
+        "StartWorkerExistingProcess",
+        0);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.NavPreloadAfterSWStart",
+        response_start - worker_start, 1);
+    histogram_tester.ExpectTotalCount(
+        "ServiceWorker.NavigationPreload.NavPreloadAfterSWStart_"
+        "StartWorkerExistingProcess",
+        0);
+  }
+
+  // The worker had to start up, and navigation preload finished first.
+  {
+    base::TimeDelta worker_start = base::TimeDelta::FromMilliseconds(2345);
+    base::TimeDelta response_start = base::TimeDelta::FromMilliseconds(456);
+    EmbeddedWorkerStatus initial_worker_status = EmbeddedWorkerStatus::STOPPED;
+    ServiceWorkerMetrics::StartSituation start_situation =
+        ServiceWorkerMetrics::StartSituation::EXISTING_PROCESS;
+    base::HistogramTester histogram_tester;
+    ServiceWorkerMetrics::RecordNavigationPreloadResponse(
+        worker_start, response_start, initial_worker_status, start_situation);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ResponseTime", response_start, 1);
+    histogram_tester.ExpectUniqueSample(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker", true, 1);
+    histogram_tester.ExpectUniqueSample(
+        "ServiceWorker.NavigationPreload.FinishedBeforeStartWorker_"
+        "StartWorkerExistingProcess",
+        true, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime", response_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_NavPreloadFirst",
+        response_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.ConcurrentTime_NavPreloadFirst_"
+        "StartWorkerExistingProcess",
+        response_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.SWStartAfterNavPreload",
+        worker_start - response_start, 1);
+    histogram_tester.ExpectTimeBucketCount(
+        "ServiceWorker.NavigationPreload.SWStartAfterNavPreload_"
+        "StartWorkerExistingProcess",
+        worker_start - response_start, 1);
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_url_request_job.cc b/content/browser/service_worker/service_worker_url_request_job.cc
index f69a0ac..afa3704 100644
--- a/content/browser/service_worker/service_worker_url_request_job.cc
+++ b/content/browser/service_worker/service_worker_url_request_job.cc
@@ -555,9 +555,11 @@
   }
   if (version->should_exclude_from_uma())
     return;
+  worker_start_situation_ = version->embedded_worker()->start_situation();
   ServiceWorkerMetrics::RecordActivatedWorkerPreparationForMainFrame(
       worker_ready_time_ - request()->creation_time(), initial_worker_status_,
-      version->embedded_worker()->start_situation(), did_navigation_preload_);
+      worker_start_situation_, did_navigation_preload_);
+  MaybeReportNavigationPreloadMetrics();
 }
 
 void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
@@ -885,9 +887,31 @@
       base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent,
                  weak_factory_.GetWeakPtr())));
   worker_start_time_ = base::TimeTicks::Now();
-  did_navigation_preload_ =
-      fetch_dispatcher_->MaybeStartNavigationPreload(request());
+  did_navigation_preload_ = fetch_dispatcher_->MaybeStartNavigationPreload(
+      request(),
+      base::BindOnce(&ServiceWorkerURLRequestJob::OnNavigationPreloadResponse,
+                     weak_factory_.GetWeakPtr()));
   fetch_dispatcher_->Run();
 }
 
+void ServiceWorkerURLRequestJob::OnNavigationPreloadResponse() {
+  DCHECK(navigation_preload_response_time_.is_null());
+  navigation_preload_response_time_ = base::TimeTicks::Now();
+  MaybeReportNavigationPreloadMetrics();
+}
+
+void ServiceWorkerURLRequestJob::MaybeReportNavigationPreloadMetrics() {
+  if (worker_start_time_.is_null() || worker_ready_time_.is_null() ||
+      navigation_preload_response_time_.is_null()) {
+    return;
+  }
+  DCHECK(!reported_navigation_preload_metrics_);
+  reported_navigation_preload_metrics_ = true;
+
+  ServiceWorkerMetrics::RecordNavigationPreloadResponse(
+      worker_ready_time_ - worker_start_time_,
+      navigation_preload_response_time_ - worker_start_time_,
+      initial_worker_status_, worker_start_situation_);
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_url_request_job.h b/content/browser/service_worker/service_worker_url_request_job.h
index e80d35ee..95e1c43 100644
--- a/content/browser/service_worker/service_worker_url_request_job.h
+++ b/content/browser/service_worker/service_worker_url_request_job.h
@@ -238,16 +238,55 @@
   bool HasRequestBody();
   void RequestBodyFileSizesResolved(bool success);
 
+  // Called back from
+  // ServiceWorkerFetchEventDispatcher::MaybeStartNavigationPreload when the
+  // navigation preload response starts.
+  void OnNavigationPreloadResponse();
+
+  void MaybeReportNavigationPreloadMetrics();
+
   // Not owned.
   Delegate* delegate_;
 
   // Timing info to show on the popup in Devtools' Network tab.
   net::LoadTimingInfo load_timing_info_;
+
+  // When the worker was asked to prepare for the fetch event. Preparation may
+  // include activation and startup.
   base::TimeTicks worker_start_time_;
+
+  // When the worker confirmed it's ready for the fetch event. If it was already
+  // activated and running when asked to prepare, this should be nearly the same
+  // as |worker_start_time_|).
   base::TimeTicks worker_ready_time_;
+
+  // When the response started.
   base::Time response_time_;
 
+  // When the navigation preload response started.
+  base::TimeTicks navigation_preload_response_time_;
+
+  // True if the worker was already in ACTIVATED status when asked to prepare
+  // for the fetch event.
+  bool worker_already_activated_ = false;
+
+  // The status the worker was in when asked to prepare for the fetch event.
+  EmbeddedWorkerStatus initial_worker_status_ = EmbeddedWorkerStatus::STOPPED;
+
+  // If worker startup occurred during preparation, the situation that startup
+  // occurred in.
+  ServiceWorkerMetrics::StartSituation worker_start_situation_ =
+      ServiceWorkerMetrics::StartSituation::UNKNOWN;
+
+  // True if navigation preload was enabled.
+  bool did_navigation_preload_ = false;
+
+  // True if navigation preload metrics were reported.
+  bool reported_navigation_preload_metrics_ = false;
+
   ResponseType response_type_;
+
+  // True if URLRequestJob::Start() has been called.
   bool is_started_;
 
   net::HttpByteRange byte_range_;
@@ -291,10 +330,6 @@
 
   std::unique_ptr<FileSizeResolver> file_size_resolver_;
 
-  bool worker_already_activated_ = false;
-  EmbeddedWorkerStatus initial_worker_status_ = EmbeddedWorkerStatus::STOPPED;
-  bool did_navigation_preload_ = false;
-
   base::WeakPtrFactory<ServiceWorkerURLRequestJob> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerURLRequestJob);
diff --git a/content/browser/shapedetection/face_detection_service_dispatcher.h b/content/browser/shapedetection/face_detection_service_dispatcher.h
new file mode 100644
index 0000000..8eb4824
--- /dev/null
+++ b/content/browser/shapedetection/face_detection_service_dispatcher.h
@@ -0,0 +1,33 @@
+// 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 CONTENT_BROWSER_SHAPEDETECTION_FACE_DETECTION_SERVICE_DISPATCHER_H_
+#define CONTENT_BROWSER_SHAPEDETECTION_FACE_DETECTION_SERVICE_DISPATCHER_H_
+
+#include "content/common/content_export.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/shape_detection/public/interfaces/constants.mojom.h"
+#include "services/shape_detection/public/interfaces/facedetection_provider.mojom.h"
+
+namespace content {
+
+// Because the renderer cannot launch an out-of-process service on its own, we
+// use |FaceDetectionServiceDispatcher| to forward requests to Service Manager,
+// which then starts Face Detection Service in a utility process.
+namespace FaceDetectionServiceDispatcher {
+
+static void CreateMojoService(
+    shape_detection::mojom::FaceDetectionProviderRequest request) {
+  service_manager::Connector* connector =
+      ServiceManagerConnection::GetForProcess()->GetConnector();
+  connector->BindInterface(shape_detection::mojom::kServiceName,
+                           std::move(request));
+}
+
+}  // namespace FaceDetectionServiceDispatcher
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SHAPEDETECTION_FACE_DETECTION_SERVICE_DISPATCHER_H_
diff --git a/content/browser/shapedetection/shapedetection_browsertest.cc b/content/browser/shapedetection/shapedetection_browsertest.cc
index fa24aa47..9d9c1e2a 100644
--- a/content/browser/shapedetection/shapedetection_browsertest.cc
+++ b/content/browser/shapedetection/shapedetection_browsertest.cc
@@ -11,8 +11,8 @@
 
 namespace content {
 
-// TODO(xianglu): Enable other platforms with support. https://crbug.com/646083
-#if defined(OS_ANDROID)
+// TODO(xianglu): Enable other platforms support. https://crbug.com/646083
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 #define MAYBE_ShapeDetectionBrowserTest ShapeDetectionBrowserTest
 #else
 #define MAYBE_ShapeDetectionBrowserTest DISABLED_ShapeDetectionBrowserTest
@@ -25,11 +25,11 @@
 }  // namespace
 
 // This class contains content_browsertests for Shape Detection API, which
-// allows for generating bounding boxes for faces on still images..
+// allows for generating bounding boxes in still images.
 class MAYBE_ShapeDetectionBrowserTest : public ContentBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    // Specific flag to enable ShapeDetection and DOMRect API.
+    // Flag to enable ShapeDetection and DOMRect API.
     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
         switches::kEnableBlinkFeatures, "ShapeDetection, GeometryInterfaces");
   }
@@ -65,7 +65,7 @@
       const std::vector<float> expected_result = expected_results[face_id];
       const std::vector<float> result = results[face_id];
       for (size_t i = 0; i < 4; ++i)
-        EXPECT_NEAR(expected_result[i], result[i], 0.1) << "At index " << i;
+        EXPECT_NEAR(expected_result[i], result[i], 2) << "At index " << i;
     }
   }
 };
@@ -80,9 +80,16 @@
 IN_PROC_BROWSER_TEST_F(MAYBE_ShapeDetectionBrowserTest,
                        DetectFacesOnImageWithOneFace) {
   const std::string image_path = "/single_face.jpg";
+  std::vector<std::vector<float>> expected_results;
+#if defined(OS_ANDROID)
   const std::vector<float> expected_result = {68.640625, 102.96875, 171.5625,
                                               171.5625};
-  const std::vector<std::vector<float>> expected_results = {expected_result};
+  expected_results.push_back(expected_result);
+#elif defined(OS_MACOSX)
+  const std::vector<float> expected_result = {0, 93, 290, 290};
+  expected_results.push_back(expected_result);
+#endif
+
   RunDetectFacesOnImageUrl(image_path, expected_results);
 }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
index 330863f770..f7dd561 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnection.java
@@ -119,10 +119,6 @@
             final boolean replyToRequest) {
         ImeUtils.checkOnUiThread();
 
-        // crbug.com/663880: Non-breaking spaces can cause the IME to get confused. Replace with
-        // normal spaces.
-        text = text.replace('\u00A0', ' ');
-
         mCachedTextInputState = new TextInputState(text, new Range(selectionStart, selectionEnd),
                 new Range(compositionStart, compositionEnd), singleLine, replyToRequest);
         if (DEBUG_LOGS) Log.w(TAG, "updateState: %s", mCachedTextInputState);
diff --git a/content/public/app/BUILD.gn b/content/public/app/BUILD.gn
index 9a0f601..98d7362d 100644
--- a/content/public/app/BUILD.gn
+++ b/content/public/app/BUILD.gn
@@ -181,6 +181,7 @@
     "//media/mojo/services:media_manifest",
     "//services/device:manifest",
     "//services/file:manifest",
+    "//services/shape_detection:manifest",
   ]
 }
 
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index 4378804b..a13caab8 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -57,7 +57,11 @@
           "service_manager:user_id"
         ],
         "file": [ "file:filesystem", "file:leveldb" ],
-        "media": [ "media:media" ]
+        "media": [ "media:media" ],
+        "shape_detection": [
+          "barcode_detection",
+          "face_detection"
+        ]
       }
     },
     "navigation:frame": {
diff --git a/content/utility/BUILD.gn b/content/utility/BUILD.gn
index 421de76..ae88f47 100644
--- a/content/utility/BUILD.gn
+++ b/content/utility/BUILD.gn
@@ -39,6 +39,8 @@
     "//services/service_manager",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/interfaces",
+    "//services/shape_detection:lib",
+    "//services/shape_detection/public/interfaces",
     "//third_party/WebKit/public:blink_headers",
     "//third_party/WebKit/public:mojo_bindings",
     "//url",
diff --git a/content/utility/DEPS b/content/utility/DEPS
index 1069d6a..6569bcd 100644
--- a/content/utility/DEPS
+++ b/content/utility/DEPS
@@ -4,5 +4,6 @@
   "+content/public/utility",
   "+services/service_manager",
   "+services/service_manager",
+  "+services/shape_detection",
   "+sandbox/win/src",
 ]
diff --git a/content/utility/utility_service_factory.cc b/content/utility/utility_service_factory.cc
index 68dff9f9..e70647c 100644
--- a/content/utility/utility_service_factory.cc
+++ b/content/utility/utility_service_factory.cc
@@ -9,6 +9,8 @@
 #include "content/public/utility/content_utility_client.h"
 #include "content/public/utility/utility_thread.h"
 #include "content/utility/utility_thread_impl.h"
+#include "services/shape_detection/public/interfaces/constants.mojom.h"
+#include "services/shape_detection/shape_detection_service.h"
 
 #if defined(ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS)
 #include "media/mojo/services/media_service_factory.h"  // nogncheck
@@ -28,6 +30,11 @@
   info.factory = base::Bind(&media::CreateMediaService);
   services->insert(std::make_pair("media", info));
 #endif
+  ServiceInfo shape_detection_info;
+  shape_detection_info.factory =
+      base::Bind(&shape_detection::ShapeDetectionService::Create);
+  services->insert(std::make_pair(shape_detection::mojom::kServiceName,
+                                  shape_detection_info));
 }
 
 void UtilityServiceFactory::OnServiceQuit() {
diff --git a/device/bluetooth/device.cc b/device/bluetooth/device.cc
index 0e018a2e..61eb1db 100644
--- a/device/bluetooth/device.cc
+++ b/device/bluetooth/device.cc
@@ -123,6 +123,42 @@
   callback.Run(std::move(characteristics));
 }
 
+void Device::GetDescriptors(const std::string& service_id,
+                            const std::string& characteristic_id,
+                            const GetDescriptorsCallback& callback) {
+  device::BluetoothDevice* device = adapter_->GetDevice(GetAddress());
+  if (!device) {
+    callback.Run(base::nullopt);
+    return;
+  }
+
+  device::BluetoothRemoteGattService* service =
+      device->GetGattService(service_id);
+  if (!service) {
+    callback.Run(base::nullopt);
+    return;
+  }
+
+  device::BluetoothRemoteGattCharacteristic* characteristic =
+      service->GetCharacteristic(characteristic_id);
+  if (!characteristic) {
+    callback.Run(base::nullopt);
+    return;
+  }
+
+  std::vector<mojom::DescriptorInfoPtr> descriptors;
+
+  for (const auto* descriptor : characteristic->GetDescriptors()) {
+    mojom::DescriptorInfoPtr descriptor_info = mojom::DescriptorInfo::New();
+
+    descriptor_info->id = descriptor->GetIdentifier();
+    descriptor_info->uuid = descriptor->GetUUID();
+    descriptors.push_back(std::move(descriptor_info));
+  }
+
+  callback.Run(std::move(descriptors));
+}
+
 Device::Device(scoped_refptr<device::BluetoothAdapter> adapter,
                std::unique_ptr<device::BluetoothGattConnection> connection)
     : adapter_(std::move(adapter)), connection_(std::move(connection)) {
diff --git a/device/bluetooth/device.h b/device/bluetooth/device.h
index 6a72f8ca..24bb6fd2 100644
--- a/device/bluetooth/device.h
+++ b/device/bluetooth/device.h
@@ -15,6 +15,7 @@
 #include "device/bluetooth/bluetooth_device.h"
 #include "device/bluetooth/bluetooth_gatt_connection.h"
 #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h"
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "device/bluetooth/public/interfaces/device.mojom.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -53,6 +54,9 @@
   void GetServices(const GetServicesCallback& callback) override;
   void GetCharacteristics(const std::string& service_id,
                           const GetCharacteristicsCallback& callback) override;
+  void GetDescriptors(const std::string& service_id,
+                      const std::string& characteristic_id,
+                      const GetDescriptorsCallback& callback) override;
 
  private:
   Device(scoped_refptr<device::BluetoothAdapter> adapter,
diff --git a/device/bluetooth/public/interfaces/device.mojom b/device/bluetooth/public/interfaces/device.mojom
index 5448f342..2e3773a 100644
--- a/device/bluetooth/public/interfaces/device.mojom
+++ b/device/bluetooth/public/interfaces/device.mojom
@@ -58,6 +58,11 @@
   uint32 properties;
 };
 
+struct DescriptorInfo {
+  string id;
+  UUID uuid;
+};
+
 interface Device {
   // Disconnects and deletes the Device.
   Disconnect();
@@ -75,4 +80,12 @@
   // means that no characteristics were found.
   GetCharacteristics(string service_id) =>
       (array<CharacteristicInfo>? characteristics);
+
+  // Gets the GATT Descriptors of the GATT Characteristic with matching
+  // |characteristic_id| in the GATT Service with matching |service_id|.
+  // If |descriptors| is null, an error occured while attempting to retrieve
+  // the array of descriptors. If |descriptors| is empty, this simply
+  // means that no descriptors were found.
+  GetDescriptors(string service_id, string characteristic_id) =>
+      (array<DescriptorInfo>? descriptors);
 };
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
index 5dd2c71d..dc8981e9 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
+++ b/ios/chrome/browser/metrics/tab_usage_recorder_egtest.mm
@@ -515,7 +515,8 @@
 
 // Verify correct recording of metrics when the reloading of an evicted tab
 // fails.
-- (void)testEvictedTabReloadFailure {
+// TODO(crbug.com/684987): Re-enable this test.
+- (void)DISABLED_testEvictedTabReloadFailure {
   web::test::SetUpFileBasedHttpServer();
   chrome_test_util::HistogramTester histogramTester;
   FailureBlock failureBlock = ^(NSString* error) {
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index c8bc6df..e86dd04a 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -1636,26 +1636,6 @@
   return tab.webController;
 }
 
-- (void)webController:(CRWWebController*)webController
-    onFormResubmissionForRequest:(NSURLRequest*)request
-                   continueBlock:(ProceduralBlock)continueBlock
-                     cancelBlock:(ProceduralBlock)cancelBlock {
-  // Display the action sheet with the arrow pointing at the top center of the
-  // web contents.
-  CGPoint dialogLocation =
-      CGPointMake(CGRectGetMidX(webController.view.frame),
-                  CGRectGetMinY(webController.view.frame) +
-                      [self.tabHeadersDelegate headerHeightForTab:self]);
-  auto helper = FormResubmissionTabHelper::FromWebState(webController.webState);
-  helper->PresentFormResubmissionDialog(dialogLocation,
-                                        base::BindBlock(^(bool shouldContinue) {
-                                          if (shouldContinue)
-                                            continueBlock();
-                                          else
-                                            cancelBlock();
-                                        }));
-}
-
 // The web page wants to close its own window.
 - (void)webPageOrderedClose {
   // Only allow a web page to close itself if it was opened by DOM, or if there
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn
index 686ad438..8cdae1d 100644
--- a/ios/chrome/browser/ui/BUILD.gn
+++ b/ios/chrome/browser/ui/BUILD.gn
@@ -300,6 +300,7 @@
     "//ios/chrome/browser/ui/tools_menu",
     "//ios/chrome/browser/ui/voice",
     "//ios/chrome/browser/upgrade",
+    "//ios/chrome/browser/web",
     "//ios/chrome/browser/web:web_internal",
     "//ios/chrome/common",
     "//ios/net",
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_controller_egtest.mm b/ios/chrome/browser/ui/activity_services/activity_service_controller_egtest.mm
index d7df3f9..9b599f2 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_controller_egtest.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_controller_egtest.mm
@@ -55,7 +55,9 @@
 
 // Test that when trying to print a page redirected to an unprintable page, a
 // snackbar explaining that the page cannot be printed is displayed.
-- (void)testActivityServiceControllerPrintAfterRedirectionToUnprintablePage {
+// TODO(crbug.com/684987): Re-enable this test.
+- (void)
+    DISABLED_testActivityServiceControllerPrintAfterRedirectionToUnprintablePage {
   std::map<GURL, std::string> responses;
   const GURL regularPageURL = web::test::HttpServer::MakeUrl("http://choux");
   responses[regularPageURL] = "fleur";
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 3953c693b..19d7cc9 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -147,6 +147,7 @@
 #import "ios/chrome/browser/ui/voice/text_to_speech_player.h"
 #include "ios/chrome/browser/upgrade/upgrade_center.h"
 #import "ios/chrome/browser/web/error_page_content.h"
+#import "ios/chrome/browser/web/form_resubmission_tab_helper.h"
 #import "ios/chrome/browser/web/passkit_dialog_provider.h"
 #import "ios/chrome/browser/xcallback_parameters.h"
 #import "ios/chrome/common/material_timing.h"
@@ -2570,6 +2571,22 @@
   return YES;
 }
 
+- (void)webState:(web::WebState*)webState
+    runRepostFormDialogWithCompletionHandler:(void (^)(BOOL))handler {
+  // Display the action sheet with the arrow pointing at the top center of the
+  // web contents.
+  UIView* view = webState->GetView();
+  CGPoint dialogLocation =
+      CGPointMake(CGRectGetMidX(view.frame),
+                  CGRectGetMinY(view.frame) +
+                      [self headerHeightForTab:[self tabForWebState:webState]]);
+  auto helper = FormResubmissionTabHelper::FromWebState(webState);
+  helper->PresentFormResubmissionDialog(dialogLocation,
+                                        base::BindBlock(^(bool shouldContinue) {
+                                          handler(shouldContinue);
+                                        }));
+}
+
 - (web::JavaScriptDialogPresenter*)javaScriptDialogPresenterForWebState:
     (web::WebState*)webState {
   return _javaScriptDialogPresenter.get();
diff --git a/ios/chrome/browser/ui/error_page_egtest.mm b/ios/chrome/browser/ui/error_page_egtest.mm
index 5b4be88c..8864a80 100644
--- a/ios/chrome/browser/ui/error_page_egtest.mm
+++ b/ios/chrome/browser/ui/error_page_egtest.mm
@@ -70,7 +70,8 @@
 #pragma mark - tests
 
 // Tests whether the error page is displayed for a bad URL.
-- (void)testErrorPage {
+// TODO(crbug.com/684987): Re-enable this test.
+- (void)DISABLED_testErrorPage {
   std::unique_ptr<web::DataResponseProvider> provider(
       new ErrorPageResponseProvider());
   web::test::SetUpHttpServer(std::move(provider));
@@ -81,7 +82,8 @@
 }
 
 // Tests whether the error page is displayed if it is behind a redirect.
-- (void)testErrorPageRedirect {
+// TODO(crbug.com/684987): Re-enable this test.
+- (void)DISABLED_testErrorPageRedirect {
   std::unique_ptr<web::DataResponseProvider> provider(
       new ErrorPageResponseProvider());
   web::test::SetUpHttpServer(std::move(provider));
diff --git a/ios/web/navigation/history_state_operations_inttest.mm b/ios/web/navigation/history_state_operations_inttest.mm
index afd2c86dc..a305cf3 100644
--- a/ios/web/navigation/history_state_operations_inttest.mm
+++ b/ios/web/navigation/history_state_operations_inttest.mm
@@ -339,3 +339,34 @@
     return GetJavaScriptState() == new_state;
   });
 }
+
+// Tests that calling window.history.pushState() creates a new NavigationItem
+// and prunes trailing items.
+TEST_F(HistoryStateOperationsTest, PushState) {
+  // Navigate to about:blank then navigate back to the test page.  The created
+  // NavigationItem can be used later to verify that the state is replaced
+  // rather than pushed.
+  GURL about_blank("about:blank");
+  LoadUrl(about_blank);
+  web::NavigationItem* about_blank_item = GetLastCommittedItem();
+  ExecuteBlockAndWaitForLoad(state_operations_url(), ^{
+    navigation_manager()->GoBack();
+  });
+  ASSERT_EQ(state_operations_url(), GetLastCommittedItem()->GetURL());
+  web::NavigationItem* non_pushed_item = GetLastCommittedItem();
+  // Set up the state parameters and tap the replace state button.
+  std::string empty_state;
+  std::string empty_title;
+  GURL new_url = state_operations_url().Resolve("path");
+  SetStateParams(empty_state, empty_title, new_url);
+  ASSERT_TRUE(web::test::TapWebViewElementWithId(web_state(), kPushStateId));
+  // Verify that the url with the path is pushed.
+  base::test::ios::WaitUntilCondition(^bool {
+    return GetLastCommittedItem()->GetURL() == new_url;
+  });
+  // Verify that a new NavigationItem was created and that the forward item was
+  // pruned.
+  EXPECT_EQ(GetIndexOfNavigationItem(non_pushed_item) + 1,
+            GetIndexOfNavigationItem(GetLastCommittedItem()));
+  EXPECT_EQ(NSNotFound, GetIndexOfNavigationItem(about_blank_item));
+}
diff --git a/ios/web/public/test/fakes/test_web_state_delegate.h b/ios/web/public/test/fakes/test_web_state_delegate.h
index 30e941df..02b112b2 100644
--- a/ios/web/public/test/fakes/test_web_state_delegate.h
+++ b/ios/web/public/test/fakes/test_web_state_delegate.h
@@ -15,6 +15,15 @@
 
 namespace web {
 
+// Encapsulates parameters passed to ShowRepostFormWarningDialog.
+struct TestRepostFormRequest {
+  TestRepostFormRequest();
+  TestRepostFormRequest(const TestRepostFormRequest&);
+  ~TestRepostFormRequest();
+  WebState* web_state = nullptr;
+  base::Callback<void(bool)> callback;
+};
+
 // Encapsulates parameters passed to OnAuthRequired.
 struct TestAuthenticationRequest {
   TestAuthenticationRequest();
@@ -37,7 +46,9 @@
   void LoadProgressChanged(WebState* source, double progress) override;
   bool HandleContextMenu(WebState* source,
                          const ContextMenuParams& params) override;
-
+  void ShowRepostFormWarningDialog(
+      WebState* source,
+      const base::Callback<void(bool)>& callback) override;
   TestJavaScriptDialogPresenter* GetTestJavaScriptDialogPresenter();
   void OnAuthRequired(WebState* source,
                       NSURLProtectionSpace* protection_space,
@@ -54,6 +65,12 @@
     return handle_context_menu_called_;
   }
 
+  // Returns the last Repost Form request passed to
+  // |ShowRepostFormWarningDialog|.
+  TestRepostFormRequest* last_repost_form_request() const {
+    return last_repost_form_request_.get();
+  }
+
   // True if the WebStateDelegate GetJavaScriptDialogPresenter method has been
   // called.
   bool get_java_script_dialog_presenter_called() const {
@@ -73,6 +90,7 @@
  private:
   bool load_progress_changed_called_ = false;
   bool handle_context_menu_called_ = false;
+  std::unique_ptr<TestRepostFormRequest> last_repost_form_request_;
   bool get_java_script_dialog_presenter_called_ = false;
   TestJavaScriptDialogPresenter java_script_dialog_presenter_;
   std::unique_ptr<TestAuthenticationRequest> last_authentication_request_;
diff --git a/ios/web/public/test/fakes/test_web_state_delegate.mm b/ios/web/public/test/fakes/test_web_state_delegate.mm
index e309f178..640f372 100644
--- a/ios/web/public/test/fakes/test_web_state_delegate.mm
+++ b/ios/web/public/test/fakes/test_web_state_delegate.mm
@@ -8,6 +8,13 @@
 
 namespace web {
 
+TestRepostFormRequest::TestRepostFormRequest() {}
+
+TestRepostFormRequest::~TestRepostFormRequest() = default;
+
+TestRepostFormRequest::TestRepostFormRequest(const TestRepostFormRequest&) =
+    default;
+
 TestAuthenticationRequest::TestAuthenticationRequest() {}
 
 TestAuthenticationRequest::~TestAuthenticationRequest() = default;
@@ -35,6 +42,14 @@
   return NO;
 }
 
+void TestWebStateDelegate::ShowRepostFormWarningDialog(
+    WebState* source,
+    const base::Callback<void(bool)>& callback) {
+  last_repost_form_request_ = base::MakeUnique<TestRepostFormRequest>();
+  last_repost_form_request_->web_state = source;
+  last_repost_form_request_->callback = callback;
+}
+
 TestJavaScriptDialogPresenter*
 TestWebStateDelegate::GetTestJavaScriptDialogPresenter() {
   return &java_script_dialog_presenter_;
diff --git a/ios/web/public/web_state/ui/crw_web_delegate.h b/ios/web/public/web_state/ui/crw_web_delegate.h
index b8dd184..d64f8044 100644
--- a/ios/web/public/web_state/ui/crw_web_delegate.h
+++ b/ios/web/public/web_state/ui/crw_web_delegate.h
@@ -81,16 +81,6 @@
 // Called when a placeholder image should be displayed instead of the WebView.
 - (void)webController:(CRWWebController*)webController
     retrievePlaceholderOverlayImage:(void (^)(UIImage*))block;
-// Consults the delegate whether a form should be resubmitted for a request.
-// Occurs when a POST request is reached when navigating through history.
-// Call |continueBlock| if a form should be resubmitted.
-// Call |cancelBlock| if a form should not be resubmitted.
-// Delegates must call either of these (just once) before the load will
-// continue.
-- (void)webController:(CRWWebController*)webController
-    onFormResubmissionForRequest:(NSURLRequest*)request
-                   continueBlock:(ProceduralBlock)continueBlock
-                     cancelBlock:(ProceduralBlock)cancelBlock;
 
 // ---------------------------------------------------------------------
 // TODO(rohitrao): Eliminate as many of the following delegate methods as
diff --git a/ios/web/public/web_state/web_state_delegate.h b/ios/web/public/web_state/web_state_delegate.h
index d5f1590..9aefa08 100644
--- a/ios/web/public/web_state/web_state_delegate.h
+++ b/ios/web/public/web_state/web_state_delegate.h
@@ -39,6 +39,13 @@
   virtual bool HandleContextMenu(WebState* source,
                                  const ContextMenuParams& params);
 
+  // Requests the repost form confirmation dialog. Clients must call |callback|
+  // with true to allow repost and with false to cancel the repost. If this
+  // method is not implemented then WebState will repost the form.
+  virtual void ShowRepostFormWarningDialog(
+      WebState* source,
+      const base::Callback<void(bool)>& callback);
+
   // Returns a pointer to a service to manage dialogs. May return nullptr in
   // which case dialogs aren't shown.
   // TODO(crbug.com/622084): Find better place for this method.
diff --git a/ios/web/public/web_state/web_state_delegate_bridge.h b/ios/web/public/web_state/web_state_delegate_bridge.h
index 211bebd4..155cef7d 100644
--- a/ios/web/public/web_state/web_state_delegate_bridge.h
+++ b/ios/web/public/web_state/web_state_delegate_bridge.h
@@ -30,6 +30,12 @@
 - (BOOL)webState:(web::WebState*)webState
     handleContextMenu:(const web::ContextMenuParams&)params;
 
+// Requests the repost form confirmation dialog. Clients must call |handler|
+// with YES to allow repost and with NO to cancel the repost. If this method is
+// not implemented then WebState will repost the form.
+- (void)webState:(web::WebState*)webState
+    runRepostFormDialogWithCompletionHandler:(void (^)(BOOL))handler;
+
 // Returns a pointer to a service to manage dialogs. May return null in which
 // case dialogs aren't shown.
 - (web::JavaScriptDialogPresenter*)javaScriptDialogPresenterForWebState:
@@ -60,6 +66,9 @@
   void LoadProgressChanged(WebState* source, double progress) override;
   bool HandleContextMenu(WebState* source,
                          const ContextMenuParams& params) override;
+  void ShowRepostFormWarningDialog(
+      WebState* source,
+      const base::Callback<void(bool)>& callback) override;
   JavaScriptDialogPresenter* GetJavaScriptDialogPresenter(
       WebState* source) override;
   void OnAuthRequired(WebState* source,
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 99d5921..429bc8a 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -5347,10 +5347,13 @@
   // If the request is form submission or resubmission, then prompt the
   // user before proceeding.
   DCHECK(isFormPOSTResubmission);
-  [self.delegate webController:self
-      onFormResubmissionForRequest:nil
-                     continueBlock:webViewNavigationBlock
-                       cancelBlock:defaultNavigationBlock];
+  _webStateImpl->ShowRepostFormWarningDialog(
+      base::BindBlock(^(bool shouldContinue) {
+        if (shouldContinue)
+          webViewNavigationBlock();
+        else
+          defaultNavigationBlock();
+      }));
 }
 
 #pragma mark -
diff --git a/ios/web/web_state/web_state_delegate.mm b/ios/web/web_state/web_state_delegate.mm
index 7aeba30..879e6f7 100644
--- a/ios/web/web_state/web_state_delegate.mm
+++ b/ios/web/web_state/web_state_delegate.mm
@@ -30,6 +30,12 @@
   return false;
 }
 
+void WebStateDelegate::ShowRepostFormWarningDialog(
+    WebState*,
+    const base::Callback<void(bool)>& callback) {
+  callback.Run(true);
+}
+
 JavaScriptDialogPresenter* WebStateDelegate::GetJavaScriptDialogPresenter(
     WebState*) {
   return nullptr;
diff --git a/ios/web/web_state/web_state_delegate_bridge.mm b/ios/web/web_state/web_state_delegate_bridge.mm
index 695410a..1b80609 100644
--- a/ios/web/web_state/web_state_delegate_bridge.mm
+++ b/ios/web/web_state/web_state_delegate_bridge.mm
@@ -37,6 +37,21 @@
   return NO;
 }
 
+void WebStateDelegateBridge::ShowRepostFormWarningDialog(
+    WebState* source,
+    const base::Callback<void(bool)>& callback) {
+  base::Callback<void(bool)> local_callback(callback);
+  SEL selector = @selector(webState:runRepostFormDialogWithCompletionHandler:);
+  if ([delegate_ respondsToSelector:selector]) {
+    [delegate_ webState:source
+        runRepostFormDialogWithCompletionHandler:^(BOOL should_continue) {
+          local_callback.Run(should_continue);
+        }];
+  } else {
+    local_callback.Run(true);
+  }
+}
+
 JavaScriptDialogPresenter* WebStateDelegateBridge::GetJavaScriptDialogPresenter(
     WebState* source) {
   SEL selector = @selector(javaScriptDialogPresenterForWebState:);
diff --git a/ios/web/web_state/web_state_delegate_bridge_unittest.mm b/ios/web/web_state/web_state_delegate_bridge_unittest.mm
index 422b0ec..1429a63b 100644
--- a/ios/web/web_state/web_state_delegate_bridge_unittest.mm
+++ b/ios/web/web_state/web_state_delegate_bridge_unittest.mm
@@ -8,6 +8,7 @@
 
 #include <memory>
 
+#include "base/mac/bind_objc_block.h"
 #import "base/mac/scoped_nsobject.h"
 #include "base/strings/utf_string_conversions.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
@@ -17,6 +18,13 @@
 #import "third_party/ocmock/gtest_support.h"
 #include "ui/base/page_transition_types.h"
 
+// Class which conforms to CRWWebStateDelegate protocol, but does not implement
+// any optional methods.
+@interface TestEmptyWebStateDelegate : NSObject<CRWWebStateDelegate>
+@end
+@implementation TestEmptyWebStateDelegate
+@end
+
 namespace web {
 
 // Test fixture to test WebStateDelegateBridge class.
@@ -29,8 +37,11 @@
         [OCMockObject niceMockForProtocol:@protocol(CRWWebStateDelegate)];
     delegate_.reset([[CRWWebStateDelegateStub alloc]
         initWithRepresentedObject:originalMockDelegate]);
+    empty_delegate_.reset([[TestEmptyWebStateDelegate alloc] init]);
 
     bridge_.reset(new WebStateDelegateBridge(delegate_.get()));
+    empty_delegate_bridge_.reset(
+        new WebStateDelegateBridge(empty_delegate_.get()));
   }
 
   void TearDown() override {
@@ -39,7 +50,9 @@
   }
 
   base::scoped_nsprotocol<id> delegate_;
+  base::scoped_nsprotocol<id> empty_delegate_;
   std::unique_ptr<WebStateDelegateBridge> bridge_;
+  std::unique_ptr<WebStateDelegateBridge> empty_delegate_bridge_;
   web::TestWebState test_web_state_;
 };
 
@@ -97,6 +110,28 @@
   EXPECT_EQ(context_menu_params.location.y, result_params->location.y);
 }
 
+// Tests |ShowRepostFormWarningDialog| forwarding.
+TEST_F(WebStateDelegateBridgeTest, ShowRepostFormWarningDialog) {
+  EXPECT_FALSE([delegate_ repostFormWarningRequested]);
+  EXPECT_FALSE([delegate_ webState]);
+  base::Callback<void(bool)> callback;
+  bridge_->ShowRepostFormWarningDialog(&test_web_state_, callback);
+  EXPECT_TRUE([delegate_ repostFormWarningRequested]);
+  EXPECT_EQ(&test_web_state_, [delegate_ webState]);
+}
+
+// Tests |ShowRepostFormWarningDialog| forwarding to delegate which does not
+// implement |webState:runRepostFormDialogWithCompletionHandler:| method.
+TEST_F(WebStateDelegateBridgeTest, ShowRepostFormWarningWithNoDelegateMethod) {
+  __block bool callback_called = false;
+  empty_delegate_bridge_->ShowRepostFormWarningDialog(
+      nullptr, base::BindBlock(^(bool should_repost) {
+        EXPECT_TRUE(should_repost);
+        callback_called = true;
+      }));
+  EXPECT_TRUE(callback_called);
+}
+
 // Tests |GetJavaScriptDialogPresenter| forwarding.
 TEST_F(WebStateDelegateBridgeTest, GetJavaScriptDialogPresenter) {
   EXPECT_FALSE([delegate_ javaScriptDialogPresenterRequested]);
diff --git a/ios/web/web_state/web_state_delegate_stub.h b/ios/web/web_state/web_state_delegate_stub.h
index 282fc8b..dac17d55 100644
--- a/ios/web/web_state/web_state_delegate_stub.h
+++ b/ios/web/web_state/web_state_delegate_stub.h
@@ -21,9 +21,13 @@
 // ContextMenuParams reveived in |webState:handleContextMenu:| call.
 // nullptr if that delegate method was not called.
 @property(nonatomic, readonly) web::ContextMenuParams* contextMenuParams;
+// Whether |webState:runRepostFormDialogWithCompletionHandler:| has been called
+// or not.
+@property(nonatomic, readonly) BOOL repostFormWarningRequested;
 // Whether |javaScriptDialogPresenterForWebState:| has been called or not.
 @property(nonatomic, readonly) BOOL javaScriptDialogPresenterRequested;
-// Whether |authenticationRequested| has been called or not.
+// Whether |webState:didRequestHTTPAuthForProtectionSpace:...| has been called
+// or not.
 @property(nonatomic, readonly) BOOL authenticationRequested;
 
 @end
diff --git a/ios/web/web_state/web_state_delegate_stub.mm b/ios/web/web_state/web_state_delegate_stub.mm
index b86a9eba..05e53fb 100644
--- a/ios/web/web_state/web_state_delegate_stub.mm
+++ b/ios/web/web_state/web_state_delegate_stub.mm
@@ -18,6 +18,7 @@
 
 @synthesize webState = _webState;
 @synthesize changedProgress = _changedProgress;
+@synthesize repostFormWarningRequested = _repostFormWarningRequested;
 @synthesize authenticationRequested = _authenticationRequested;
 
 - (web::WebState*)webState:(web::WebState*)webState
@@ -39,6 +40,12 @@
   return YES;
 }
 
+- (void)webState:(web::WebState*)webState
+    runRepostFormDialogWithCompletionHandler:(void (^)(BOOL))handler {
+  _webState = webState;
+  _repostFormWarningRequested = YES;
+}
+
 - (web::JavaScriptDialogPresenter*)javaScriptDialogPresenterForWebState:
     (web::WebState*)webState {
   _webState = webState;
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index 7abae97..2e6d0d3 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -247,6 +247,9 @@
   // Notifies the delegate that a context menu needs handling.
   bool HandleContextMenu(const ContextMenuParams& params);
 
+  // Notifies the delegate that a Form Repost dialog needs to be presented.
+  void ShowRepostFormWarningDialog(const base::Callback<void(bool)>& callback);
+
   // Notifies the delegate that a JavaScript dialog needs to be presented.
   void RunJavaScriptDialog(const GURL& origin_url,
                            JavaScriptDialogType java_script_dialog_type,
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 6d5fe34..3bbae89 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -454,6 +454,15 @@
   return false;
 }
 
+void WebStateImpl::ShowRepostFormWarningDialog(
+    const base::Callback<void(bool)>& callback) {
+  if (delegate_) {
+    delegate_->ShowRepostFormWarningDialog(this, callback);
+  } else {
+    callback.Run(true);
+  }
+}
+
 void WebStateImpl::RunJavaScriptDialog(
     const GURL& origin_url,
     JavaScriptDialogType javascript_dialog_type,
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index 0337c0b6..25e5bf5 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -417,6 +417,13 @@
   web_state_->HandleContextMenu(context_menu_params);
   EXPECT_TRUE(delegate.handle_context_menu_called());
 
+  // Test that ShowRepostFormWarningDialog() is called.
+  EXPECT_FALSE(delegate.last_repost_form_request());
+  base::Callback<void(bool)> repost_callback;
+  web_state_->ShowRepostFormWarningDialog(repost_callback);
+  ASSERT_TRUE(delegate.last_repost_form_request());
+  EXPECT_EQ(delegate.last_repost_form_request()->web_state, web_state_.get());
+
   // Test that GetJavaScriptDialogPresenter() is called.
   TestJavaScriptDialogPresenter* presenter =
       delegate.GetTestJavaScriptDialogPresenter();
diff --git a/media/gpu/media_foundation_video_encode_accelerator_win.cc b/media/gpu/media_foundation_video_encode_accelerator_win.cc
index 17ba2b2..2de7b01 100644
--- a/media/gpu/media_foundation_video_encode_accelerator_win.cc
+++ b/media/gpu/media_foundation_video_encode_accelerator_win.cc
@@ -356,6 +356,8 @@
   DVLOG(3) << "HW encoder(s) found: " << count;
   hr = encoder_.CreateInstance(CLSIDs[0]);
   RETURN_ON_HR_FAILURE(hr, "Couldn't activate hardware encoder", false);
+  RETURN_ON_FAILURE((encoder_.get() != nullptr),
+                    "No HW encoder instance created", false);
   return true;
 }
 
@@ -438,8 +440,10 @@
 
 bool MediaFoundationVideoEncodeAccelerator::SetEncoderModes() {
   DCHECK(main_client_task_runner_->BelongsToCurrentThread());
+  RETURN_ON_FAILURE((encoder_.get() != nullptr),
+                    "No HW encoder instance created", false);
 
-  HRESULT hr = encoder_.QueryInterface(IID_ICodecAPI, codec_api_.ReceiveVoid());
+  HRESULT hr = encoder_.QueryInterface(codec_api_.Receive());
   RETURN_ON_HR_FAILURE(hr, "Couldn't get ICodecAPI", false);
   VARIANT var;
   var.vt = VT_UI4;
diff --git a/media/gpu/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi_video_decode_accelerator.cc
index 8c73ccc..cd97f57 100644
--- a/media/gpu/vaapi_video_decode_accelerator.cc
+++ b/media/gpu/vaapi_video_decode_accelerator.cc
@@ -268,9 +268,8 @@
   DISALLOW_COPY_AND_ASSIGN(VaapiVP9Accelerator);
 };
 
-VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0) {}
-
-VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() {}
+VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() = default;
+VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() = default;
 
 void VaapiVideoDecodeAccelerator::NotifyError(Error error) {
   if (!task_runner_->BelongsToCurrentThread()) {
@@ -462,41 +461,62 @@
     FinishFlush();
 }
 
-void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
+void VaapiVideoDecodeAccelerator::QueueInputBuffer(
     const BitstreamBuffer& bitstream_buffer) {
   DCHECK(task_runner_->BelongsToCurrentThread());
-  TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id",
+  TRACE_EVENT1("Video Decoder", "QueueInputBuffer", "input_id",
                bitstream_buffer.id());
 
-  DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
+  DVLOG(4) << "Queueing new input buffer id: " << bitstream_buffer.id()
            << " size: " << (int)bitstream_buffer.size();
 
-  std::unique_ptr<SharedMemoryRegion> shm(
-      new SharedMemoryRegion(bitstream_buffer, true));
-
-  // Skip empty buffers.
+  base::AutoLock auto_lock(lock_);
   if (bitstream_buffer.size() == 0) {
-    if (client_)
-      client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id());
-    return;
+    // Dummy buffer for flush.
+    DCHECK(!base::SharedMemory::IsHandleValid(bitstream_buffer.handle()));
+    input_buffers_.push(make_linked_ptr(new InputBuffer()));
+  } else {
+    std::unique_ptr<SharedMemoryRegion> shm(
+        new SharedMemoryRegion(bitstream_buffer, true));
+
+    RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(), "Failed to map input buffer",
+                                 UNREADABLE_INPUT, );
+
+    linked_ptr<InputBuffer> input_buffer(new InputBuffer());
+    input_buffer->shm = std::move(shm);
+    input_buffer->id = bitstream_buffer.id();
+    input_buffers_.push(input_buffer);
+    ++num_stream_bufs_at_decoder_;
+    TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
+                   num_stream_bufs_at_decoder_);
   }
 
-  RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(), "Failed to map input buffer",
-                               UNREADABLE_INPUT, );
-
-  base::AutoLock auto_lock(lock_);
-
-  // Set up a new input buffer and queue it for later.
-  linked_ptr<InputBuffer> input_buffer(new InputBuffer());
-  input_buffer->shm = std::move(shm);
-  input_buffer->id = bitstream_buffer.id();
-
-  ++num_stream_bufs_at_decoder_;
-  TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
-                 num_stream_bufs_at_decoder_);
-
-  input_buffers_.push(input_buffer);
   input_ready_.Signal();
+
+  switch (state_) {
+    case kIdle:
+      state_ = kDecoding;
+      decoder_thread_task_runner_->PostTask(
+          FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask,
+                                base::Unretained(this)));
+      break;
+
+    case kDecoding:
+      // Decoder already running.
+      break;
+
+    case kResetting:
+      // When resetting, allow accumulating bitstream buffers, so that
+      // the client can queue after-seek-buffers while we are finishing with
+      // the before-seek one.
+      break;
+
+    default:
+      LOG(ERROR) << "Decode/Flush request from client in invalid state: "
+                 << state_;
+      NotifyError(PLATFORM_FAILURE);
+      break;
+  }
 }
 
 bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() {
@@ -515,12 +535,6 @@
   // We could have got woken up in a different state or never got to sleep
   // due to current state; check for that.
   switch (state_) {
-    case kFlushing:
-      // Here we are only interested in finishing up decoding buffers that are
-      // already queued up. Otherwise will stop decoding.
-      if (input_buffers_.empty())
-        return false;
-      // else fallthrough
     case kDecoding:
     case kIdle:
       DCHECK(!input_buffers_.empty());
@@ -528,12 +542,17 @@
       curr_input_buffer_ = input_buffers_.front();
       input_buffers_.pop();
 
-      DVLOG(4) << "New current bitstream buffer, id: " << curr_input_buffer_->id
-               << " size: " << curr_input_buffer_->shm->size();
+      if (curr_input_buffer_->is_flush()) {
+        DVLOG(4) << "New flush buffer";
+      } else {
+        DVLOG(4) << "New current bitstream buffer, id: "
+                 << curr_input_buffer_->id
+                 << " size: " << curr_input_buffer_->shm->size();
 
-      decoder_->SetStream(
-          static_cast<uint8_t*>(curr_input_buffer_->shm->memory()),
-          curr_input_buffer_->shm->size());
+        decoder_->SetStream(
+            static_cast<uint8_t*>(curr_input_buffer_->shm->memory()),
+            curr_input_buffer_->shm->size());
+      }
       return true;
 
     default:
@@ -566,11 +585,11 @@
   DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
 
   while (available_va_surfaces_.empty() &&
-         (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) {
+         (state_ == kDecoding || state_ == kIdle)) {
     surfaces_available_.Wait();
   }
 
-  if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle)
+  if (state_ != kDecoding && state_ != kIdle)
     return false;
 
   return true;
@@ -592,6 +611,11 @@
   while (GetInputBuffer_Locked()) {
     DCHECK(curr_input_buffer_.get());
 
+    if (curr_input_buffer_->is_flush()) {
+      FlushTask();
+      break;
+    }
+
     AcceleratedVideoDecoder::DecodeResult res;
     {
       // We are OK releasing the lock here, as decoder never calls our methods
@@ -736,32 +760,17 @@
     return;
   }
 
-  // We got a new input buffer from the client, map it and queue for later use.
-  MapAndQueueNewInputBuffer(bitstream_buffer);
-
-  base::AutoLock auto_lock(lock_);
-  switch (state_) {
-    case kIdle:
-      state_ = kDecoding;
-      decoder_thread_task_runner_->PostTask(
-          FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask,
-                                base::Unretained(this)));
-      break;
-
-    case kDecoding:
-      // Decoder already running, fallthrough.
-    case kResetting:
-      // When resetting, allow accumulating bitstream buffers, so that
-      // the client can queue after-seek-buffers while we are finishing with
-      // the before-seek one.
-      break;
-
-    default:
-      RETURN_AND_NOTIFY_ON_FAILURE(
-          false, "Decode request from client in invalid state: " << state_,
-          PLATFORM_FAILURE, );
-      break;
+  // Skip empty buffers. VaapiVDA uses empty buffer as dummy buffer for flush
+  // internally.
+  if (bitstream_buffer.size() == 0) {
+    if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle()))
+      base::SharedMemory::CloseHandle(bitstream_buffer.handle());
+    if (client_)
+      client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id());
+    return;
   }
+
+  QueueInputBuffer(bitstream_buffer);
 }
 
 void VaapiVideoDecodeAccelerator::RecycleVASurfaceID(
@@ -825,10 +834,8 @@
     surfaces_available_.Signal();
   }
 
-  // The resolution changing may happen while resetting or flushing. In this
-  // case we do not change state and post DecodeTask().
-  if (state_ != kResetting && state_ != kFlushing) {
-    state_ = kDecoding;
+  // Resume DecodeTask if it is still in decoding state.
+  if (state_ == kDecoding) {
     decoder_thread_task_runner_->PostTask(
         FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask,
                               base::Unretained(this)));
@@ -909,8 +916,12 @@
 
 void VaapiVideoDecodeAccelerator::FlushTask() {
   DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread());
+  DCHECK(curr_input_buffer_.get() && curr_input_buffer_->is_flush());
+
   DVLOG(1) << "Flush task";
 
+  curr_input_buffer_.reset();
+
   // First flush all the pictures that haven't been outputted, notifying the
   // client to output them.
   bool res = decoder_->Flush();
@@ -929,15 +940,8 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
   DVLOG(1) << "Got flush request";
 
-  base::AutoLock auto_lock(lock_);
-  state_ = kFlushing;
-  // Queue a flush task after all existing decoding tasks to clean up.
-  decoder_thread_task_runner_->PostTask(
-      FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::FlushTask,
-                            base::Unretained(this)));
-
-  input_ready_.Signal();
-  surfaces_available_.Signal();
+  // Queue a dummy buffer, which means flush.
+  QueueInputBuffer(media::BitstreamBuffer());
 }
 
 void VaapiVideoDecodeAccelerator::FinishFlush() {
@@ -946,7 +950,7 @@
   finish_flush_pending_ = false;
 
   base::AutoLock auto_lock(lock_);
-  if (state_ != kFlushing) {
+  if (state_ != kDecoding) {
     DCHECK_EQ(state_, kDestroying);
     return;  // We could've gotten destroyed already.
   }
@@ -958,7 +962,14 @@
     return;
   }
 
-  state_ = kIdle;
+  // Resume decoding if necessary.
+  if (input_buffers_.empty()) {
+    state_ = kIdle;
+  } else {
+    decoder_thread_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask,
+                              base::Unretained(this)));
+  }
 
   task_runner_->PostTask(FROM_HERE,
                          base::Bind(&Client::NotifyFlushDone, client_));
@@ -998,9 +1009,11 @@
 
   // Drop all remaining input buffers, if present.
   while (!input_buffers_.empty()) {
-    task_runner_->PostTask(
-        FROM_HERE, base::Bind(&Client::NotifyEndOfBitstreamBuffer, client_,
-                              input_buffers_.front()->id));
+    const auto& input_buffer = input_buffers_.front();
+    if (!input_buffer->is_flush())
+      task_runner_->PostTask(
+          FROM_HERE, base::Bind(&Client::NotifyEndOfBitstreamBuffer, client_,
+                                input_buffer->id));
     input_buffers_.pop();
   }
 
diff --git a/media/gpu/vaapi_video_decode_accelerator.h b/media/gpu/vaapi_video_decode_accelerator.h
index 8228a928..241c002 100644
--- a/media/gpu/vaapi_video_decode_accelerator.h
+++ b/media/gpu/vaapi_video_decode_accelerator.h
@@ -91,9 +91,8 @@
   // Notify the client that an error has occurred and decoding cannot continue.
   void NotifyError(Error error);
 
-  // Map the received input buffer into this process' address space and
-  // queue it for decode.
-  void MapAndQueueNewInputBuffer(const BitstreamBuffer& bitstream_buffer);
+  // Queue a input buffer for decode.
+  void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer);
 
   // Get a new input buffer from the queue and set it up in decoder. This will
   // sleep if no input buffers are available. Return true if a new buffer has
@@ -196,8 +195,6 @@
     kDecoding,
     // Resetting, waiting for decoder to finish current task and cleanup.
     kResetting,
-    // Flushing, waiting for decoder to finish current task and cleanup.
-    kFlushing,
     // Idle, decoder in state ready to start/resume decoding.
     kIdle,
     // Destroying, waiting for the decoder to finish current task.
@@ -214,7 +211,10 @@
     InputBuffer();
     ~InputBuffer();
 
-    int32_t id;
+    // Indicates this is a dummy buffer for flush request.
+    bool is_flush() const { return shm == nullptr; }
+
+    int32_t id = -1;
     std::unique_ptr<SharedMemoryRegion> shm;
   };
 
diff --git a/media/mojo/clients/mojo_audio_decoder.cc b/media/mojo/clients/mojo_audio_decoder.cc
index 03b69138..d4575c2 100644
--- a/media/mojo/clients/mojo_audio_decoder.cc
+++ b/media/mojo/clients/mojo_audio_decoder.cc
@@ -48,6 +48,7 @@
 
   // This could happen during reinitialization.
   if (remote_decoder_.encountered_error()) {
+    DVLOG(1) << __func__ << ": Connection error happened.";
     task_runner_->PostTask(FROM_HERE, base::Bind(init_cb, false));
     return;
   }
@@ -58,6 +59,7 @@
                    : CdmContext::kInvalidCdmId;
 
   if (config.is_encrypted() && CdmContext::kInvalidCdmId == cdm_id) {
+    DVLOG(1) << __func__ << ": Invalid CdmContext.";
     task_runner_->PostTask(FROM_HERE, base::Bind(init_cb, false));
     return;
   }
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index f9e46334..a92031bd 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -6,6 +6,7 @@
 import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
 import("//testing/test.gni")
 
 config("mojo_media_config") {
@@ -162,24 +163,22 @@
 # component builds, so don't declare it, otherwise the "all" target will still
 # try to build it.
 if (!(is_win && is_component_build)) {
-  test("media_service_unittests") {
+  service_test("media_service_unittests") {
     testonly = true
 
     sources = [
       "media_service_unittest.cc",
-      "run_all_unittests.cc",
     ]
 
+    catalog = ":media_service_unittests_catalog"
+
     deps = [
       "//base",
-      "//base/test:test_support",
       "//media",
       "//media/base:test_support",
       "//media/mojo/clients",
       "//media/mojo/common",
       "//media/mojo/interfaces",
-      "//mojo/edk/system",
-      "//services/catalog:lib",
       "//services/service_manager/public/cpp:service_test_support",
       "//testing/gmock",
       "//testing/gtest",
@@ -187,7 +186,6 @@
 
     data_deps = [
       ":media",
-      ":media_service_unittests_catalog_copy",
     ]
   }
 }  # !(is_win && is_component_build)
@@ -225,13 +223,3 @@
   embedded_services = [ ":test_manifest" ]
   standalone_services = [ ":media_manifest" ]
 }
-
-copy("media_service_unittests_catalog_copy") {
-  sources = get_target_outputs(":media_service_unittests_catalog")
-  outputs = [
-    "${root_out_dir}/media_service_unittests_catalog.json",
-  ]
-  deps = [
-    ":media_service_unittests_catalog",
-  ]
-}
diff --git a/media/mojo/services/DEPS b/media/mojo/services/DEPS
deleted file mode 100644
index 03898ce..0000000
--- a/media/mojo/services/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-specific_include_rules = {
-  "run_all_unittests.cc": [
-    "+mojo/edk/embedder",
-    "+services/catalog",
-  ]
-}
diff --git a/media/mojo/services/run_all_unittests.cc b/media/mojo/services/run_all_unittests.cc
deleted file mode 100644
index 6e5204f..0000000
--- a/media/mojo/services/run_all_unittests.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/test_suite.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "services/catalog/catalog.h"
-
-namespace {
-
-const base::FilePath::CharType kCatalogFilename[] =
-    FILE_PATH_LITERAL("media_service_unittests_catalog.json");
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::TestSuite test_suite(argc, argv);
-
-  catalog::Catalog::LoadDefaultCatalogManifest(
-      base::FilePath(kCatalogFilename));
-
-  mojo::edk::Init();
-  base::Thread ipc_thread("IPC thread");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  return base::LaunchUnitTests(
-      argc, argv,
-      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
-}
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc
index 6803d30..d5ab1e8 100644
--- a/net/http/http_response_headers.cc
+++ b/net/http/http_response_headers.cc
@@ -1116,31 +1116,48 @@
   return lifetimes;
 }
 
-// From RFC 2616 section 13.2.3:
+// From RFC 7234 section 4.2.3:
 //
-// Summary of age calculation algorithm, when a cache receives a response:
+// The following data is used for the age calculation:
 //
-//   /*
-//    * age_value
-//    *      is the value of Age: header received by the cache with
-//    *              this response.
-//    * date_value
-//    *      is the value of the origin server's Date: header
-//    * request_time
-//    *      is the (local) time when the cache made the request
-//    *              that resulted in this cached response
-//    * response_time
-//    *      is the (local) time when the cache received the
-//    *              response
-//    * now
-//    *      is the current (local) time
-//    */
-//   apparent_age = max(0, response_time - date_value);
-//   corrected_received_age = max(apparent_age, age_value);
-//   response_delay = response_time - request_time;
-//   corrected_initial_age = corrected_received_age + response_delay;
-//   resident_time = now - response_time;
-//   current_age   = corrected_initial_age + resident_time;
+//    age_value
+//
+//       The term "age_value" denotes the value of the Age header field
+//       (Section 5.1), in a form appropriate for arithmetic operation; or
+//       0, if not available.
+//
+//    date_value
+//
+//       The term "date_value" denotes the value of the Date header field,
+//       in a form appropriate for arithmetic operations.  See Section
+//       7.1.1.2 of [RFC7231] for the definition of the Date header field,
+//       and for requirements regarding responses without it.
+//
+//    now
+//
+//       The term "now" means "the current value of the clock at the host
+//       performing the calculation".  A host ought to use NTP ([RFC5905])
+//       or some similar protocol to synchronize its clocks to Coordinated
+//       Universal Time.
+//
+//    request_time
+//
+//       The current value of the clock at the host at the time the request
+//       resulting in the stored response was made.
+//
+//    response_time
+//
+//       The current value of the clock at the host at the time the
+//       response was received.
+//
+//    The age is then calculated as
+//
+//     apparent_age = max(0, response_time - date_value);
+//     response_delay = response_time - request_time;
+//     corrected_age_value = age_value + response_delay;
+//     corrected_initial_age = max(apparent_age, corrected_age_value);
+//     resident_time = now - response_time;
+//     current_age = corrected_initial_age + resident_time;
 //
 TimeDelta HttpResponseHeaders::GetCurrentAge(const Time& request_time,
                                              const Time& response_time,
@@ -1157,9 +1174,9 @@
   GetAgeValue(&age_value);
 
   TimeDelta apparent_age = std::max(TimeDelta(), response_time - date_value);
-  TimeDelta corrected_received_age = std::max(apparent_age, age_value);
   TimeDelta response_delay = response_time - request_time;
-  TimeDelta corrected_initial_age = corrected_received_age + response_delay;
+  TimeDelta corrected_age_value = age_value + response_delay;
+  TimeDelta corrected_initial_age = std::max(apparent_age, corrected_age_value);
   TimeDelta resident_time = current_time - response_time;
   TimeDelta current_age = corrected_initial_age + resident_time;
 
diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc
index 68a55f3..3edee0f7 100644
--- a/net/http/http_response_headers_unittest.cc
+++ b/net/http/http_response_headers_unittest.cc
@@ -2203,6 +2203,69 @@
   EXPECT_EQ(TimeDelta::FromSeconds(1), GetStaleWhileRevalidateValue());
 }
 
+struct GetCurrentAgeTestData {
+  const char* headers;
+  const char* request_time;
+  const char* response_time;
+  const char* current_time;
+  const int expected_age;
+};
+
+class GetCurrentAgeTest
+    : public HttpResponseHeadersTest,
+      public ::testing::WithParamInterface<GetCurrentAgeTestData> {
+};
+
+TEST_P(GetCurrentAgeTest, GetCurrentAge) {
+  const GetCurrentAgeTestData test = GetParam();
+
+  base::Time request_time, response_time, current_time;
+  ASSERT_TRUE(base::Time::FromString(test.request_time, &request_time));
+  ASSERT_TRUE(base::Time::FromString(test.response_time, &response_time));
+  ASSERT_TRUE(base::Time::FromString(test.current_time, &current_time));
+
+  std::string headers(test.headers);
+  HeadersToRaw(&headers);
+  scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers));
+
+  base::TimeDelta age =
+      parsed->GetCurrentAge(request_time, response_time, current_time);
+  EXPECT_EQ(test.expected_age, age.InSeconds());
+}
+
+const struct GetCurrentAgeTestData get_current_age_tests[] = {
+    // Without Date header.
+    {"HTTP/1.1 200 OK\n"
+     "Age: 2",
+     "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
+     "Fri, 20 Jan 2011 10:40:14 GMT", 8},
+    // Without Age header.
+    {"HTTP/1.1 200 OK\n"
+     "Date: Fri, 20 Jan 2011 10:40:10 GMT\n",
+     "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
+     "Fri, 20 Jan 2011 10:40:14 GMT", 6},
+    // date_value > response_time with Age header.
+    {"HTTP/1.1 200 OK\n"
+     "Date: Fri, 20 Jan 2011 10:40:14 GMT\n"
+     "Age: 2\n",
+     "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
+     "Fri, 20 Jan 2011 10:40:14 GMT", 8},
+     // date_value > response_time without Age header.
+     {"HTTP/1.1 200 OK\n"
+     "Date: Fri, 20 Jan 2011 10:40:14 GMT\n",
+     "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
+     "Fri, 20 Jan 2011 10:40:14 GMT", 6},
+    // apparent_age > corrected_age_value
+    {"HTTP/1.1 200 OK\n"
+     "Date: Fri, 20 Jan 2011 10:40:07 GMT\n"
+     "Age: 0\n",
+     "Fri, 20 Jan 2011 10:40:08 GMT", "Fri, 20 Jan 2011 10:40:12 GMT",
+     "Fri, 20 Jan 2011 10:40:14 GMT", 7}};
+
+INSTANTIATE_TEST_CASE_P(HttpResponseHeaders,
+                        GetCurrentAgeTest,
+                        testing::ValuesIn(get_current_age_tests));
+
 }  // namespace
 
 }  // namespace net
diff --git a/services/catalog/public/tools/catalog.gni b/services/catalog/public/tools/catalog.gni
index ae112b4..67a5774 100644
--- a/services/catalog/public/tools/catalog.gni
+++ b/services/catalog/public/tools/catalog.gni
@@ -138,3 +138,58 @@
     }
   }
 }
+
+# Generates a source_set target which defines a single string contstant
+# containing the contents of a compiled catalog manifest.
+#
+# Parameters:
+#
+#   catalog
+#       The catalog target whose output should be stringified.
+#
+#   output_symbol_name
+#       The fully qualified symbol name of the C++ string constant to define in
+#       the generate source_set.
+#
+template("catalog_cpp_source") {
+  assert(defined(invoker.catalog), "catalog is required")
+  assert(defined(invoker.output_symbol_name), "output_symbol_name is required")
+
+  catalog_target = invoker.catalog
+  catalog_target_dir = get_label_info(catalog_target, "target_gen_dir")
+  catalog_target_name = get_label_info(catalog_target, "name")
+  catalog_filename = "$catalog_target_dir/${catalog_target_name}.json"
+
+  generator_target_name = "${target_name}__generator"
+  generated_filename = "${target_gen_dir}/${target_name}.cc"
+
+  action(generator_target_name) {
+    testonly = defined(invoker.testonly) && invoker.testonly
+    script = "//services/catalog/public/tools/sourcify_manifest.py"
+    inputs = [
+      catalog_filename,
+    ]
+    outputs = [
+      generated_filename,
+    ]
+    args = [
+      "--input=" + rebase_path(catalog_filename, root_build_dir),
+      "--output=" + rebase_path(generated_filename, root_build_dir),
+      "--symbol-name=" + invoker.output_symbol_name,
+    ]
+    if (is_debug || dcheck_always_on) {
+      args += [ "--pretty" ]
+    }
+    deps = [
+      catalog_target,
+    ]
+  }
+
+  source_set(target_name) {
+    testonly = defined(invoker.testonly) && invoker.testonly
+    sources = get_target_outputs(":$generator_target_name")
+    deps = [
+      ":$generator_target_name",
+    ]
+  }
+}
diff --git a/services/catalog/public/tools/sourcify_manifest.py b/services/catalog/public/tools/sourcify_manifest.py
new file mode 100755
index 0000000..81c219d
--- /dev/null
+++ b/services/catalog/public/tools/sourcify_manifest.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Generates a C++ source file which defines a string constant containing the
+contents of a catalog manifest file. Useful for baking catalogs into binaries
+which don't want to hit disk before initializing the catalog."""
+
+import argparse
+import json
+import os.path
+import sys
+
+
+# Token used to delimit raw strings in the generated source file. It's illegal
+# for this token to appear within the contents of the input manifest itself.
+_RAW_STRING_DELIMITER = "#CATALOG_JSON#"
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="Generates a C++ constant containing a catalog manifest.")
+  parser.add_argument("--input")
+  parser.add_argument("--output")
+  parser.add_argument("--symbol-name")
+  parser.add_argument("--pretty", action="store_true")
+  args, _ = parser.parse_known_args()
+
+  if args.input is None or args.output is None or args.symbol_name is None:
+    raise Exception("--input, --output, and --symbol-name are required")
+
+  with open(args.input, 'r') as input_file:
+    manifest_contents = input_file.read()
+
+  if manifest_contents.find(_RAW_STRING_DELIMITER) >= 0:
+    raise Exception(
+        "Unexpected '%s' found in input manifest." % _RAW_STRING_DELIMITER)
+
+  qualified_symbol_name = args.symbol_name.split("::")
+  namespace = qualified_symbol_name[0:-1]
+  symbol_name = qualified_symbol_name[-1]
+
+  with open(args.output, 'w') as output_file:
+    output_file.write(
+        "// This is a generated file produced by\n"
+        "// src/services/catalog/public/tools/sourcify_manifest.py.\n\n")
+    for name in namespace:
+      output_file.write("namespace %s {\n" % name)
+    output_file.write("\nextern const char %s[];" % symbol_name)
+    output_file.write("\nconst char %s[] = R\"%s(%s)%s\";\n\n" %
+        (symbol_name, _RAW_STRING_DELIMITER, manifest_contents,
+            _RAW_STRING_DELIMITER))
+    for name in reversed(namespace):
+      output_file.write("}  // %s\n" % name)
+
+  return 0
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/services/service_manager/public/cpp/connector.h b/services/service_manager/public/cpp/connector.h
index b2829c1..793fe91 100644
--- a/services/service_manager/public/cpp/connector.h
+++ b/services/service_manager/public/cpp/connector.h
@@ -69,6 +69,12 @@
                      mojo::InterfacePtr<Interface>* ptr) {
     return BindInterface(Identity(name, mojom::kInheritUserID), ptr);
   }
+  template <typename Interface>
+  void BindInterface(const std::string& name,
+                     mojo::InterfaceRequest<Interface> request) {
+    return BindInterface(Identity(name, mojom::kInheritUserID),
+                         Interface::Name_, request.PassMessagePipe());
+  }
   virtual void BindInterface(const Identity& target,
                              const std::string& interface_name,
                              mojo::ScopedMessagePipeHandle interface_pipe) = 0;
diff --git a/services/service_manager/public/cpp/test/BUILD.gn b/services/service_manager/public/cpp/test/BUILD.gn
index e9fb8c9..569aac0 100644
--- a/services/service_manager/public/cpp/test/BUILD.gn
+++ b/services/service_manager/public/cpp/test/BUILD.gn
@@ -26,3 +26,22 @@
     "//services/service_manager/background:main",
   ]
 }
+
+# TODO(rockot): Remove this when all consumers of :run_all_service_tests are
+# ported to service_test targets.
+source_set("run_all_service_tests_with_catalog") {
+  testonly = true
+
+  sources = [
+    "run_all_service_tests_with_catalog.cc",
+    "service_test_catalog.h",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//services/catalog:lib",
+    "//services/service_manager/background:lib",
+  ]
+}
diff --git a/services/service_manager/public/cpp/test/DEPS b/services/service_manager/public/cpp/test/DEPS
new file mode 100644
index 0000000..11f060e9
--- /dev/null
+++ b/services/service_manager/public/cpp/test/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "run_all_service_tests_with_catalog.cc": [
+    "+services/catalog",
+  ]
+}
diff --git a/services/ui/demo/run_all_unittests.cc b/services/service_manager/public/cpp/test/run_all_service_tests_with_catalog.cc
similarity index 66%
rename from services/ui/demo/run_all_unittests.cc
rename to services/service_manager/public/cpp/test/run_all_service_tests_with_catalog.cc
index 29dc7f8..bce6a6c 100644
--- a/services/ui/demo/run_all_unittests.cc
+++ b/services/service_manager/public/cpp/test/run_all_service_tests_with_catalog.cc
@@ -1,31 +1,30 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// 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.
 
-#include "base/files/file_path.h"
-#include "base/logging.h"
+#include <memory>
+
+#include "base/json/json_reader.h"
 #include "base/message_loop/message_loop.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
 #include "base/threading/thread.h"
+#include "base/values.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/scoped_ipc_support.h"
 #include "services/catalog/catalog.h"
-
-namespace {
-
-const base::FilePath::CharType kCatalogFilename[] =
-    FILE_PATH_LITERAL("mus_demo_unittests_catalog.json");
-
-}  // namespace
+#include "services/service_manager/public/cpp/test/service_test_catalog.h"
 
 int main(int argc, char** argv) {
   base::TestSuite test_suite(argc, argv);
 
-  catalog::Catalog::LoadDefaultCatalogManifest(
-      base::FilePath(kCatalogFilename));
+  std::unique_ptr<base::Value> manifest_value =
+      base::JSONReader::Read(service_manager::test::kServiceTestCatalog);
+  DCHECK(manifest_value);
+  catalog::Catalog::SetDefaultCatalogManifest(std::move(manifest_value));
 
   mojo::edk::Init();
+
   base::Thread ipc_thread("IPC thread");
   ipc_thread.StartWithOptions(
       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
diff --git a/services/service_manager/public/cpp/test/service_test_catalog.h b/services/service_manager/public/cpp/test/service_test_catalog.h
new file mode 100644
index 0000000..2a627c2
--- /dev/null
+++ b/services/service_manager/public/cpp/test/service_test_catalog.h
@@ -0,0 +1,18 @@
+// 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.
+
+namespace service_manager {
+namespace test {
+
+// This symbol must be defined by any target linking against the
+// ":run_all_service_tests" target in this directory. Use the service_test
+// GN template defined in
+// src/services/service_manager/public/tools/test/service_test.gni to
+// autogenerate and link against a definition of the symbol dervied from the
+// contents of a generated service catalog. See the service_test.gni
+// documentation for more details.
+extern const char kServiceTestCatalog[];
+
+}  // namespace test
+}  // namespace service_manager
diff --git a/services/service_manager/public/tools/test/service_test.gni b/services/service_manager/public/tools/test/service_test.gni
new file mode 100644
index 0000000..9f5508c
--- /dev/null
+++ b/services/service_manager/public/tools/test/service_test.gni
@@ -0,0 +1,37 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//services/catalog/public/tools/catalog.gni")
+import("//testing/test.gni")
+
+# Generates a unittest binary with a baked-in Service Manager catalog manifest.
+#
+# Parameters:
+#
+#   catalog
+#       The catalog target whose output should be used as the static catalog
+#       manifest for this test binary.
+#
+template("service_test") {
+  assert(defined(invoker.catalog), "service_test must specify a catalog")
+
+  catalog_source_target = "${target_name}__catalog_source"
+
+  test(target_name) {
+    forward_variables_from(invoker, "*", [ "catalog" ])
+
+    if (!defined(deps)) {
+      deps = []
+    }
+    deps += [
+      ":$catalog_source_target",
+      "//services/service_manager/public/cpp/test:run_all_service_tests_with_catalog",
+    ]
+  }
+
+  catalog_cpp_source(catalog_source_target) {
+    catalog = invoker.catalog
+    output_symbol_name = "service_manager::test::kServiceTestCatalog"
+  }
+}
diff --git a/services/shape_detection/BUILD.gn b/services/shape_detection/BUILD.gn
new file mode 100644
index 0000000..05fdfb2
--- /dev/null
+++ b/services/shape_detection/BUILD.gn
@@ -0,0 +1,45 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+
+source_set("lib") {
+  sources = [
+    "face_detection_provider_impl.h",
+    "shape_detection_service.cc",
+    "shape_detection_service.h",
+  ]
+
+  if (is_mac) {
+    sources += [
+      "face_detection_impl_mac.h",
+      "face_detection_impl_mac.mm",
+    ]
+  } else {
+    sources += [ "face_detection_provider_impl.cc" ]
+  }
+
+  deps = [
+    "//mojo/public/cpp/bindings",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+
+  public_deps = [
+    "//base",
+    "//media/capture",
+    "//services/service_manager/public/cpp",
+    "//services/shape_detection/public/interfaces",
+  ]
+
+  data_deps = [
+    ":manifest",
+  ]
+}
+
+service_manifest("manifest") {
+  name = "shape_detection"
+  source = "manifest.json"
+}
diff --git a/services/shape_detection/DEPS b/services/shape_detection/DEPS
new file mode 100644
index 0000000..7db8eb2
--- /dev/null
+++ b/services/shape_detection/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+media/capture/video/scoped_result_callback.h",
+]
diff --git a/services/shape_detection/README.md b/services/shape_detection/README.md
new file mode 100644
index 0000000..679d4dc
--- /dev/null
+++ b/services/shape_detection/README.md
@@ -0,0 +1,2 @@
+Shape Detection is an isolated service that exists to facilitate safe
+face/barcode detection.
diff --git a/services/shape_detection/face_detection_impl_mac.h b/services/shape_detection/face_detection_impl_mac.h
new file mode 100644
index 0000000..a67118e
--- /dev/null
+++ b/services/shape_detection/face_detection_impl_mac.h
@@ -0,0 +1,35 @@
+// 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 SERVICES_SHAPE_DETECTION_FACE_DETECTION_IMPL_MAC_H_
+#define SERVICES_SHAPE_DETECTION_FACE_DETECTION_IMPL_MAC_H_
+
+#import <QuartzCore/QuartzCore.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "services/shape_detection/public/interfaces/facedetection.mojom.h"
+
+namespace shape_detection {
+
+class FaceDetectionImplMac : public shape_detection::mojom::FaceDetection {
+ public:
+  FaceDetectionImplMac(shape_detection::mojom::FaceDetectorOptionsPtr options);
+  ~FaceDetectionImplMac() override;
+
+  void Detect(mojo::ScopedSharedBufferHandle frame_data,
+              uint32_t width,
+              uint32_t height,
+              const shape_detection::mojom::FaceDetection::DetectCallback&
+                  callback) override;
+
+ private:
+  base::scoped_nsobject<CIContext> context_;
+  base::scoped_nsobject<CIDetector> detector_;
+
+  DISALLOW_COPY_AND_ASSIGN(FaceDetectionImplMac);
+};
+
+}  // namespace shape_detection
+
+#endif  // SERVICES_SHAPE_DETECTION_FACE_DETECTION_IMPL_MAC_H_
diff --git a/services/shape_detection/face_detection_impl_mac.mm b/services/shape_detection/face_detection_impl_mac.mm
new file mode 100644
index 0000000..ab40e708
--- /dev/null
+++ b/services/shape_detection/face_detection_impl_mac.mm
@@ -0,0 +1,127 @@
+// 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 "services/shape_detection/face_detection_impl_mac.h"
+
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/shared_memory.h"
+#include "media/capture/video/scoped_result_callback.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "services/shape_detection/face_detection_provider_impl.h"
+
+namespace shape_detection {
+
+namespace {
+
+// kCIFormatRGBA8 is not exposed to public until Mac 10.11. So we define the
+// same constant to support RGBA8 format in earlier versions.
+#if !defined(MAC_OS_X_VERSION_10_11) || \
+    MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11
+const int kCIFormatRGBA8 = 24;
+#endif
+
+void RunCallbackWithFaces(
+    const shape_detection::mojom::FaceDetection::DetectCallback& callback,
+    shape_detection::mojom::FaceDetectionResultPtr faces) {
+  callback.Run(std::move(faces));
+}
+
+void RunCallbackWithNoFaces(
+    const shape_detection::mojom::FaceDetection::DetectCallback& callback) {
+  callback.Run(shape_detection::mojom::FaceDetectionResult::New());
+}
+
+}  // anonymous namespace
+
+void FaceDetectionProviderImpl::CreateFaceDetection(
+    shape_detection::mojom::FaceDetectionRequest request,
+    shape_detection::mojom::FaceDetectorOptionsPtr options) {
+  mojo::MakeStrongBinding(
+      base::MakeUnique<FaceDetectionImplMac>(std::move(options)),
+      std::move(request));
+}
+
+FaceDetectionImplMac::FaceDetectionImplMac(
+    shape_detection::mojom::FaceDetectorOptionsPtr options) {
+  context_.reset([[CIContext alloc] init]);
+  NSDictionary* const opts = @{CIDetectorAccuracy : CIDetectorAccuracyHigh};
+  detector_.reset([[CIDetector detectorOfType:CIDetectorTypeFace
+                                      context:context_
+                                      options:opts] retain]);
+}
+
+FaceDetectionImplMac::~FaceDetectionImplMac() {}
+
+void FaceDetectionImplMac::Detect(mojo::ScopedSharedBufferHandle frame_data,
+                                  uint32_t width,
+                                  uint32_t height,
+                                  const DetectCallback& callback) {
+  media::ScopedResultCallback<DetectCallback> scoped_callback(
+      base::Bind(&RunCallbackWithFaces, callback),
+      base::Bind(&RunCallbackWithNoFaces));
+
+  base::CheckedNumeric<uint32_t> num_pixels =
+      base::CheckedNumeric<uint32_t>(width) * height;
+  base::CheckedNumeric<uint32_t> num_bytes = num_pixels * 4;
+  if (!num_bytes.IsValid()) {
+    DLOG(ERROR) << "Data overflow";
+    return;
+  }
+
+  base::SharedMemoryHandle memory_handle;
+  size_t memory_size = 0;
+  bool read_only_flag = false;
+  const MojoResult result = mojo::UnwrapSharedMemoryHandle(
+      std::move(frame_data), &memory_handle, &memory_size, &read_only_flag);
+  DCHECK_EQ(MOJO_RESULT_OK, result) << "Failed to unwrap SharedBufferHandle";
+  if (!memory_size || memory_size != num_bytes.ValueOrDie()) {
+    DLOG(ERROR) << "Invalid image size";
+    return;
+  }
+
+  auto shared_memory =
+      base::MakeUnique<base::SharedMemory>(memory_handle, true /* read_only */);
+  if (!shared_memory->Map(memory_size)) {
+    DLOG(ERROR) << "Failed to map bytes from shared memory";
+    return;
+  }
+
+  NSData* byte_data = [NSData dataWithBytesNoCopy:shared_memory->memory()
+                                           length:num_bytes.ValueOrDie()
+                                     freeWhenDone:NO];
+
+  base::ScopedCFTypeRef<CGColorSpaceRef> colorspace(
+      CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
+
+  // CIImage will return nil when RGBA8 is not supported in a certain version.
+  base::scoped_nsobject<CIImage> ci_image([[CIImage alloc]
+      initWithBitmapData:byte_data
+             bytesPerRow:width * 4
+                    size:CGSizeMake(width, height)
+                  format:kCIFormatRGBA8
+              colorSpace:colorspace]);
+  if (!ci_image) {
+    DLOG(ERROR) << "Failed to create CIImage";
+    return;
+  }
+
+  NSArray* const features = [detector_ featuresInImage:ci_image];
+
+  shape_detection::mojom::FaceDetectionResultPtr faces =
+      shape_detection::mojom::FaceDetectionResult::New();
+  for (CIFaceFeature* const f in features) {
+    // In the default Core Graphics coordinate space, the origin is located
+    // in the lower-left corner, and thus |ci_image| is flipped vertically.
+    // We need to adjust |y| coordinate of bounding box before sending it.
+    gfx::RectF boundingbox(f.bounds.origin.x,
+                           height - f.bounds.origin.y - f.bounds.size.height,
+                           f.bounds.size.width, f.bounds.size.height);
+    faces->bounding_boxes.push_back(boundingbox);
+  }
+  scoped_callback.Run(std::move(faces));
+}
+
+}  // namespace shape_detection
diff --git a/services/shape_detection/face_detection_provider_impl.cc b/services/shape_detection/face_detection_provider_impl.cc
new file mode 100644
index 0000000..e552ed06
--- /dev/null
+++ b/services/shape_detection/face_detection_provider_impl.cc
@@ -0,0 +1,15 @@
+// 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 "services/shape_detection/face_detection_provider_impl.h"
+
+namespace shape_detection {
+
+void FaceDetectionProviderImpl::CreateFaceDetection(
+    shape_detection::mojom::FaceDetectionRequest request,
+    shape_detection::mojom::FaceDetectorOptionsPtr options) {
+  DLOG(ERROR) << "Platform not supported for Face Detection Service.";
+}
+
+}  // namespace shape_detection
diff --git a/services/shape_detection/face_detection_provider_impl.h b/services/shape_detection/face_detection_provider_impl.h
new file mode 100644
index 0000000..a2b86909
--- /dev/null
+++ b/services/shape_detection/face_detection_provider_impl.h
@@ -0,0 +1,31 @@
+// 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 SERVICES_SHAPE_DETECTION_FACE_DETECTION_PROVIDER_IMPL_H_
+#define SERVICES_SHAPE_DETECTION_FACE_DETECTION_PROVIDER_IMPL_H_
+
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/shape_detection/public/interfaces/facedetection_provider.mojom.h"
+
+namespace shape_detection {
+
+class FaceDetectionProviderImpl
+    : public shape_detection::mojom::FaceDetectionProvider {
+ public:
+  ~FaceDetectionProviderImpl() override = default;
+
+  static void Create(
+      shape_detection::mojom::FaceDetectionProviderRequest request) {
+    mojo::MakeStrongBinding(base::MakeUnique<FaceDetectionProviderImpl>(),
+                            std::move(request));
+  }
+
+  void CreateFaceDetection(
+      shape_detection::mojom::FaceDetectionRequest request,
+      shape_detection::mojom::FaceDetectorOptionsPtr options) override;
+};
+
+}  // namespace shape_detection
+
+#endif  // SERVICES_SHAPE_DETECTION_FACE_DETECTION_PROVIDER_IMPL_H_
diff --git a/services/shape_detection/manifest.json b/services/shape_detection/manifest.json
new file mode 100644
index 0000000..b92fb5c
--- /dev/null
+++ b/services/shape_detection/manifest.json
@@ -0,0 +1,15 @@
+{
+  "name": "shape_detection",
+  "display_name": "Shape Detection Service",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "face_detection": [ "shape_detection::mojom::FaceDetectionProvider" ],
+        "barcode_detection": [ "shape_detection::mojom::BarcodeDetection" ]
+      },
+      "requires": {
+        "service_manager": [ "service_manager:all_users" ]
+      }
+    }
+  }
+}
diff --git a/services/shape_detection/public/interfaces/BUILD.gn b/services/shape_detection/public/interfaces/BUILD.gn
index 6aaf8a4..953742864 100644
--- a/services/shape_detection/public/interfaces/BUILD.gn
+++ b/services/shape_detection/public/interfaces/BUILD.gn
@@ -7,6 +7,7 @@
 mojom("interfaces") {
   sources = [
     "barcodedetection.mojom",
+    "constants.mojom",
     "facedetection.mojom",
     "facedetection_provider.mojom",
     "textdetection.mojom",
diff --git a/services/shape_detection/public/interfaces/constants.mojom b/services/shape_detection/public/interfaces/constants.mojom
new file mode 100644
index 0000000..85f919b
--- /dev/null
+++ b/services/shape_detection/public/interfaces/constants.mojom
@@ -0,0 +1,9 @@
+// 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.
+
+// https://wicg.github.io/shape-detection-api/#api
+
+module shape_detection.mojom;
+
+const string kServiceName = "shape_detection";
diff --git a/services/shape_detection/shape_detection_service.cc b/services/shape_detection/shape_detection_service.cc
new file mode 100644
index 0000000..d9210e9
--- /dev/null
+++ b/services/shape_detection/shape_detection_service.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 "services/shape_detection/shape_detection_service.h"
+
+#include "base/macros.h"
+#include "services/service_manager/public/cpp/interface_registry.h"
+#include "services/service_manager/public/cpp/service_context.h"
+#include "services/shape_detection/face_detection_provider_impl.h"
+
+namespace shape_detection {
+
+namespace {
+
+void OnConnectionLost(std::unique_ptr<service_manager::ServiceContextRef> ref) {
+  // No-op. This merely takes ownership of |ref| so it can be destroyed when
+  // this function is invoked.
+}
+}
+
+std::unique_ptr<service_manager::Service> ShapeDetectionService::Create() {
+  return base::MakeUnique<ShapeDetectionService>();
+}
+
+ShapeDetectionService::ShapeDetectionService() = default;
+
+ShapeDetectionService::~ShapeDetectionService() = default;
+
+void ShapeDetectionService::OnStart() {
+  ref_factory_.reset(new service_manager::ServiceContextRefFactory(
+      base::Bind(&service_manager::ServiceContext::RequestQuit,
+                 base::Unretained(context()))));
+}
+
+bool ShapeDetectionService::OnConnect(
+    const service_manager::ServiceInfo& remote_info,
+    service_manager::InterfaceRegistry* registry) {
+  // Add a reference to the service and tie it to the lifetime of the
+  // InterfaceRegistry's connection.
+  std::unique_ptr<service_manager::ServiceContextRef> connection_ref =
+      ref_factory_->CreateRef();
+  registry->AddConnectionLostClosure(
+      base::Bind(&OnConnectionLost, base::Passed(&connection_ref)));
+  registry->AddInterface(base::Bind(&FaceDetectionProviderImpl::Create));
+  return true;
+}
+
+bool ShapeDetectionService::OnStop() {
+  return true;
+}
+
+}  // namespace shape_detection
diff --git a/services/shape_detection/shape_detection_service.h b/services/shape_detection/shape_detection_service.h
new file mode 100644
index 0000000..b84125f
--- /dev/null
+++ b/services/shape_detection/shape_detection_service.h
@@ -0,0 +1,38 @@
+// 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 SERVICES_SHAPE_DETECTION_SHAPE_DETECTION_SERVICE_H_
+#define SERVICES_SHAPE_DETECTION_SHAPE_DETECTION_SERVICE_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace shape_detection {
+
+class ShapeDetectionService : public service_manager::Service {
+ public:
+  // Factory function for use as an embedded service.
+  static std::unique_ptr<service_manager::Service> Create();
+
+  ShapeDetectionService();
+  ~ShapeDetectionService() override;
+
+  void OnStart() override;
+  bool OnConnect(const service_manager::ServiceInfo& remote_info,
+                 service_manager::InterfaceRegistry* registry) override;
+  bool OnStop() override;
+
+ private:
+  std::unique_ptr<service_manager::ServiceContextRefFactory> ref_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShapeDetectionService);
+};
+
+}  // namespace shape_detection
+
+#endif  // SERVICES_SHAPE_DETECTION_SHAPE_DETECTION_SERVICE_H_
diff --git a/services/ui/clipboard/BUILD.gn b/services/ui/clipboard/BUILD.gn
index 7df28dc..758c303 100644
--- a/services/ui/clipboard/BUILD.gn
+++ b/services/ui/clipboard/BUILD.gn
@@ -5,6 +5,7 @@
 import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
 import("//testing/test.gni")
 
 source_set("lib") {
@@ -30,18 +31,16 @@
   ]
 }
 
-test("mus_clipboard_unittests") {
+service_test("mus_clipboard_unittests") {
   sources = [
     "clipboard_unittest.cc",
-    "run_all_unittests.cc",
   ]
 
+  catalog = ":mus_clipboard_unittests_catalog"
+
   deps = [
     "//base",
-    "//base/test:test_support",
     "//mojo/common",
-    "//mojo/edk/system",
-    "//services/catalog:lib",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/service_manager/public/cpp:sources",
     "//services/ui/public/interfaces",
@@ -49,7 +48,6 @@
 
   data_deps = [
     ":lib",
-    ":mus_clipboard_unittests_catalog_copy",
     "//services/ui",
   ]
 }
@@ -63,13 +61,3 @@
   embedded_services = [ ":test_manifest" ]
   standalone_services = [ "//services/ui:manifest" ]
 }
-
-copy("mus_clipboard_unittests_catalog_copy") {
-  sources = get_target_outputs(":mus_clipboard_unittests_catalog")
-  outputs = [
-    "${root_out_dir}/mus_clipboard_unittests_catalog.json",
-  ]
-  deps = [
-    ":mus_clipboard_unittests_catalog",
-  ]
-}
diff --git a/services/ui/clipboard/DEPS b/services/ui/clipboard/DEPS
deleted file mode 100644
index 03898ce..0000000
--- a/services/ui/clipboard/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-specific_include_rules = {
-  "run_all_unittests.cc": [
-    "+mojo/edk/embedder",
-    "+services/catalog",
-  ]
-}
diff --git a/services/ui/clipboard/run_all_unittests.cc b/services/ui/clipboard/run_all_unittests.cc
deleted file mode 100644
index fc89617..0000000
--- a/services/ui/clipboard/run_all_unittests.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/test_suite.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "services/catalog/catalog.h"
-
-namespace {
-
-const base::FilePath::CharType kCatalogFilename[] =
-    FILE_PATH_LITERAL("mus_clipboard_unittests_catalog.json");
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::TestSuite test_suite(argc, argv);
-
-  catalog::Catalog::LoadDefaultCatalogManifest(
-      base::FilePath(kCatalogFilename));
-
-  mojo::edk::Init();
-  base::Thread ipc_thread("IPC thread");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  return base::LaunchUnitTests(
-      argc, argv,
-      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
-}
diff --git a/services/ui/demo/BUILD.gn b/services/ui/demo/BUILD.gn
index 88278a3..43e89cf 100644
--- a/services/ui/demo/BUILD.gn
+++ b/services/ui/demo/BUILD.gn
@@ -5,6 +5,7 @@
 import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
 import("//testing/test.gni")
 
 source_set("lib") {
@@ -57,20 +58,18 @@
   source = "test_manifest.json"
 }
 
-test("mus_demo_unittests") {
+service_test("mus_demo_unittests") {
   testonly = true
 
   sources = [
     "mus_demo_unittests.cc",
-    "run_all_unittests.cc",
   ]
 
+  catalog = ":mus_demo_unittests_catalog"
+
   deps = [
     ":demo",
     "//base",
-    "//base/test:test_support",
-    "//mojo/edk/system",
-    "//services/catalog:lib",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/ui/public/interfaces",
@@ -79,7 +78,6 @@
 
   data_deps = [
     ":mus_demo",
-    ":mus_demo_unittests_catalog_copy",
   ]
 }
 
@@ -97,13 +95,3 @@
     "//services/ui:manifest",
   ]
 }
-
-copy("mus_demo_unittests_catalog_copy") {
-  sources = get_target_outputs(":mus_demo_unittests_catalog")
-  outputs = [
-    "${root_out_dir}/mus_demo_unittests_catalog.json",
-  ]
-  deps = [
-    ":mus_demo_unittests_catalog",
-  ]
-}
diff --git a/services/ui/demo/DEPS b/services/ui/demo/DEPS
index 3878391..c2c4696 100644
--- a/services/ui/demo/DEPS
+++ b/services/ui/demo/DEPS
@@ -5,10 +5,3 @@
   "+ui/aura_extra",
   "+ui/wm",
 ]
-
-specific_include_rules = {
-  "run_all_unittests.cc": [
-    "+mojo/edk/embedder",
-    "+services/catalog",
-  ]
-}
diff --git a/services/ui/ime/BUILD.gn b/services/ui/ime/BUILD.gn
index b19a0a98..7b771c7e 100644
--- a/services/ui/ime/BUILD.gn
+++ b/services/ui/ime/BUILD.gn
@@ -5,6 +5,7 @@
 import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
 import("//testing/test.gni")
 
 source_set("lib") {
@@ -32,18 +33,16 @@
   ]
 }
 
-test("mus_ime_unittests") {
+service_test("mus_ime_unittests") {
   sources = [
     "ime_unittest.cc",
-    "run_all_unittests.cc",
   ]
 
+  catalog = ":mus_ime_unittests_catalog"
+
   deps = [
     "//base",
-    "//base/test:test_support",
     "//mojo/common",
-    "//mojo/edk/system",
-    "//services/catalog:lib",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/service_manager/public/cpp:sources",
     "//services/ui/public/interfaces",
@@ -51,7 +50,6 @@
 
   data_deps = [
     ":lib",
-    ":mus_ime_unittests_catalog_copy",
     "//services/ui",
     "//services/ui/ime/test_ime_driver",
   ]
@@ -69,13 +67,3 @@
     "//services/ui/ime/test_ime_driver:manifest",
   ]
 }
-
-copy("mus_ime_unittests_catalog_copy") {
-  sources = get_target_outputs(":mus_ime_unittests_catalog")
-  outputs = [
-    "${root_out_dir}/mus_ime_unittests_catalog.json",
-  ]
-  deps = [
-    ":mus_ime_unittests_catalog",
-  ]
-}
diff --git a/services/ui/ime/DEPS b/services/ui/ime/DEPS
deleted file mode 100644
index 03898ce..0000000
--- a/services/ui/ime/DEPS
+++ /dev/null
@@ -1,6 +0,0 @@
-specific_include_rules = {
-  "run_all_unittests.cc": [
-    "+mojo/edk/embedder",
-    "+services/catalog",
-  ]
-}
diff --git a/services/ui/ime/run_all_unittests.cc b/services/ui/ime/run_all_unittests.cc
deleted file mode 100644
index 1a6b6588..0000000
--- a/services/ui/ime/run_all_unittests.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/test_suite.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "services/catalog/catalog.h"
-
-namespace {
-
-const base::FilePath::CharType kCatalogFilename[] =
-    FILE_PATH_LITERAL("mus_ime_unittests_catalog.json");
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::TestSuite test_suite(argc, argv);
-
-  catalog::Catalog::LoadDefaultCatalogManifest(
-      base::FilePath(kCatalogFilename));
-
-  mojo::edk::Init();
-  base::Thread ipc_thread("IPC thread");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  return base::LaunchUnitTests(
-      argc, argv,
-      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
-}
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index a8d8e199..123544a7 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -7,6 +7,7 @@
 import("//services/catalog/public/tools/catalog.gni")
 import("//services/service_manager/public/cpp/service.gni")
 import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
 
 static_library("lib") {
   sources = [
@@ -201,7 +202,7 @@
   }
 }
 
-test("mus_ws_unittests") {
+service_test("mus_ws_unittests") {
   sources = [
     "cursor_unittest.cc",
     "display_unittest.cc",
@@ -210,7 +211,6 @@
     "event_matcher_unittest.cc",
     "focus_controller_unittest.cc",
     "frame_generator_unittest.cc",
-    "run_all_unittests.cc",
     "server_window_compositor_frame_sink_manager_test_api.cc",
     "server_window_compositor_frame_sink_manager_test_api.h",
     "server_window_drawn_tracker_unittest.cc",
@@ -229,6 +229,8 @@
     "window_tree_unittest.cc",
   ]
 
+  catalog = ":mus_ws_unittests_catalog"
+
   deps = [
     ":lib",
     ":test_support",
@@ -237,9 +239,7 @@
     "//base/test:test_support",
     "//cc:cc",
     "//gpu/ipc/client",
-    "//mojo/edk/system",
     "//mojo/public/cpp/bindings:bindings",
-    "//services/catalog:lib",
     "//services/service_manager/public/cpp:service_test_support",
     "//services/service_manager/public/cpp:sources",
     "//services/service_manager/public/interfaces",
@@ -258,10 +258,6 @@
     "//ui/gfx/geometry/mojo",
     "//ui/gl",
   ]
-
-  data_deps = [
-    ":mus_ws_unittests_catalog_copy",
-  ]
 }
 
 service_manifest("mus_ws_unittests_app_manifest") {
@@ -274,13 +270,3 @@
 
   standalone_services = [ "//services/ui:manifest" ]
 }
-
-copy("mus_ws_unittests_catalog_copy") {
-  sources = get_target_outputs(":mus_ws_unittests_catalog")
-  outputs = [
-    "${root_out_dir}/mus_ws_unittests_catalog.json",
-  ]
-  deps = [
-    ":mus_ws_unittests_catalog",
-  ]
-}
diff --git a/services/ui/ws/DEPS b/services/ui/ws/DEPS
index a04db9e1..f771ac8 100644
--- a/services/ui/ws/DEPS
+++ b/services/ui/ws/DEPS
@@ -11,8 +11,4 @@
   "gpu_host.h": [
     "+services/ui/gpu/gpu_main.h",
   ],
-  "run_all_unittests.cc": [
-    "+mojo/edk/embedder",
-    "+services/catalog",
-  ]
 }
diff --git a/services/ui/ws/run_all_unittests.cc b/services/ui/ws/run_all_unittests.cc
deleted file mode 100644
index 4ee1ea5..0000000
--- a/services/ui/ws/run_all_unittests.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/test/launcher/unit_test_launcher.h"
-#include "base/test/test_suite.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "services/catalog/catalog.h"
-
-namespace {
-
-const base::FilePath::CharType kCatalogFilename[] =
-    FILE_PATH_LITERAL("mus_ws_unittests_catalog.json");
-
-}  // namespace
-
-int main(int argc, char** argv) {
-  base::TestSuite test_suite(argc, argv);
-
-  catalog::Catalog::LoadDefaultCatalogManifest(
-      base::FilePath(kCatalogFilename));
-
-  mojo::edk::Init();
-  base::Thread ipc_thread("IPC thread");
-  ipc_thread.StartWithOptions(
-      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
-  mojo::edk::ScopedIPCSupport ipc_support(
-      ipc_thread.task_runner(),
-      mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
-  return base::LaunchUnitTests(
-      argc, argv,
-      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
-}
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 985e8e5f..50f44c7 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -768,7 +768,6 @@
 crbug.com/446385 [ Win7 Debug ] http/tests/xmlhttprequest/xmlhttprequest-json-response-overflow.html [ Crash Pass Timeout ]
 crbug.com/446385 [ Win7 Debug ] virtual/mojo-loading/http/tests/xmlhttprequest/xmlhttprequest-json-response-overflow.html [ Crash Pass Timeout ]
 crbug.com/248938 virtual/threaded/animations/dynamic-stylesheet-loading.html [ Pass Failure Timeout ]
-crbug.com/248938 virtual/threaded/transitions/change-duration-during-transition.html [ Pass Failure ]
 crbug.com/638693 virtual/threaded/animations/display-inline-style-adjust.html [ Pass Crash Failure ]
 crbug.com/421283 fast/html/marquee-scrollamount.html [ Pass Failure ]
 
@@ -2336,3 +2335,12 @@
 
 # CQ and Rebaseline-cl for crrev.com/2626973005 does not include this
 crbug.com/680407 [ Mac ] fast/forms/select/menulist-appearance-basic.html [ NeedsManualRebaseline ]
+
+# No mock Mojo interface for services yet
+crbug.com/659139 [ Mac ] http/tests/shapedetection/shapedetection-cross-origin.html [ Skip ]
+crbug.com/659139 [ Mac ] shapedetection/detection-HTMLCanvasElement.html [ Skip ]
+crbug.com/659139 [ Mac ] shapedetection/detection-HTMLImageElement.html [ Skip ]
+crbug.com/659139 [ Mac ] shapedetection/detection-HTMLVideoElement.html [ Skip ]
+crbug.com/659139 [ Mac ] shapedetection/detection-ImageBitmap.html [ Skip ]
+crbug.com/659139 [ Mac ] shapedetection/detection-ImageData.html [ Skip ]
+crbug.com/659139 [ Mac ] shapedetection/detection-options.html [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation-expected.txt
new file mode 100644
index 0000000..673a815
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation-expected.txt
@@ -0,0 +1,148 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js. 
+FAIL CSS Transitions: property <--color> from neutral to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 255 , 255 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (0) is [green] assert_equals: expected "rgb ( 255 , 255 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (0.3) is [green] assert_equals: expected "rgb ( 179 , 217 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (0.6) is [green] assert_equals: expected "rgb ( 102 , 179 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from neutral to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 65 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (0) is [green] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 0 , 38 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 0 , 77 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [initial] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 0 , 0 , 255 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (0) is [green] assert_equals: expected "rgb ( 0 , 0 , 255 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 0 , 38 , 179 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 0 , 77 , 102 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [inherit] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (0) is [green] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 0 , 38 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 0 , 77 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [unset] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (-0.3) is [orange] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (0) is [orange] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (0.3) is [orange] assert_equals: expected "rgb ( 77 , 50 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (0.6) is [orange] assert_equals: expected "rgb ( 153 , 99 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (1) is [orange] assert_equals: expected "rgb ( 255 , 165 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [orange] at (1.5) is [orange] assert_equals: expected "rgb ( 255 , 248 , 0 ) " but got "orange "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (-0.3) is [currentcolor] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (0) is [currentcolor] assert_equals: expected "rgb ( 0 , 0 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (0.3) is [currentcolor] assert_equals: expected "rgb ( 0 , 77 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (0.6) is [currentcolor] assert_equals: expected "rgb ( 0 , 153 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (1) is [currentcolor] assert_equals: expected "rgb ( 0 , 255 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [black] to [currentcolor] at (1.5) is [currentcolor] assert_equals: expected "rgb ( 0 , 255 , 0 ) " but got "currentcolor "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 255 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (0) is [green] assert_equals: expected "rgb ( 255 , 0 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 179 , 38 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 102 , 77 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-activelink] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (-0.3) is [green] assert_equals: expected "rgb ( 0 , 0 , 255 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (0) is [green] assert_equals: expected "rgb ( 0 , 0 , 255 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (0.3) is [green] assert_equals: expected "rgb ( 0 , 38 , 179 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (0.6) is [green] assert_equals: expected "rgb ( 0 , 77 , 102 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (1) is [green] assert_equals: expected "rgb ( 0 , 128 , 0 ) " but got "green "
+FAIL CSS Transitions: property <--color> from [-webkit-link] to [green] at (1.5) is [green] assert_equals: expected "rgb ( 0 , 192 , 0 ) " but got "green "
+PASS CSS Animations: property <--color> from neutral to [green] at (-0.3) is [rgb(255, 255, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (0) is [rgb(255, 255, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (0.3) is [rgb(179, 217, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (0.6) is [rgb(102, 179, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from neutral to [green] at (1.5) is [rgb(0, 65, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (-0.3) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (0) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (0.3) is [rgb(0, 38, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (0.6) is [rgb(0, 77, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [initial] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (-0.3) is [rgb(0, 0, 255)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (0) is [rgb(0, 0, 255)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (0.3) is [rgb(0, 38, 179)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (0.6) is [rgb(0, 77, 102)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [inherit] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (-0.3) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (0) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (0.3) is [rgb(0, 38, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (0.6) is [rgb(0, 77, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [unset] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (-0.3) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (0) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (0.3) is [rgb(77, 50, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (0.6) is [rgb(153, 99, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (1) is [rgb(255, 165, 0)] 
+PASS CSS Animations: property <--color> from [black] to [orange] at (1.5) is [rgb(255, 248, 0)] 
+PASS CSS Animations: property <--color> from [black] to [currentcolor] at (-0.3) is [rgb(0, 0, 0)] 
+PASS CSS Animations: property <--color> from [black] to [currentcolor] at (0) is [rgb(0, 0, 0)] 
+FAIL CSS Animations: property <--color> from [black] to [currentcolor] at (0.3) is [rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 77 , 0 ) " but got "rgb ( 0 , 0 , 0 ) "
+FAIL CSS Animations: property <--color> from [black] to [currentcolor] at (0.6) is [rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 153 , 0 ) " but got "rgb ( 0 , 0 , 0 ) "
+FAIL CSS Animations: property <--color> from [black] to [currentcolor] at (1) is [rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 255 , 0 ) " but got "rgb ( 0 , 0 , 0 ) "
+FAIL CSS Animations: property <--color> from [black] to [currentcolor] at (1.5) is [rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 255 , 0 ) " but got "rgb ( 0 , 0 , 0 ) "
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (-0.3) is [rgb(255, 0, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (0) is [rgb(255, 0, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (0.3) is [rgb(179, 38, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (0.6) is [rgb(102, 77, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-activelink] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (-0.3) is [rgb(0, 0, 255)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (0) is [rgb(0, 0, 255)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (0.3) is [rgb(0, 38, 179)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (0.6) is [rgb(0, 77, 102)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS CSS Animations: property <--color> from [-webkit-link] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (-0.3) is [rgb(255, 255, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (0) is [rgb(255, 255, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (0.3) is [rgb(179, 217, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (0.6) is [rgb(102, 179, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from neutral to [green] at (1.5) is [rgb(0, 65, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (-0.3) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (0) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (0.3) is [rgb(0, 38, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (0.6) is [rgb(0, 77, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [initial] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (-0.3) is [rgb(0, 0, 255)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (0) is [rgb(0, 0, 255)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (0.3) is [rgb(0, 38, 179)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (0.6) is [rgb(0, 77, 102)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [inherit] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (-0.3) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (0) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (0.3) is [rgb(0, 38, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (0.6) is [rgb(0, 77, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [unset] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (-0.3) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (0) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (0.3) is [rgb(77, 50, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (0.6) is [rgb(153, 99, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (1) is [rgb(255, 165, 0)] 
+PASS Web Animations: property <--color> from [black] to [orange] at (1.5) is [rgb(255, 248, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (-0.3) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (0) is [rgb(0, 0, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (0.3) is [rgb(0, 77, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (0.6) is [rgb(0, 153, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (1) is [rgb(0, 255, 0)] 
+PASS Web Animations: property <--color> from [black] to [currentcolor] at (1.5) is [rgb(0, 255, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (-0.3) is [rgb(255, 0, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (0) is [rgb(255, 0, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (0.3) is [rgb(179, 38, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (0.6) is [rgb(102, 77, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [-webkit-activelink] to [green] at (1.5) is [rgb(0, 192, 0)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (-0.3) is [rgb(0, 0, 255)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (0) is [rgb(0, 0, 255)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (0.3) is [rgb(0, 38, 179)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (0.6) is [rgb(0, 77, 102)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (1) is [rgb(0, 128, 0)] 
+PASS Web Animations: property <--color> from [-webkit-link] to [green] at (1.5) is [rgb(0, 192, 0)] 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation.html
new file mode 100644
index 0000000..5d60922
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/color-type-interpolation.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --color: blue;
+}
+.target {
+  --color: yellow;
+  color: lime;
+}
+</style>
+<body alink="red" link="blue">
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--color',
+  syntax: '<color>',
+  initialValue: 'black',
+});
+
+assertInterpolation({
+  property: '--color',
+  from: neutralKeyframe,
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(255, 255, 0)'},
+  {at: 0, is: 'rgb(255, 255, 0)'},
+  {at: 0.3, is: 'rgb(179, 217, 0)'},
+  {at: 0.6, is: 'rgb(102, 179, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 65, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'initial',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(0, 38, 0)'},
+  {at: 0.6, is: 'rgb(0, 77, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'inherit',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(0, 0, 255)'},
+  {at: 0.3, is: 'rgb(0, 38, 179)'},
+  {at: 0.6, is: 'rgb(0, 77, 102)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'unset',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(0, 38, 0)'},
+  {at: 0.6, is: 'rgb(0, 77, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'black',
+  to: 'orange',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(77, 50, 0)'},
+  {at: 0.6, is: 'rgb(153, 99, 0)'},
+  {at: 1, is: 'rgb(255, 165, 0)'},
+  {at: 1.5, is: 'rgb(255, 248, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: 'black',
+  to: 'currentcolor',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 0)'},
+  {at: 0, is: 'rgb(0, 0, 0)'},
+  {at: 0.3, is: 'rgb(0, 77, 0)'},
+  {at: 0.6, is: 'rgb(0, 153, 0)'},
+  {at: 1, is: 'rgb(0, 255, 0)'},
+  {at: 1.5, is: 'rgb(0, 255, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: '-webkit-activelink',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(255, 0, 0)'},
+  {at: 0, is: 'rgb(255, 0, 0)'},
+  {at: 0.3, is: 'rgb(179, 38, 0)'},
+  {at: 0.6, is: 'rgb(102, 77, 0)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color',
+  from: '-webkit-link',
+  to: 'green',
+}, [
+  {at: -0.3, is: 'rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(0, 0, 255)'},
+  {at: 0.3, is: 'rgb(0, 38, 179)'},
+  {at: 0.6, is: 'rgb(0, 77, 102)'},
+  {at: 1, is: 'rgb(0, 128, 0)'},
+  {at: 1.5, is: 'rgb(0, 192, 0)'},
+]);
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/empty-initial-value-crash.html b/third_party/WebKit/LayoutTests/animations/custom-properties/empty-initial-value-crash.html
new file mode 100644
index 0000000..efd5e5d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/empty-initial-value-crash.html
@@ -0,0 +1,31 @@
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<style>
+#target {
+  --x: initial;
+}
+</style>
+<div id="target"></div>
+<script>
+setup(() => {
+  CSS.registerProperty({name: '--x'});
+});
+
+test(() => {
+  var animation = target.animate({'--x': 'test'}, 1);
+  assert_equals(getComputedStyle(target).getPropertyValue('--x'), '');
+  animation.cancel();
+}, 'Do not crash when animating with an underlying value of initial');
+
+test(() => {
+  var animation = target.animate({'--x': 'initial'}, {fill: 'forwards'});
+  assert_equals(getComputedStyle(target).getPropertyValue('--x'), '');
+  animation.cancel();
+}, 'Do not crash when animating an empty initial value');
+
+test(() => {
+  var animation = target.animate({'--x': 'inherit'}, {fill: 'forwards'});
+  assert_equals(getComputedStyle(target).getPropertyValue('--x'), '');
+  animation.cancel();
+}, 'Do not crash when animating an inherited empty initial value');
+</script>
diff --git a/third_party/WebKit/LayoutTests/animations/interpolation/resources/interpolation-test.js b/third_party/WebKit/LayoutTests/animations/interpolation/resources/interpolation-test.js
index 1fe9ee9..4539a67 100644
--- a/third_party/WebKit/LayoutTests/animations/interpolation/resources/interpolation-test.js
+++ b/third_party/WebKit/LayoutTests/animations/interpolation/resources/interpolation-test.js
@@ -102,7 +102,7 @@
     supportsProperty: function() {return true;},
     supportsValue: function() {return true;},
     setup: function(property, from, target) {
-      target.style[property] = isNeutralKeyframe(from) ? '' : from;
+      target.style.setProperty(property, isNeutralKeyframe(from) ? '' : from);
     },
     nonInterpolationExpectations: function(from, to) {
       return expectFlip(from, to, -Infinity);
@@ -112,7 +112,7 @@
       target.style.transitionDelay = '-1e10s';
       target.style.transitionTimingFunction = createEasing(at);
       target.style.transitionProperty = property;
-      target.style[property] = isNeutralKeyframe(to) ? '' : to;
+      target.style.setProperty(property, isNeutralKeyframe(to) ? '' : to);
     },
     rebaseline: false,
   };
@@ -129,10 +129,12 @@
       this.interpolateComposite(property, from, 'replace', to, 'replace', at, target);
     },
     interpolateComposite: function(property, from, fromComposite, to, toComposite, at, target) {
-      // Convert to camelCase
-      for (var i = property.length - 2; i > 0; --i) {
-        if (property[i] === '-') {
-          property = property.substring(0, i) + property[i + 1].toUpperCase() + property.substring(i + 2);
+      // Convert standard properties to camelCase.
+      if (!property.startsWith('--')) {
+        for (var i = property.length - 2; i > 0; --i) {
+          if (property[i] === '-') {
+            property = property.substring(0, i) + property[i + 1].toUpperCase() + property.substring(i + 2);
+          }
         }
       }
       var keyframes = [];
@@ -312,18 +314,18 @@
     return expectations.map(function(expectation) {
       var actualTargetContainer = createTargetContainer(testContainer, 'actual');
       var expectedTargetContainer = createTargetContainer(testContainer, 'expected');
-      expectedTargetContainer.target.style[property] = expectation.is;
+      expectedTargetContainer.target.style.setProperty(property, expectation.is);
       var target = actualTargetContainer.target;
       interpolationMethod.setup(property, from, target);
       target.interpolate = function() {
         interpolationMethod.interpolate(property, from, to, expectation.at, target);
       };
       target.measure = function() {
-        var actualValue = getComputedStyle(target)[property];
+        var actualValue = getComputedStyle(target).getPropertyValue(property);
         test(function() {
           assert_equals(
             normalizeValue(actualValue),
-            normalizeValue(getComputedStyle(expectedTargetContainer.target)[property]));
+            normalizeValue(getComputedStyle(expectedTargetContainer.target).getPropertyValue(property)));
         }, `${testText} at (${expectation.at}) is [${sanitizeUrls(actualValue)}]`);
         if (rebaselineExpectation) {
           rebaselineExpectation.textContent += `  {at: ${expectation.at}, is: '${actualValue}'},\n`;
@@ -369,18 +371,18 @@
     return compositionTest.expectations.map(function(expectation) {
       var actualTargetContainer = createTargetContainer(testContainer, 'actual');
       var expectedTargetContainer = createTargetContainer(testContainer, 'expected');
-      expectedTargetContainer.target.style[property] = expectation.is;
+      expectedTargetContainer.target.style.setProperty(property, expectation.is);
       var target = actualTargetContainer.target;
-      target.style[property] = underlying;
+      target.style.setProperty(property, underlying);
       target.interpolate = function() {
         webAnimationsInterpolation.interpolateComposite(property, from, fromComposite, to, toComposite, expectation.at, target);
       };
       target.measure = function() {
-        var actualValue = getComputedStyle(target)[property];
+        var actualValue = getComputedStyle(target).getPropertyValue(property);
         test(function() {
           assert_equals(
             normalizeValue(actualValue),
-            normalizeValue(getComputedStyle(expectedTargetContainer.target)[property]));
+            normalizeValue(getComputedStyle(expectedTargetContainer.target).getPropertyValue(property)));
         }, `${testText} at (${expectation.at}) is [${sanitizeUrls(actualValue)}]`);
         if (rebaselineExpectation) {
           rebaselineExpectation.textContent += `  {at: ${expectation.at}, is: '${actualValue}'},\n`;
diff --git a/third_party/WebKit/LayoutTests/animations/responsive-neutral-keyframe.html b/third_party/WebKit/LayoutTests/animations/responsive-neutral-keyframe.html
index 4ce193b55b..efa2197 100644
--- a/third_party/WebKit/LayoutTests/animations/responsive-neutral-keyframe.html
+++ b/third_party/WebKit/LayoutTests/animations/responsive-neutral-keyframe.html
@@ -4,14 +4,20 @@
 if (window.testRunner)
   testRunner.waitUntilDone();
 
+function waitForCompositor() {
+  return document.body.animate({opacity: [1, 1]}, 1).finished;
+}
+
 target.animate({translate: '100px'}, 1e8);
 
 requestAnimationFrame(() => {
   requestAnimationFrame(() => {
     target.style.translate = '100px';
     requestAnimationFrame(() => {
-      if (window.testRunner)
-        testRunner.notifyDone();
+      waitForCompositor().then(() => {
+        if (window.testRunner)
+          testRunner.notifyDone();
+      });
     });
   });
 });
diff --git a/third_party/WebKit/LayoutTests/animations/state-at-end-event.html b/third_party/WebKit/LayoutTests/animations/state-at-end-event.html
index 330438f4..9c961e0 100644
--- a/third_party/WebKit/LayoutTests/animations/state-at-end-event.html
+++ b/third_party/WebKit/LayoutTests/animations/state-at-end-event.html
@@ -46,6 +46,7 @@
         testRunner.waitUntilDone();
 
       document.getElementById('tester').addEventListener('animationend', testEnded, false);
+      document.getElementById('container').offsetTop; // Force style recalc
       document.getElementById('container').className = 'moved';
     }
     
diff --git a/third_party/WebKit/LayoutTests/animations/viewport-unit-animation-responsive.html b/third_party/WebKit/LayoutTests/animations/viewport-unit-animation-responsive.html
index dbb751d..04fc896 100644
--- a/third_party/WebKit/LayoutTests/animations/viewport-unit-animation-responsive.html
+++ b/third_party/WebKit/LayoutTests/animations/viewport-unit-animation-responsive.html
@@ -15,7 +15,7 @@
   testRunner.waitUntilDone();
 }
 function waitForCompositor() {
-  return document.body.animate({opacity: [1, 1]}, 1).ready;
+  return document.body.animate({opacity: [1, 1]}, 1).finished;
 }
 
 var position = 'translate(calc(50vw - 50px), calc(50vh - 50px))';
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
index 4f7e8a7..bfaa22f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/timeline-test.js
@@ -233,10 +233,10 @@
 
 InspectorTest.printTimestampRecords = function(typeName)
 {
-    const records = InspectorTest.timelineModel().eventDividerRecords();
-    for (let record of records) {
-        if (record.type() === typeName)
-            InspectorTest.printTraceEventProperties(record.traceEvent());
+    var dividers = InspectorTest.timelineModel().eventDividers();
+    for (var event of dividers) {
+        if (event.name === typeName)
+            InspectorTest.printTraceEventProperties(event);
     }
 };
 
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-load-event.html b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-load-event.html
index 6259b29..ab12d0e 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-load-event.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-load-event.html
@@ -42,9 +42,9 @@
         InspectorTest.printTimestampRecords("MarkLoad");
         InspectorTest.printTimestampRecords("MarkFirstPaint");
 
-        var eventDividers = InspectorTest.timelineModel().eventDividerRecords();
+        var eventDividers = InspectorTest.timelineModel().eventDividers();
         for (var i = 1; i < eventDividers.length; ++i)
-            InspectorTest.assertGreaterOrEqual(eventDividers[i], eventDividers[i - 1], "Event divider timestamps should be monotonically non-decreasing");
+            InspectorTest.assertGreaterOrEqual(eventDividers[i].startTime, eventDividers[i - 1].startTime, "Event divider timestamps should be monotonically non-decreasing");
 
         InspectorTest.completeTest();
     }
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-time/timeline-timer-fired-from-eval-call-site.html b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-time/timeline-timer-fired-from-eval-call-site.html
index e11b4e5..8f65114 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-time/timeline-timer-fired-from-eval-call-site.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-time/timeline-timer-fired-from-eval-call-site.html
@@ -33,7 +33,7 @@
     function finish()
     {
         var events = InspectorTest.timelineModel().mainThreadEvents();
-        for (let i = 0; i < events.length; ++i) {
+        for (var i = 0; i < events.length; ++i) {
             if (events[i].name !== TimelineModel.TimelineModel.RecordType.TimerFire)
                 continue;
             var functionCallChild = InspectorTest.findChildEvent(events, i, TimelineModel.TimelineModel.RecordType.FunctionCall);
diff --git a/third_party/WebKit/LayoutTests/transitions/change-duration-during-transition-expected.txt b/third_party/WebKit/LayoutTests/transitions/change-duration-during-transition-expected.txt
deleted file mode 100644
index 7ba7a94c..0000000
--- a/third_party/WebKit/LayoutTests/transitions/change-duration-during-transition-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Warning this test is running in real-time and may be flaky.
-PASS - "left" property for "box" element at 0.5s saw something close to: 50
-PASS - "left" property for "box" element at 0.8s saw something close to: 0
-
diff --git a/third_party/WebKit/LayoutTests/transitions/change-duration-during-transition.html b/third_party/WebKit/LayoutTests/transitions/change-duration-during-transition.html
index c30a61d..de4df63 100644
--- a/third_party/WebKit/LayoutTests/transitions/change-duration-during-transition.html
+++ b/third_party/WebKit/LayoutTests/transitions/change-duration-during-transition.html
@@ -1,7 +1,9 @@
 <!DOCTYPE html>
-
 <html>
 <head>
+  <meta charset="utf-8">
+  <script src="../resources/testharness.js"></script>
+  <script src="../resources/testharnessreport.js"></script>
   <style>
     #box {
       position: absolute;
@@ -10,39 +12,31 @@
       left: 0px;
       background-color: blue;
       transition-duration: 1s;
+      transition-delay: -0.75s;
       transition-timing-function: linear;
       transition-property: left;
     }
   </style>
-  <script src="../animations/resources/animation-test-helpers.js" type="text/javascript"></script>
-  <script>
-    const expectedValues = [
-      // [time, element-id, property, expected-value, tolerance]
-      [0.5, 'box', 'left', 50, 10],
-      [0.8, 'box', 'left', 0, 0],
-    ];
-
-    function changeDuration() {
-      box.style.transitionDuration = "0.1s";
-    }
-
-    function goBack() {
-      box.style.left = "0px";
-    }
-
-    const callbacks = {
-      0.1: changeDuration,
-      0.6: goBack,
-    }
-
-    function setupTest() {
-      box.style.left = '100px';
-    }
-
-    runTransitionTest(expectedValues, setupTest, callbacks, false, true);
-  </script>
 </head>
 <body>
   <div id="box"></div>
+  <script>
+    'use strict';
+
+    async_test(t => {
+      t.step_func(() => {
+        box.offsetTop; // Force style recalc
+        box.style.left = '400px';
+        assert_equals(getComputedStyle(box).left, '300px', 'The transition progresses 75% immediately due to negative transition-delay');
+        box.style.transitionDuration = '7.5s';
+        assert_equals(getComputedStyle(box).left, '300px', 'Changing the duration does not affect the current transition');
+      })();
+      box.addEventListener('transitionend', t.step_func_done(() => {
+        assert_equals(getComputedStyle(box).left, '400px', 'The final value has been reached when transitionend fires');
+        box.style.left = '1400px';
+        assert_equals(getComputedStyle(box).left, '500px', 'With the new duration taking effect, the transition progresses 10% immediately');
+      }));
+    }, 'Transition duration change should not affect transition in progress');
+  </script>
 </body>
 </html>
diff --git a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
index 0a3d410..ef834b99 100644
--- a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
+++ b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.cpp
@@ -265,4 +265,13 @@
           cssProperty() == CSSPropertyTextDecorationColor));
 }
 
+const CSSValue* CSSColorInterpolationType::createCSSValue(
+    const InterpolableValue& interpolableValue,
+    const NonInterpolableValue*,
+    const StyleResolverState& state) const {
+  const InterpolableList& colorPair = toInterpolableList(interpolableValue);
+  Color color = resolveInterpolableColor(*colorPair.get(Unvisited), state);
+  return CSSColorValue::create(color.rgb());
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.h b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.h
index 2d5c0eeb..505e201 100644
--- a/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.h
+++ b/third_party/WebKit/Source/core/animation/CSSColorInterpolationType.h
@@ -50,6 +50,10 @@
                                        ConversionCheckers&) const final;
   InterpolationValue convertStyleColorPair(const StyleColor&,
                                            const StyleColor&) const;
+
+  const CSSValue* createCSSValue(const InterpolableValue&,
+                                 const NonInterpolableValue*,
+                                 const StyleResolverState&) const final;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp
index c15db7d..dcb67ea 100644
--- a/third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp
+++ b/third_party/WebKit/Source/core/animation/CSSInterpolationType.cpp
@@ -87,6 +87,12 @@
     if (!inheritedValue) {
       inheritedValue = m_initialValue.get();
     }
+    if (inheritedValue == m_inheritedValue.get()) {
+      return true;
+    }
+    if (!inheritedValue || !m_inheritedValue) {
+      return false;
+    }
     return m_inheritedValue->equals(*inheritedValue);
   }
 
@@ -207,20 +213,23 @@
       return nullptr;
     }
 
+    const CSSValue* value = nullptr;
     if (declaration.isInitial(isInheritedProperty)) {
-      return maybeConvertValue(*registration->initial(), state,
-                               conversionCheckers);
+      value = registration->initial();
+    } else {
+      const CSSValue* value =
+          state.parentStyle()->getRegisteredVariable(name, isInheritedProperty);
+      if (!value) {
+        value = registration->initial();
+      }
+      conversionCheckers.push_back(InheritedCustomPropertyChecker::create(
+          name, isInheritedProperty, value, registration->initial()));
+    }
+    if (!value) {
+      return nullptr;
     }
 
-    DCHECK(declaration.isInherit(isInheritedProperty));
-    const CSSValue* inheritedValue =
-        state.parentStyle()->getRegisteredVariable(name, isInheritedProperty);
-    if (!inheritedValue) {
-      inheritedValue = registration->initial();
-    }
-    conversionCheckers.push_back(InheritedCustomPropertyChecker::create(
-        name, isInheritedProperty, inheritedValue, registration->initial()));
-    return maybeConvertValue(*inheritedValue, state, conversionCheckers);
+    return maybeConvertValue(*value, state, conversionCheckers);
   }
 
   if (declaration.value()->needsVariableResolution()) {
@@ -263,6 +272,9 @@
   if (!underlyingValue) {
     underlyingValue = registration->initial();
   }
+  if (!underlyingValue) {
+    return nullptr;
+  }
   // TODO(alancutter): Remove the need for passing in conversion checkers.
   ConversionCheckers dummyConversionCheckers;
   return maybeConvertValue(*underlyingValue, environment.state(),
diff --git a/third_party/WebKit/Source/core/animation/EffectModel.h b/third_party/WebKit/Source/core/animation/EffectModel.h
index 58b722f..0966cccb 100644
--- a/third_party/WebKit/Source/core/animation/EffectModel.h
+++ b/third_party/WebKit/Source/core/animation/EffectModel.h
@@ -58,7 +58,7 @@
                       double iterationDuration,
                       Vector<RefPtr<Interpolation>>&) const = 0;
 
-  virtual bool affects(PropertyHandle) const { return false; }
+  virtual bool affects(const PropertyHandle&) const { return false; }
   virtual bool isTransformRelatedEffect() const { return false; }
   virtual bool isKeyframeEffectModel() const { return false; }
 
diff --git a/third_party/WebKit/Source/core/animation/Interpolation.h b/third_party/WebKit/Source/core/animation/Interpolation.h
index 8adbf33..1404f4cb 100644
--- a/third_party/WebKit/Source/core/animation/Interpolation.h
+++ b/third_party/WebKit/Source/core/animation/Interpolation.h
@@ -28,7 +28,7 @@
   virtual bool isInvalidatableInterpolation() const { return false; }
   virtual bool isLegacyStyleInterpolation() const { return false; }
 
-  virtual PropertyHandle getProperty() const = 0;
+  virtual const PropertyHandle& getProperty() const = 0;
   virtual bool dependsOnUnderlyingValue() const { return false; }
 
  protected:
diff --git a/third_party/WebKit/Source/core/animation/InterpolationEffect.cpp b/third_party/WebKit/Source/core/animation/InterpolationEffect.cpp
index 55a57395..ae12c262 100644
--- a/third_party/WebKit/Source/core/animation/InterpolationEffect.cpp
+++ b/third_party/WebKit/Source/core/animation/InterpolationEffect.cpp
@@ -36,7 +36,7 @@
 }
 
 void InterpolationEffect::addInterpolationsFromKeyframes(
-    PropertyHandle property,
+    const PropertyHandle& property,
     const Keyframe::PropertySpecificKeyframe& keyframeA,
     const Keyframe::PropertySpecificKeyframe& keyframeB,
     double applyFrom,
diff --git a/third_party/WebKit/Source/core/animation/InterpolationEffect.h b/third_party/WebKit/Source/core/animation/InterpolationEffect.h
index 392fb4a..dfb926b 100644
--- a/third_party/WebKit/Source/core/animation/InterpolationEffect.h
+++ b/third_party/WebKit/Source/core/animation/InterpolationEffect.h
@@ -46,7 +46,7 @@
   }
 
   void addInterpolationsFromKeyframes(
-      PropertyHandle,
+      const PropertyHandle&,
       const Keyframe::PropertySpecificKeyframe& keyframeA,
       const Keyframe::PropertySpecificKeyframe& keyframeB,
       double applyFrom,
diff --git a/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.h b/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.h
index 2fb90cc..c017104 100644
--- a/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.h
+++ b/third_party/WebKit/Source/core/animation/InvalidatableInterpolation.h
@@ -20,14 +20,14 @@
 class CORE_EXPORT InvalidatableInterpolation : public Interpolation {
  public:
   static PassRefPtr<InvalidatableInterpolation> create(
-      PropertyHandle property,
+      const PropertyHandle& property,
       PassRefPtr<PropertySpecificKeyframe> startKeyframe,
       PassRefPtr<PropertySpecificKeyframe> endKeyframe) {
     return adoptRef(new InvalidatableInterpolation(
         property, std::move(startKeyframe), std::move(endKeyframe)));
   }
 
-  PropertyHandle getProperty() const final { return m_property; }
+  const PropertyHandle& getProperty() const final { return m_property; }
   virtual void interpolate(int iteration, double fraction);
   bool dependsOnUnderlyingValue() const final;
   static void applyStack(const ActiveInterpolations&,
@@ -36,7 +36,7 @@
   virtual bool isInvalidatableInterpolation() const { return true; }
 
  private:
-  InvalidatableInterpolation(PropertyHandle property,
+  InvalidatableInterpolation(const PropertyHandle& property,
                              PassRefPtr<PropertySpecificKeyframe> startKeyframe,
                              PassRefPtr<PropertySpecificKeyframe> endKeyframe)
       : Interpolation(),
diff --git a/third_party/WebKit/Source/core/animation/Keyframe.cpp b/third_party/WebKit/Source/core/animation/Keyframe.cpp
index bf504e4a..fe7fe0e 100644
--- a/third_party/WebKit/Source/core/animation/Keyframe.cpp
+++ b/third_party/WebKit/Source/core/animation/Keyframe.cpp
@@ -10,7 +10,7 @@
 
 PassRefPtr<Interpolation>
 Keyframe::PropertySpecificKeyframe::createInterpolation(
-    PropertyHandle propertyHandle,
+    const PropertyHandle& propertyHandle,
     const Keyframe::PropertySpecificKeyframe& end) const {
   // const_cast to take refs.
   return InvalidatableInterpolation::create(
diff --git a/third_party/WebKit/Source/core/animation/Keyframe.h b/third_party/WebKit/Source/core/animation/Keyframe.h
index bbafe93..9a70eb90f 100644
--- a/third_party/WebKit/Source/core/animation/Keyframe.h
+++ b/third_party/WebKit/Source/core/animation/Keyframe.h
@@ -98,7 +98,7 @@
         double offset,
         PassRefPtr<TimingFunction> easing) const = 0;
     virtual PassRefPtr<Interpolation> createInterpolation(
-        PropertyHandle,
+        const PropertyHandle&,
         const Keyframe::PropertySpecificKeyframe& end) const;
 
    protected:
@@ -112,7 +112,7 @@
   };
 
   virtual PassRefPtr<PropertySpecificKeyframe> createPropertySpecificKeyframe(
-      PropertyHandle) const = 0;
+      const PropertyHandle&) const = 0;
 
  protected:
   Keyframe()
diff --git a/third_party/WebKit/Source/core/animation/KeyframeEffectModel.h b/third_party/WebKit/Source/core/animation/KeyframeEffectModel.h
index ac34919..a92b608 100644
--- a/third_party/WebKit/Source/core/animation/KeyframeEffectModel.h
+++ b/third_party/WebKit/Source/core/animation/KeyframeEffectModel.h
@@ -83,7 +83,7 @@
   void setFrames(KeyframeVector& keyframes);
 
   const PropertySpecificKeyframeVector& getPropertySpecificKeyframes(
-      PropertyHandle property) const {
+      const PropertyHandle& property) const {
     ensureKeyframeGroups();
     return m_keyframeGroups->get(property)->keyframes();
   }
@@ -128,7 +128,7 @@
     return normalizedKeyframes(keyframes);
   }
 
-  bool affects(PropertyHandle property) const override {
+  bool affects(const PropertyHandle& property) const override {
     ensureKeyframeGroups();
     return m_keyframeGroups->contains(property);
   }
diff --git a/third_party/WebKit/Source/core/animation/LegacyStyleInterpolation.cpp b/third_party/WebKit/Source/core/animation/LegacyStyleInterpolation.cpp
index 452696c..7280279d 100644
--- a/third_party/WebKit/Source/core/animation/LegacyStyleInterpolation.cpp
+++ b/third_party/WebKit/Source/core/animation/LegacyStyleInterpolation.cpp
@@ -41,7 +41,7 @@
     : Interpolation(),
       m_start(std::move(start)),
       m_end(std::move(end)),
-      m_id(id),
+      m_property(id),
       m_cachedFraction(0),
       m_cachedIteration(0),
       m_cachedValue(m_start ? m_start->clone() : nullptr) {
diff --git a/third_party/WebKit/Source/core/animation/LegacyStyleInterpolation.h b/third_party/WebKit/Source/core/animation/LegacyStyleInterpolation.h
index 44efdf3..b8723a9 100644
--- a/third_party/WebKit/Source/core/animation/LegacyStyleInterpolation.h
+++ b/third_party/WebKit/Source/core/animation/LegacyStyleInterpolation.h
@@ -35,7 +35,7 @@
   //     AnimatedStyleBuilder::applyProperty)
   // (3) a custom value that is inserted directly into the StyleResolverState.
   void apply(StyleResolverState& state) const {
-    AnimatedStyleBuilder::applyProperty(m_id, state, currentValue().get());
+    AnimatedStyleBuilder::applyProperty(id(), state, currentValue().get());
   }
 
   bool isLegacyStyleInterpolation() const final { return true; }
@@ -44,9 +44,9 @@
     return toInterpolableAnimatableValue(m_cachedValue.get())->value();
   }
 
-  CSSPropertyID id() const { return m_id; }
+  CSSPropertyID id() const { return m_property.cssProperty(); }
 
-  PropertyHandle getProperty() const final { return PropertyHandle(id()); }
+  const PropertyHandle& getProperty() const final { return m_property; }
 
   void interpolate(int iteration, double fraction) final;
 
@@ -58,7 +58,7 @@
  private:
   const std::unique_ptr<InterpolableValue> m_start;
   const std::unique_ptr<InterpolableValue> m_end;
-  CSSPropertyID m_id;
+  PropertyHandle m_property;
 
   mutable double m_cachedFraction;
   mutable int m_cachedIteration;
diff --git a/third_party/WebKit/Source/core/animation/StringKeyframe.cpp b/third_party/WebKit/Source/core/animation/StringKeyframe.cpp
index 420eafc..7f6bb9ff 100644
--- a/third_party/WebKit/Source/core/animation/StringKeyframe.cpp
+++ b/third_party/WebKit/Source/core/animation/StringKeyframe.cpp
@@ -100,7 +100,8 @@
 }
 
 PassRefPtr<Keyframe::PropertySpecificKeyframe>
-StringKeyframe::createPropertySpecificKeyframe(PropertyHandle property) const {
+StringKeyframe::createPropertySpecificKeyframe(
+    const PropertyHandle& property) const {
   if (property.isCSSProperty())
     return CSSPropertySpecificKeyframe::create(
         offset(), &easing(), &cssPropertyValue(property), composite());
diff --git a/third_party/WebKit/Source/core/animation/StringKeyframe.h b/third_party/WebKit/Source/core/animation/StringKeyframe.h
index d60c92da..5c5deff 100644
--- a/third_party/WebKit/Source/core/animation/StringKeyframe.h
+++ b/third_party/WebKit/Source/core/animation/StringKeyframe.h
@@ -157,7 +157,7 @@
 
   PassRefPtr<Keyframe> clone() const override;
   PassRefPtr<Keyframe::PropertySpecificKeyframe> createPropertySpecificKeyframe(
-      PropertyHandle) const override;
+      const PropertyHandle&) const override;
 
   bool isStringKeyframe() const override { return true; }
 
diff --git a/third_party/WebKit/Source/core/animation/animatable/AnimatableValueKeyframe.cpp b/third_party/WebKit/Source/core/animation/animatable/AnimatableValueKeyframe.cpp
index fe315ae..3ff76d0 100644
--- a/third_party/WebKit/Source/core/animation/animatable/AnimatableValueKeyframe.cpp
+++ b/third_party/WebKit/Source/core/animation/animatable/AnimatableValueKeyframe.cpp
@@ -33,7 +33,7 @@
 
 PassRefPtr<Keyframe::PropertySpecificKeyframe>
 AnimatableValueKeyframe::createPropertySpecificKeyframe(
-    PropertyHandle property) const {
+    const PropertyHandle& property) const {
   return PropertySpecificKeyframe::create(
       offset(), &easing(), propertyValue(property.cssProperty()), composite());
 }
@@ -46,7 +46,7 @@
 
 PassRefPtr<Interpolation>
 AnimatableValueKeyframe::PropertySpecificKeyframe::createInterpolation(
-    PropertyHandle property,
+    const PropertyHandle& property,
     const Keyframe::PropertySpecificKeyframe& end) const {
   return LegacyStyleInterpolation::create(
       value(), toAnimatableValuePropertySpecificKeyframe(end).value(),
diff --git a/third_party/WebKit/Source/core/animation/animatable/AnimatableValueKeyframe.h b/third_party/WebKit/Source/core/animation/animatable/AnimatableValueKeyframe.h
index c5b67aa..14aa1702 100644
--- a/third_party/WebKit/Source/core/animation/animatable/AnimatableValueKeyframe.h
+++ b/third_party/WebKit/Source/core/animation/animatable/AnimatableValueKeyframe.h
@@ -51,7 +51,7 @@
         double offset,
         PassRefPtr<TimingFunction> easing) const final;
     PassRefPtr<Interpolation> createInterpolation(
-        PropertyHandle,
+        const PropertyHandle&,
         const Keyframe::PropertySpecificKeyframe& end) const final;
 
    private:
@@ -80,7 +80,7 @@
 
   PassRefPtr<Keyframe> clone() const override;
   PassRefPtr<Keyframe::PropertySpecificKeyframe> createPropertySpecificKeyframe(
-      PropertyHandle) const override;
+      const PropertyHandle&) const override;
 
   bool isAnimatableValueKeyframe() const override { return true; }
 
diff --git a/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.cpp b/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.cpp
index 492c2ca..df1fbfb 100644
--- a/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.cpp
+++ b/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.cpp
@@ -4,6 +4,8 @@
 
 #include "core/css/CSSSyntaxDescriptor.h"
 
+#include "core/animation/CSSColorInterpolationType.h"
+#include "core/animation/CSSValueInterpolationType.h"
 #include "core/css/CSSCustomPropertyDeclaration.h"
 #include "core/css/CSSURIValue.h"
 #include "core/css/CSSValueList.h"
@@ -211,4 +213,47 @@
                                                          isAnimationTainted);
 }
 
+InterpolationTypes CSSSyntaxDescriptor::createInterpolationTypes(
+    const AtomicString& propertyName) const {
+  PropertyHandle property(propertyName);
+  InterpolationTypes interpolationTypes;
+  for (const CSSSyntaxComponent& component : m_syntaxComponents) {
+    if (component.m_repeatable) {
+      // TODO(alancutter): Support animation of repeatable types.
+      continue;
+    }
+
+    switch (component.m_type) {
+      case CSSSyntaxType::Color:
+        interpolationTypes.append(
+            WTF::makeUnique<CSSColorInterpolationType>(property));
+        break;
+      case CSSSyntaxType::Length:
+      case CSSSyntaxType::Number:
+      case CSSSyntaxType::Percentage:
+      case CSSSyntaxType::LengthPercentage:
+      case CSSSyntaxType::Image:
+      case CSSSyntaxType::Url:
+      case CSSSyntaxType::Integer:
+      case CSSSyntaxType::Angle:
+      case CSSSyntaxType::Time:
+      case CSSSyntaxType::Resolution:
+      case CSSSyntaxType::TransformFunction:
+        // TODO(alancutter): Support smooth interpolation of these types.
+        break;
+      case CSSSyntaxType::TokenStream:
+      case CSSSyntaxType::Ident:
+      case CSSSyntaxType::CustomIdent:
+        // Uses the CSSValueInterpolationType added below.
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+  interpolationTypes.append(
+      WTF::makeUnique<CSSValueInterpolationType>(property));
+  return interpolationTypes;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.h b/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.h
index 94eb432..ff4dace 100644
--- a/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.h
+++ b/third_party/WebKit/Source/core/css/CSSSyntaxDescriptor.h
@@ -5,6 +5,7 @@
 #ifndef CSSSyntaxDescriptor_h
 #define CSSSyntaxDescriptor_h
 
+#include "core/animation/InterpolationTypesMap.h"
 #include "core/css/parser/CSSParserTokenRange.h"
 
 namespace blink {
@@ -52,6 +53,9 @@
            m_syntaxComponents[0].m_type == CSSSyntaxType::TokenStream;
   }
 
+  InterpolationTypes createInterpolationTypes(
+      const AtomicString& propertyName) const;
+
  private:
   Vector<CSSSyntaxComponent> m_syntaxComponents;
 };
diff --git a/third_party/WebKit/Source/core/css/CSSValueKeywords.json5 b/third_party/WebKit/Source/core/css/CSSValueKeywords.json5
index b579eaf..320818c 100644
--- a/third_party/WebKit/Source/core/css/CSSValueKeywords.json5
+++ b/third_party/WebKit/Source/core/css/CSSValueKeywords.json5
@@ -1,1126 +1,1126 @@
 {
-// The mode argument is used to limit the keyword to be used only for certain
-// CSSParserModes. Values that have the prefix -internal- are only allowed by
-// CSSParserModes listed in allowInternalPropertyAndValue()
+  // The mode argument is used to limit the keyword to be used only for certain
+  // CSSParserModes. Values that have the prefix -internal- are only allowed by
+  // CSSParserModes listed in allowInternalPropertyAndValue()
 
-parameters: {
+  parameters: {
     mode: {},
-},
-
-//
-// CSS value names
-//
-
-data: [
-
-  "inherit",
-  "initial",
-  //
-  // outline-style
-  // border-top-style
-  // border-bottom-style
-  // border-left-style
-  // border-right-style
-  // The order here must match the order of the EBorderStyle enum in ComputedStyleConstants.h.
-  "none",
-  "hidden",
-  "inset",
-  "groove",
-  "outset",
-  "ridge",
-  "dotted",
-  "dashed",
-  "solid",
-  "double",
-
-  //
-  // font
-  //
-  "caption",
-  "icon",
-  "menu",
-  "message-box",
-  "small-caption",
-  "-webkit-mini-control",
-  "-webkit-small-control",
-  "-webkit-control",
-  "status-bar",
-
-  //
-  // font-style
-  //
-  //normal
-  "italic",
-  "oblique",
-  // The following is only allowed in @font-face:
-  "all",
-
-  // font-variant-ligatures:
-  //
-  // normal
-  "common-ligatures",
-  "no-common-ligatures",
-  "discretionary-ligatures",
-  "no-discretionary-ligatures",
-  "historical-ligatures",
-  "no-historical-ligatures",
-  "contextual",
-  "no-contextual",
-
-  // font-variant-caps:
-  //
-  // normal
-  "small-caps",
-  "all-small-caps",
-  "petite-caps",
-  "all-petite-caps",
-  "unicase",
-  "titling-caps",
-
-  // font-variant-numeric
-  // normal
-  "lining-nums",
-  "oldstyle-nums",
-  "proportional-nums",
-  "tabular-nums",
-  "diagonal-fractions",
-  "stacked-fractions",
-  "ordinal",
-  "slashed-zero",
-
-  //
-  // font-weigth
-  //
-  "normal",
-  "bold",
-  "bolder",
-  "lighter",
-  "100",
-  "200",
-  "300",
-  "400",
-  "500",
-  "600",
-  "700",
-  "800",
-  "900",
-
-  //
-  // font-stretch
-  //
-  "ultra-condensed",
-  "extra-condensed",
-  "condensed",
-  "semi-condensed",
-  "semi-expanded",
-  "expanded",
-  "extra-expanded",
-  "ultra-expanded",
-
-  //
-  // font-size
-  //
-  "xx-small",
-  "x-small",
-  "small",
-  "medium",
-  "large",
-  "x-large",
-  "xx-large",
-  "-webkit-xxx-large",
-  "smaller",
-  "larger",
-
-  //
-  // font-family (<generic-family> in CSS 2.1)
-  //
-  "serif",
-  "sans-serif",
-  "cursive",
-  "fantasy",
-  "monospace",
-  "-webkit-body",
-  "-webkit-pictograph",
-
-  //
-  // font-display
-  //
-  //auto
-  //block
-  "swap",
-  "fallback",
-  "optional",
-
-  //
-  //
-  // *-color
-  //
-  "aqua",
-  "black",
-  "blue",
-  "fuchsia",
-  "gray",
-  "green",
-  "lime",
-  "maroon",
-  "navy",
-  "olive",
-  "orange",
-  "purple",
-  "red",
-  "silver",
-  "teal",
-  "white",
-  "yellow",
-  "transparent",
-  "-webkit-link",
-  "-webkit-activelink",
-  "activeborder",
-  "activecaption",
-  "appworkspace",
-  "background",
-  "buttonface",
-  "buttonhighlight",
-  "buttonshadow",
-  "buttontext",
-  "captiontext",
-  "graytext",
-  "highlight",
-  "highlighttext",
-  "inactiveborder",
-  "inactivecaption",
-  "inactivecaptiontext",
-  "infobackground",
-  "infotext",
-  "menutext",
-  "scrollbar",
-  "threeddarkshadow",
-  "threedface",
-  "threedhighlight",
-  "threedlightshadow",
-  "threedshadow",
-  "window",
-  "windowframe",
-  "windowtext",
-  "-internal-active-list-box-selection",
-  "-internal-active-list-box-selection-text",
-  "-internal-inactive-list-box-selection",
-  "-internal-inactive-list-box-selection-text",
-  {
-    name: "-webkit-focus-ring-color",
-    mode: "QuirksOrUASheet",
   },
-  "currentcolor",
-  "grey",
+
   //
-  // Value used to implement the behavior in:
-  // https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk
-  "-internal-quirk-inherit",
-  //
-  // background-repeat
-  //
-  "repeat",
-  "repeat-x",
-  "repeat-y",
-  "no-repeat",
-  // round
-  // space
-  //
-  // -webkit-mask-composite
-  //
-  "clear",
-  "copy",
-  "source-over",
-  "source-in",
-  "source-out",
-  "source-atop",
-  "destination-over",
-  "destination-in",
-  "destination-out",
-  "destination-atop",
-  "xor",
-  // highlight
-  "plus-lighter",
-  //
-  // vertical-align
-  //
-  "baseline",
-  "middle",
-  "sub",
-  "super",
-  "text-top",
-  "text-bottom",
-  "top",
-  "bottom",
-  // HTML alignment MIDDLE has no corresponding CSS alignment
-  "-webkit-baseline-middle",
-  //
-  // text-align
-  // The order of this enum must match the order found in CSSParserFastPaths::isValidKeywordPropertyAndValue().
-  //
-  "-webkit-auto",
-  "left",
-  "right",
-  "center",
-  "justify",
-  "-webkit-left",
-  "-webkit-right",
-  "-webkit-center",
-  "-webkit-match-parent",
-  "-internal-center",
-  //
-  // text-justify
-  //
-  //auto
-  //none
-  "inter-word",
-  "distribute",
-  //
-  // list-style-position
-  //
-  "outside",
-  "inside",
-  //
-  // list-style-type
-  // The order of this enum must match the order found in CSSParserFastPaths::isValidKeywordPropertyAndValue().
-  //
-  "disc",
-  "circle",
-  "square",
-  "decimal",
-  "decimal-leading-zero",
-  "arabic-indic",
-  "bengali",
-  "cambodian",
-  "khmer",
-  "devanagari",
-  "gujarati",
-  "gurmukhi",
-  "kannada",
-  "lao",
-  "malayalam",
-  "mongolian",
-  "myanmar",
-  "oriya",
-  "persian",
-  "urdu",
-  "telugu",
-  "tibetan",
-  "thai",
-  "lower-roman",
-  "upper-roman",
-  "lower-greek",
-  "lower-alpha",
-  "lower-latin",
-  "upper-alpha",
-  "upper-latin",
-  "cjk-earthly-branch",
-  "cjk-heavenly-stem",
-  "ethiopic-halehame",
-  "ethiopic-halehame-am",
-  "ethiopic-halehame-ti-er",
-  "ethiopic-halehame-ti-et",
-  "hangul",
-  "hangul-consonant",
-  "korean-hangul-formal",
-  "korean-hanja-formal",
-  "korean-hanja-informal",
-  "hebrew",
-  "armenian",
-  "lower-armenian",
-  "upper-armenian",
-  "georgian",
-  "cjk-ideographic",
-  "simp-chinese-formal",
-  "simp-chinese-informal",
-  "trad-chinese-formal",
-  "trad-chinese-informal",
-  "hiragana",
-  "katakana",
-  "hiragana-iroha",
-  "katakana-iroha",
-  //none
-  //
-  // display
-  // The order of this enum must match the order found in CSSParserFastPaths::isValidKeywordPropertyAndValue().
-  //
-  "inline",
-  "block",
-	"flow-root",
-  "list-item",
-  "inline-block",
-  "table",
-  "inline-table",
-  "table-row-group",
-  "table-header-group",
-  "table-footer-group",
-  "table-row",
-  "table-column-group",
-  "table-column",
-  "table-cell",
-  "table-caption",
-  "-webkit-box",
-  "-webkit-inline-box",
-  "flex",
-  "inline-flex",
-  "grid",
-  "inline-grid",
-  "contents",
-  //none
-  "-webkit-flex",
-  "-webkit-inline-flex",
-  //
-  // cursor
-  // The order of this enum must match the order found in CSSPropertyParser::consumeCursor().
-  //
-  "auto",
-  "crosshair",
-  "default",
-  "pointer",
-  "move",
-  "vertical-text",
-  "cell",
-  "context-menu",
-  "alias",
-  // copy
-  "progress",
-  "no-drop",
-  "not-allowed",
-  "zoom-in",
-  "zoom-out",
-  "e-resize",
-  "ne-resize",
-  "nw-resize",
-  "n-resize",
-  "se-resize",
-  "sw-resize",
-  "s-resize",
-  "w-resize",
-  "ew-resize",
-  "ns-resize",
-  "nesw-resize",
-  "nwse-resize",
-  "col-resize",
-  "row-resize",
-  "text",
-  "wait",
-  "help",
-  "all-scroll",
-  "-webkit-grab",
-  "-webkit-grabbing",
-  "-webkit-zoom-in",
-  "-webkit-zoom-out",
-  // none
-  //
-  // direction
-  //
-  "ltr",
-  "rtl",
-  //
-  // text-transform
-  //
-  "capitalize",
-  "uppercase",
-  "lowercase",
-  //none
-  //
-  // visibility
-  //
-  "visible",
-  //hidden
-  "collapse",
-  //
-  // Unordered rest
+  // CSS value names
   //
-  "a3",
-  "a4",
-  "a5",
-  "above",
-  "absolute",
-  "always",
-  "avoid",
-  "b4",
-  "b5",
-  "below",
-  "bidi-override",
-  "blink",
-  "both",
-  "close-quote",
-  "embed",
-  "fixed",
-  "hand",
-  "hide",
-  "isolate",
-  "isolate-override",
-  "plaintext",
-  "-webkit-isolate",
-  "-webkit-isolate-override",
-  "-webkit-plaintext",
-  "landscape",
-  "ledger",
-  "legal",
-  "letter",
-  "line-through",
-  "local",
-  "no-close-quote",
-  "no-open-quote",
-  "nowrap",
-  "open-quote",
-  "overlay",
-  "overline",
-  "portrait",
-  "pre",
-  "pre-line",
-  "pre-wrap",
-  "relative",
-  "scroll",
-  "separate",
-  "show",
-  "static",
-  "thick",
-  "thin",
-  "underline",
-  "wavy",
-  "-webkit-nowrap",
 
-  // CSS3 Values
-  // box-align
-  "stretch",
-  "start",
-  "end",
-  //center
-  //baseline
+  data: [
 
-  // box-decoration-break
-  "clone",
-  "slice",
+    "inherit",
+    "initial",
+    //
+    // outline-style
+    // border-top-style
+    // border-bottom-style
+    // border-left-style
+    // border-right-style
+    // The order here must match the order of the EBorderStyle enum in ComputedStyleConstants.h.
+    "none",
+    "hidden",
+    "inset",
+    "groove",
+    "outset",
+    "ridge",
+    "dotted",
+    "dashed",
+    "solid",
+    "double",
 
-  // box-direction
-  // normal
-  "reverse",
+    //
+    // font
+    //
+    "caption",
+    "icon",
+    "menu",
+    "message-box",
+    "small-caption",
+    "-webkit-mini-control",
+    "-webkit-small-control",
+    "-webkit-control",
+    "status-bar",
 
-  // box-orient
-  "horizontal",
-  "vertical",
-  "inline-axis",
-  "block-axis",
+    //
+    // font-style
+    //
+    //normal
+    "italic",
+    "oblique",
+    // The following is only allowed in @font-face:
+    "all",
 
-  // box-pack
-  // start
-  // end
-  // center
-  // justify
+    // font-variant-ligatures:
+    //
+    // normal
+    "common-ligatures",
+    "no-common-ligatures",
+    "discretionary-ligatures",
+    "no-discretionary-ligatures",
+    "historical-ligatures",
+    "no-historical-ligatures",
+    "contextual",
+    "no-contextual",
 
-  // box-lines
-  "single",
-  "multiple",
+    // font-variant-caps:
+    //
+    // normal
+    "small-caps",
+    "all-small-caps",
+    "petite-caps",
+    "all-petite-caps",
+    "unicase",
+    "titling-caps",
 
-  // align-content
-  // start
-  // end
-  "flex-start",
-  "flex-end",
-  // center
-  "space-between",
-  "space-around",
-  "space-evenly",
-  // stretch
-  "unsafe",
-  "safe",
+    // font-variant-numeric
+    // normal
+    "lining-nums",
+    "oldstyle-nums",
+    "proportional-nums",
+    "tabular-nums",
+    "diagonal-fractions",
+    "stacked-fractions",
+    "ordinal",
+    "slashed-zero",
 
-  // align-items / align-self
-  // flex-start
-  // flex-end
-  // center
-  // baseline
-  // stretch
+    //
+    // font-weigth
+    //
+    "normal",
+    "bold",
+    "bolder",
+    "lighter",
+    "100",
+    "200",
+    "300",
+    "400",
+    "500",
+    "600",
+    "700",
+    "800",
+    "900",
 
-  // justify-content
-  // start
-  // end
-  // flex-start
-  // flex-end
-  // center
-  // space-between
-  // space-around
-  // space-evenly
-  // stretch
-  // unsafe
-  // safe
+    //
+    // font-stretch
+    //
+    "ultra-condensed",
+    "extra-condensed",
+    "condensed",
+    "semi-condensed",
+    "semi-expanded",
+    "expanded",
+    "extra-expanded",
+    "ultra-expanded",
 
+    //
+    // font-size
+    //
+    "xx-small",
+    "x-small",
+    "small",
+    "medium",
+    "large",
+    "x-large",
+    "xx-large",
+    "-webkit-xxx-large",
+    "smaller",
+    "larger",
 
-  // flex-flow
-  "row",
-  "row-reverse",
-  "column",
-  "column-reverse",
-  // nowrap
-  "wrap",
-  "wrap-reverse",
+    //
+    // font-family (<generic-family> in CSS 2.1)
+    //
+    "serif",
+    "sans-serif",
+    "cursive",
+    "fantasy",
+    "monospace",
+    "-webkit-body",
+    "-webkit-pictograph",
 
-  // grid-auto-flow
-  "auto-flow",
-  "dense",
+    //
+    // font-display
+    //
+    //auto
+    //block
+    "swap",
+    "fallback",
+    "optional",
 
-  //
-  // -webkit-user-modify
-  //
-  "read-only",
-  "read-write",
-  "read-write-plaintext-only",
+    //
+    //
+    // *-color
+    //
+    "aqua",
+    "black",
+    "blue",
+    "fuchsia",
+    "gray",
+    "green",
+    "lime",
+    "maroon",
+    "navy",
+    "olive",
+    "orange",
+    "purple",
+    "red",
+    "silver",
+    "teal",
+    "white",
+    "yellow",
+    "transparent",
+    "-webkit-link",
+    "-webkit-activelink",
+    "activeborder",
+    "activecaption",
+    "appworkspace",
+    "background",
+    "buttonface",
+    "buttonhighlight",
+    "buttonshadow",
+    "buttontext",
+    "captiontext",
+    "graytext",
+    "highlight",
+    "highlighttext",
+    "inactiveborder",
+    "inactivecaption",
+    "inactivecaptiontext",
+    "infobackground",
+    "infotext",
+    "menutext",
+    "scrollbar",
+    "threeddarkshadow",
+    "threedface",
+    "threedhighlight",
+    "threedlightshadow",
+    "threedshadow",
+    "window",
+    "windowframe",
+    "windowtext",
+    "-internal-active-list-box-selection",
+    "-internal-active-list-box-selection-text",
+    "-internal-inactive-list-box-selection",
+    "-internal-inactive-list-box-selection-text",
+    {
+      name: "-webkit-focus-ring-color",
+      mode: "QuirksOrUASheet",
+    },
+    "currentcolor",
+    "grey",
+    //
+    // Value used to implement the behavior in:
+    // https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk
+    "-internal-quirk-inherit",
+    //
+    // background-repeat
+    //
+    "repeat",
+    "repeat-x",
+    "repeat-y",
+    "no-repeat",
+    // round
+    // space
+    //
+    // -webkit-mask-composite
+    //
+    "clear",
+    "copy",
+    "source-over",
+    "source-in",
+    "source-out",
+    "source-atop",
+    "destination-over",
+    "destination-in",
+    "destination-out",
+    "destination-atop",
+    "xor",
+    // highlight
+    "plus-lighter",
+    //
+    // vertical-align
+    //
+    "baseline",
+    "middle",
+    "sub",
+    "super",
+    "text-top",
+    "text-bottom",
+    "top",
+    "bottom",
+    // HTML alignment MIDDLE has no corresponding CSS alignment
+    "-webkit-baseline-middle",
+    //
+    // text-align
+    // The order of this enum must match the order found in CSSParserFastPaths::isValidKeywordPropertyAndValue().
+    //
+    "-webkit-auto",
+    "left",
+    "right",
+    "center",
+    "justify",
+    "-webkit-left",
+    "-webkit-right",
+    "-webkit-center",
+    "-webkit-match-parent",
+    "-internal-center",
+    //
+    // text-justify
+    //
+    //auto
+    //none
+    "inter-word",
+    "distribute",
+    //
+    // list-style-position
+    //
+    "outside",
+    "inside",
+    //
+    // list-style-type
+    // The order of this enum must match the order found in CSSParserFastPaths::isValidKeywordPropertyAndValue().
+    //
+    "disc",
+    "circle",
+    "square",
+    "decimal",
+    "decimal-leading-zero",
+    "arabic-indic",
+    "bengali",
+    "cambodian",
+    "khmer",
+    "devanagari",
+    "gujarati",
+    "gurmukhi",
+    "kannada",
+    "lao",
+    "malayalam",
+    "mongolian",
+    "myanmar",
+    "oriya",
+    "persian",
+    "urdu",
+    "telugu",
+    "tibetan",
+    "thai",
+    "lower-roman",
+    "upper-roman",
+    "lower-greek",
+    "lower-alpha",
+    "lower-latin",
+    "upper-alpha",
+    "upper-latin",
+    "cjk-earthly-branch",
+    "cjk-heavenly-stem",
+    "ethiopic-halehame",
+    "ethiopic-halehame-am",
+    "ethiopic-halehame-ti-er",
+    "ethiopic-halehame-ti-et",
+    "hangul",
+    "hangul-consonant",
+    "korean-hangul-formal",
+    "korean-hanja-formal",
+    "korean-hanja-informal",
+    "hebrew",
+    "armenian",
+    "lower-armenian",
+    "upper-armenian",
+    "georgian",
+    "cjk-ideographic",
+    "simp-chinese-formal",
+    "simp-chinese-informal",
+    "trad-chinese-formal",
+    "trad-chinese-informal",
+    "hiragana",
+    "katakana",
+    "hiragana-iroha",
+    "katakana-iroha",
+    //none
+    //
+    // display
+    // The order of this enum must match the order found in CSSParserFastPaths::isValidKeywordPropertyAndValue().
+    //
+    "inline",
+    "block",
+    "flow-root",
+    "list-item",
+    "inline-block",
+    "table",
+    "inline-table",
+    "table-row-group",
+    "table-header-group",
+    "table-footer-group",
+    "table-row",
+    "table-column-group",
+    "table-column",
+    "table-cell",
+    "table-caption",
+    "-webkit-box",
+    "-webkit-inline-box",
+    "flex",
+    "inline-flex",
+    "grid",
+    "inline-grid",
+    "contents",
+    //none
+    "-webkit-flex",
+    "-webkit-inline-flex",
+    //
+    // cursor
+    // The order of this enum must match the order found in CSSPropertyParser::consumeCursor().
+    //
+    "auto",
+    "crosshair",
+    "default",
+    "pointer",
+    "move",
+    "vertical-text",
+    "cell",
+    "context-menu",
+    "alias",
+    // copy
+    "progress",
+    "no-drop",
+    "not-allowed",
+    "zoom-in",
+    "zoom-out",
+    "e-resize",
+    "ne-resize",
+    "nw-resize",
+    "n-resize",
+    "se-resize",
+    "sw-resize",
+    "s-resize",
+    "w-resize",
+    "ew-resize",
+    "ns-resize",
+    "nesw-resize",
+    "nwse-resize",
+    "col-resize",
+    "row-resize",
+    "text",
+    "wait",
+    "help",
+    "all-scroll",
+    "-webkit-grab",
+    "-webkit-grabbing",
+    "-webkit-zoom-in",
+    "-webkit-zoom-out",
+    // none
+    //
+    // direction
+    //
+    "ltr",
+    "rtl",
+    //
+    // text-transform
+    //
+    "capitalize",
+    "uppercase",
+    "lowercase",
+    //none
+    //
+    // visibility
+    //
+    "visible",
+    //hidden
+    "collapse",
+    //
+    // Unordered rest
+    //
+    "a3",
+    "a4",
+    "a5",
+    "above",
+    "absolute",
+    "always",
+    "avoid",
+    "b4",
+    "b5",
+    "below",
+    "bidi-override",
+    "blink",
+    "both",
+    "close-quote",
+    "embed",
+    "fixed",
+    "hand",
+    "hide",
+    "isolate",
+    "isolate-override",
+    "plaintext",
+    "-webkit-isolate",
+    "-webkit-isolate-override",
+    "-webkit-plaintext",
+    "landscape",
+    "ledger",
+    "legal",
+    "letter",
+    "line-through",
+    "local",
+    "no-close-quote",
+    "no-open-quote",
+    "nowrap",
+    "open-quote",
+    "overlay",
+    "overline",
+    "portrait",
+    "pre",
+    "pre-line",
+    "pre-wrap",
+    "relative",
+    "scroll",
+    "separate",
+    "show",
+    "static",
+    "thick",
+    "thin",
+    "underline",
+    "wavy",
+    "-webkit-nowrap",
 
-  //
-  // -webkit-user-drag
-  //
-  "element",
+    // CSS3 Values
+    // box-align
+    "stretch",
+    "start",
+    "end",
+    //center
+    //baseline
 
-  //
-  // CSS3 intrinsic dimension keywords
-  //
-  "-webkit-min-content",
-  "-webkit-max-content",
-  "-webkit-fill-available",
-  "-webkit-fit-content",
-  "min-content",
-  "max-content",
-  "fit-content",
+    // box-decoration-break
+    "clone",
+    "slice",
 
-  //
-  // text-overflow
-  //
-  "clip",
-  "ellipsis",
+    // box-direction
+    // normal
+    "reverse",
+
+    // box-orient
+    "horizontal",
+    "vertical",
+    "inline-axis",
+    "block-axis",
+
+    // box-pack
+    // start
+    // end
+    // center
+    // justify
+
+    // box-lines
+    "single",
+    "multiple",
+
+    // align-content
+    // start
+    // end
+    "flex-start",
+    "flex-end",
+    // center
+    "space-between",
+    "space-around",
+    "space-evenly",
+    // stretch
+    "unsafe",
+    "safe",
+
+    // align-items / align-self
+    // flex-start
+    // flex-end
+    // center
+    // baseline
+    // stretch
+
+    // justify-content
+    // start
+    // end
+    // flex-start
+    // flex-end
+    // center
+    // space-between
+    // space-around
+    // space-evenly
+    // stretch
+    // unsafe
+    // safe
+
+
+    // flex-flow
+    "row",
+    "row-reverse",
+    "column",
+    "column-reverse",
+    // nowrap
+    "wrap",
+    "wrap-reverse",
+
+    // grid-auto-flow
+    "auto-flow",
+    "dense",
+
+    //
+    // -webkit-user-modify
+    //
+    "read-only",
+    "read-write",
+    "read-write-plaintext-only",
+
+    //
+    // -webkit-user-drag
+    //
+    "element",
+
+    //
+    // CSS3 intrinsic dimension keywords
+    //
+    "-webkit-min-content",
+    "-webkit-max-content",
+    "-webkit-fill-available",
+    "-webkit-fit-content",
+    "min-content",
+    "max-content",
+    "fit-content",
+
+    //
+    // text-overflow
+    //
+    "clip",
+    "ellipsis",
 
-  //
-  // text-decoration-skip
-  //
-  "objects",
-  "ink",
+    //
+    // text-decoration-skip
+    //
+    "objects",
+    "ink",
 
-  //
-  // -webkit-margin-collapse
-  //
-  // collapse
-  // separate
-  "discard",
+    //
+    // -webkit-margin-collapse
+    //
+    // collapse
+    // separate
+    "discard",
 
-  //
-  // word-break
-  //
-  "break-all",
-  "keep-all",
+    //
+    // word-break
+    //
+    "break-all",
+    "keep-all",
 
-  //
-  // word-wrap
-  //
-  "break-word",
+    //
+    // word-wrap
+    //
+    "break-word",
 
-  //
-  // nbsp-mode
-  //
-  "space",
+    //
+    // nbsp-mode
+    //
+    "space",
 
-  //
-  // -webkit-line-break
-  //
-  // auto
-  "loose",
-  // normal
-  "strict",
-  "after-white-space",
+    //
+    // -webkit-line-break
+    //
+    // auto
+    "loose",
+    // normal
+    "strict",
+    "after-white-space",
 
-  // hyphens
-  "manual",
+    // hyphens
+    "manual",
 
-  // -webkit-appearance
-  // The order here must match the order in the ControlPart enum in ThemeTypes.h.
-  // All appearance values that should be accepted by the parser should be listed between 'checkbox' and 'textarea':
-  "checkbox",
-  "radio",
-  "push-button",
-  "square-button",
-  "button",
-  "button-bevel",
-  "inner-spin-button",
-  "listbox",
-  "listitem",
-  "media-enter-fullscreen-button",
-  "media-exit-fullscreen-button",
-  "media-fullscreen-volume-slider",
-  "media-fullscreen-volume-slider-thumb",
-  "media-mute-button",
-  "media-play-button",
-  "media-overlay-play-button",
-  "media-toggle-closed-captions-button",
-  "media-slider",
-  "media-sliderthumb",
-  "media-volume-slider-container",
-  "media-volume-slider",
-  "media-volume-sliderthumb",
-  "media-controls-background",
-  "media-controls-fullscreen-background",
-  "media-current-time-display",
-  "media-time-remaining-display",
-  "-internal-media-cast-off-button",
-  "-internal-media-overlay-cast-off-button",
-  "-internal-media-track-selection-checkmark",
-  "-internal-media-closed-captions-icon",
-  "-internal-media-subtitles-icon",
-  "-internal-media-overflow-button",
-  "-internal-media-download-button",
-  "menulist",
-  "menulist-button",
-  "menulist-text",
-  "menulist-textfield",
-  "meter",
-  "progress-bar",
-  "progress-bar-value",
-  "slider-horizontal",
-  "slider-vertical",
-  "sliderthumb-horizontal",
-  "sliderthumb-vertical",
-  "caret",
-  "searchfield",
-  "searchfield-cancel-button",
-  "textfield",
-  "textarea",
-  // An appearance value that should not be accepted by the parser:
-  "caps-lock-indicator",
+    // -webkit-appearance
+    // The order here must match the order in the ControlPart enum in ThemeTypes.h.
+    // All appearance values that should be accepted by the parser should be listed between 'checkbox' and 'textarea':
+    "checkbox",
+    "radio",
+    "push-button",
+    "square-button",
+    "button",
+    "button-bevel",
+    "inner-spin-button",
+    "listbox",
+    "listitem",
+    "media-enter-fullscreen-button",
+    "media-exit-fullscreen-button",
+    "media-fullscreen-volume-slider",
+    "media-fullscreen-volume-slider-thumb",
+    "media-mute-button",
+    "media-play-button",
+    "media-overlay-play-button",
+    "media-toggle-closed-captions-button",
+    "media-slider",
+    "media-sliderthumb",
+    "media-volume-slider-container",
+    "media-volume-slider",
+    "media-volume-sliderthumb",
+    "media-controls-background",
+    "media-controls-fullscreen-background",
+    "media-current-time-display",
+    "media-time-remaining-display",
+    "-internal-media-cast-off-button",
+    "-internal-media-overlay-cast-off-button",
+    "-internal-media-track-selection-checkmark",
+    "-internal-media-closed-captions-icon",
+    "-internal-media-subtitles-icon",
+    "-internal-media-overflow-button",
+    "-internal-media-download-button",
+    "menulist",
+    "menulist-button",
+    "menulist-text",
+    "menulist-textfield",
+    "meter",
+    "progress-bar",
+    "progress-bar-value",
+    "slider-horizontal",
+    "slider-vertical",
+    "sliderthumb-horizontal",
+    "sliderthumb-vertical",
+    "caret",
+    "searchfield",
+    "searchfield-cancel-button",
+    "textfield",
+    "textarea",
+    // An appearance value that should not be accepted by the parser:
+    "caps-lock-indicator",
 
-  //
-  // border-image
-  //
-  // stretch
-  // repeat
-  "round",
+    //
+    // border-image
+    //
+    // stretch
+    // repeat
+    "round",
 
-  //
-  // background-clip/background-origin
-  //
-  // border/content/padding are deprecated and ultimately will only apply to the -webkit- form of these properties.
-  // border-box/content-box/padding-box should be used instead.
-  //
-  "border",
-  "border-box",
-  "content",
-  "content-box",
-  "padding",
-  "padding-box",
+    //
+    // background-clip/background-origin
+    //
+    // border/content/padding are deprecated and ultimately will only apply to the -webkit- form of these properties.
+    // border-box/content-box/padding-box should be used instead.
+    //
+    "border",
+    "border-box",
+    "content",
+    "content-box",
+    "padding",
+    "padding-box",
 
-  // CSS 3 SHAPES
-  "margin-box",
+    // CSS 3 SHAPES
+    "margin-box",
 
-  //
-  // background-size
-  //
-  "contain",
-  "cover",
+    //
+    // background-size
+    //
+    "contain",
+    "cover",
 
-  //
-  // -webkit-rtl-ordering
-  //
-  "logical",
-  "visual",
+    //
+    // -webkit-rtl-ordering
+    //
+    "logical",
+    "visual",
 
-  //
-  // animation-direction
-  //
-  "alternate",
-  "alternate-reverse",
+    //
+    // animation-direction
+    //
+    "alternate",
+    "alternate-reverse",
 
-  //
-  // animation-fill-mode
-  //
-  "forwards",
-  "backwards",
-  // both
+    //
+    // animation-fill-mode
+    //
+    "forwards",
+    "backwards",
+    // both
 
-  //
-  // animation-iteration-count
-  "infinite",
+    //
+    // animation-iteration-count
+    "infinite",
 
-  //
-  // animation-play-state
-  //
-  "running",
-  "paused",
+    //
+    // animation-play-state
+    //
+    "running",
+    "paused",
 
-  //
-  // transform-style
-  //
-  "flat",
-  "preserve-3d",
+    //
+    // transform-style
+    //
+    "flat",
+    "preserve-3d",
 
-  //
-  // transition-timing-function
-  // animation-timing-function
-  //
-  "ease",
-  "linear",
-  "ease-in",
-  "ease-out",
-  "ease-in-out",
-  "step-start",
-  "step-middle",
-  "step-end",
-  "steps",
-  "cubic-bezier",
+    //
+    // transition-timing-function
+    // animation-timing-function
+    //
+    "ease",
+    "linear",
+    "ease-in",
+    "ease-out",
+    "ease-in-out",
+    "step-start",
+    "step-middle",
+    "step-end",
+    "steps",
+    "cubic-bezier",
 
-  //
-  // zoom
-  //
-  "document",
-  "reset",
+    //
+    // zoom
+    //
+    "document",
+    "reset",
 
-  //
-  // user-zoom
-  //
-  // fixed
-  "zoom",
+    //
+    // user-zoom
+    //
+    // fixed
+    "zoom",
 
-  //
-  // pointer-events
-  //
-  "visiblePainted",
-  "visibleFill",
-  "visibleStroke",
-  //visible
-  "painted",
-  "fill",
-  "stroke",
-  "bounding-box",
-  //all
-  //none
+    //
+    // pointer-events
+    //
+    "visiblePainted",
+    "visibleFill",
+    "visibleStroke",
+    //visible
+    "painted",
+    "fill",
+    "stroke",
+    "bounding-box",
+    //all
+    //none
 
-  //
-  // speech
-  //
-  "spell-out",
-  "digits",
-  "literal-punctuation",
-  "no-punctuation",
+    //
+    // speech
+    //
+    "spell-out",
+    "digits",
+    "literal-punctuation",
+    "no-punctuation",
 
-  //
-  // -webkit-font-smoothing
-  //
-  // auto
-  // none
-  "antialiased",
-  "subpixel-antialiased",
+    //
+    // -webkit-font-smoothing
+    //
+    // auto
+    // none
+    "antialiased",
+    "subpixel-antialiased",
 
-  // text-rendering
-  //auto
-  "optimizeSpeed",
-  "optimizeLegibility",
-  "geometricPrecision",
+    // text-rendering
+    //auto
+    "optimizeSpeed",
+    "optimizeLegibility",
+    "geometricPrecision",
 
-  // -webkit-color-adjust
-  "economy",
-  "exact",
+    // -webkit-color-adjust
+    "economy",
+    "exact",
 
-  // -webkit-writing-mode
-  // SVG compatibility
-  "lr",
-  "rl",
-  "tb",
-  "lr-tb",
-  "rl-tb",
-  "tb-rl",
-  // Standard values from CSS3
-  "horizontal-tb",
-  "vertical-rl",
-  "vertical-lr",
+    // -webkit-writing-mode
+    // SVG compatibility
+    "lr",
+    "rl",
+    "tb",
+    "lr-tb",
+    "rl-tb",
+    "tb-rl",
+    // Standard values from CSS3
+    "horizontal-tb",
+    "vertical-rl",
+    "vertical-lr",
 
-  // -webkit-ruby-position
-  "after",
-  "before",
+    // -webkit-ruby-position
+    "after",
+    "before",
 
-  // -webkit-text-emphasis-position
-  "over",
-  "under",
+    // -webkit-text-emphasis-position
+    "over",
+    "under",
 
-  // -webkit-text-emphasis-style
-  "filled",
-  "open",
-  "dot",
-  // circle
-  "double-circle",
-  "triangle",
-  "sesame",
+    // -webkit-text-emphasis-style
+    "filled",
+    "open",
+    "dot",
+    // circle
+    "double-circle",
+    "triangle",
+    "sesame",
 
-  // -webkit-radial-gradient
-  // circle
-  "ellipse",
-  "closest-side",
-  "closest-corner",
-  "farthest-side",
-  "farthest-corner",
-  // contain
-  // cover
+    // -webkit-radial-gradient
+    // circle
+    "ellipse",
+    "closest-side",
+    "closest-corner",
+    "farthest-side",
+    "farthest-corner",
+    // contain
+    // cover
 
-  // text-orientation/-webkit-text-orientation
-  "mixed",
-  "sideways",
-  "sideways-right",
-  "upright",
-  "vertical-right",
+    // text-orientation/-webkit-text-orientation
+    "mixed",
+    "sideways",
+    "sideways-right",
+    "upright",
+    "vertical-right",
 
-  // -webkit-font-feature-settings
-  "on",
-  "off",
+    // -webkit-font-feature-settings
+    "on",
+    "off",
 
-  // image-rendering
-  //auto
-  //optimizeSpeed
-  "optimizeQuality",
-  "pixelated",
-  "-webkit-optimize-contrast",
+    // image-rendering
+    //auto
+    //optimizeSpeed
+    "optimizeQuality",
+    "pixelated",
+    "-webkit-optimize-contrast",
 
-  // shape-outside
-  "nonzero",
-  "evenodd",
-  "at",
-  // closest-side
-  // farthest-side
+    // shape-outside
+    "nonzero",
+    "evenodd",
+    "at",
+    // closest-side
+    // farthest-side
 
-  "alphabetic",
+    "alphabetic",
 
-  // (display-mode:) media feature
-  "fullscreen",
-  "standalone",
-  "minimal-ui",
-  "browser",
+    // (display-mode:) media feature
+    "fullscreen",
+    "standalone",
+    "minimal-ui",
+    "browser",
 
-  // position
-  "sticky",
+    // position
+    "sticky",
 
-  // (pointer:) media feature
-  // none
-  "coarse",
-  "fine",
+    // (pointer:) media feature
+    // none
+    "coarse",
+    "fine",
 
-  // (hover:) media feature
-  //  none
-  "on-demand",
-  "hover",
+    // (hover:) media feature
+    //  none
+    "on-demand",
+    "hover",
 
-  // blend modes
-  // normal
-  "multiply",
-  "screen",
-  // overlay
-  "darken",
-  "lighten",
-  "color-dodge",
-  "color-burn",
-  "hard-light",
-  "soft-light",
-  "difference",
-  "exclusion",
-  "hue",
-  "saturation",
-  "color",
-  "luminosity",
+    // blend modes
+    // normal
+    "multiply",
+    "screen",
+    // overlay
+    "darken",
+    "lighten",
+    "color-dodge",
+    "color-burn",
+    "hard-light",
+    "soft-light",
+    "difference",
+    "exclusion",
+    "hue",
+    "saturation",
+    "color",
+    "luminosity",
 
-  // object-fit
-  "scale-down",
+    // object-fit
+    "scale-down",
 
-  // column-fill
-  "balance",
+    // column-fill
+    "balance",
 
-  // overflow
-  "-webkit-paged-x",
-  "-webkit-paged-y",
+    // overflow
+    "-webkit-paged-x",
+    "-webkit-paged-y",
 
-  // -webkit-app-region
-  "drag",
-  "no-drag",
+    // -webkit-app-region
+    "drag",
+    "no-drag",
 
-  // grid-{column|row}-{start|end}
-  "span",
+    // grid-{column|row}-{start|end}
+    "span",
 
-  // grid-template-{columns|rows}
-  "minmax",
+    // grid-template-{columns|rows}
+    "minmax",
 
-  // text-indent
-  "each-line",
-  //hanging   // hanging exists in SVGCSSValueKeywords.in
+    // text-indent
+    "each-line",
+    //hanging   // hanging exists in SVGCSSValueKeywords.in
 
-  // (scan:) media feature
-  "progressive",
-  "interlace",
+    // (scan:) media feature
+    "progressive",
+    "interlace",
 
-  //
-  // paint-order
-  //
-  // normal
-  // fill
-  // stroke
-  "markers",
+    //
+    // paint-order
+    //
+    // normal
+    // fill
+    // stroke
+    "markers",
 
-  //
-  // CSS3 viewport-length keywords
-  //
-  "-internal-extend-to-zoom",
+    //
+    // CSS3 viewport-length keywords
+    //
+    "-internal-extend-to-zoom",
 
-  // isolation
-  // auto
-  // isolate
+    // isolation
+    // auto
+    // isolate
 
-  // touch-action
-  "pan-x",
-  "pan-y",
-  "pan-left",
-  "pan-right",
-  "pan-up",
-  "pan-down",
-  "manipulation",
-  "pinch-zoom",
+    // touch-action
+    "pan-x",
+    "pan-y",
+    "pan-left",
+    "pan-right",
+    "pan-up",
+    "pan-down",
+    "manipulation",
+    "pinch-zoom",
 
-  // justify-items / justify-self
-  // auto
-  // stretch
-  // baseline
-  "last-baseline",
-  // center
-  // start
-  // end
-  "self-start",
-  "self-end",
-  // flex-start
-  // flex-end
-  // left
-  // right
-  // unsafe
-  // safe
-  "legacy",
+    // justify-items / justify-self
+    // auto
+    // stretch
+    // baseline
+    "last-baseline",
+    // center
+    // start
+    // end
+    "self-start",
+    "self-end",
+    // flex-start
+    // flex-end
+    // left
+    // right
+    // unsafe
+    // safe
+    "legacy",
 
-  // scroll-behavior
-  // auto
-  "smooth",
+    // scroll-behavior
+    // auto
+    "smooth",
 
-  // will-change
-  // auto
-  // contents
-  "scroll-position",
+    // will-change
+    // auto
+    // contents
+    "scroll-position",
 
-  // all
-  // initial
-  // inherit
-  "revert",
-  "unset",
+    // all
+    // initial
+    // inherit
+    "revert",
+    "unset",
 
-  // background-image, etc.
-  "linear-gradient",
-  "radial-gradient",
-  "repeating-linear-gradient",
-  "repeating-radial-gradient",
-  "paint",
-  "-webkit-cross-fade",
-  "-webkit-gradient",
-  "-webkit-linear-gradient",
-  "-webkit-radial-gradient",
-  "-webkit-repeating-linear-gradient",
-  "-webkit-repeating-radial-gradient",
-  "-webkit-image-set",
+    // background-image, etc.
+    "linear-gradient",
+    "radial-gradient",
+    "repeating-linear-gradient",
+    "repeating-radial-gradient",
+    "paint",
+    "-webkit-cross-fade",
+    "-webkit-gradient",
+    "-webkit-linear-gradient",
+    "-webkit-radial-gradient",
+    "-webkit-repeating-linear-gradient",
+    "-webkit-repeating-radial-gradient",
+    "-webkit-image-set",
 
-  // deprecated gradients
-  "from",
-  "to",
-  "color-stop",
-  "radial",
+    // deprecated gradients
+    "from",
+    "to",
+    "color-stop",
+    "radial",
 
-  // content
-  "attr",
-  "counter",
-  "counters",
+    // content
+    "attr",
+    "counter",
+    "counters",
 
-  // clip
-  "rect",
+    // clip
+    "rect",
 
-  // shapes
-  "polygon",
+    // shapes
+    "polygon",
 
-  // @font-face src
-  "format",
+    // @font-face src
+    "format",
 
-  // (-webkit-)filter
-  "invert",
-  "grayscale",
-  "sepia",
-  "saturate",
-  "hue-rotate",
-  "opacity",
-  "brightness",
-  "contrast",
-  "blur",
-  "drop-shadow",
-  "url",
+    // (-webkit-)filter
+    "invert",
+    "grayscale",
+    "sepia",
+    "saturate",
+    "hue-rotate",
+    "opacity",
+    "brightness",
+    "contrast",
+    "blur",
+    "drop-shadow",
+    "url",
 
-  // colors
-  "rgb",
-  "rgba",
-  "hsl",
-  "hsla",
+    // colors
+    "rgb",
+    "rgba",
+    "hsl",
+    "hsla",
 
-  // transform
-  "matrix",
-  "matrix3d",
-  "perspective",
-  "rotate",
-  "rotateX",
-  "rotateY",
-  "rotateZ",
-  "rotate3d",
-  "scale",
-  "scaleX",
-  "scaleY",
-  "scaleZ",
-  "scale3d",
-  "skew",
-  "skewX",
-  "skewY",
-  "translate",
-  "translateX",
-  "translateY",
-  "translateZ",
-  "translate3d",
+    // transform
+    "matrix",
+    "matrix3d",
+    "perspective",
+    "rotate",
+    "rotateX",
+    "rotateY",
+    "rotateZ",
+    "rotate3d",
+    "scale",
+    "scaleX",
+    "scaleY",
+    "scaleZ",
+    "scale3d",
+    "skew",
+    "skewX",
+    "skewY",
+    "translate",
+    "translateX",
+    "translateY",
+    "translateZ",
+    "translate3d",
 
-  // motion path
-  "path",
+    // motion path
+    "path",
 
-  "calc",
-  "-webkit-calc",
+    "calc",
+    "-webkit-calc",
 
-  // scroll-snap-type
-  // none
-  "mandatory",
-  "proximity",
-  "from-image",
+    // scroll-snap-type
+    // none
+    "mandatory",
+    "proximity",
+    "from-image",
 
-  // containment
-  // paint
-  "style",
-  "layout",
-  "size",
+    // containment
+    // paint
+    "style",
+    "layout",
+    "size",
 
-  // grid auto-repeat
-  "auto-fill",
-  "auto-fit",
+    // grid auto-repeat
+    "auto-fill",
+    "auto-fit",
 
-  "var",
-  "-internal-variable-value",
+    "var",
+    "-internal-variable-value",
 
-  // break-before, break-after, break-inside
-  "avoid-page",
-  "page",
-  "recto",
-  "verso",
-  "avoid-column",
+    // break-before, break-after, break-inside
+    "avoid-page",
+    "page",
+    "recto",
+    "verso",
+    "avoid-column",
 
-  // shape
-  // rect
-  // round
+    // shape
+    // rect
+    // round
 
-]
+  ],
 }
diff --git a/third_party/WebKit/Source/core/css/PropertyRegistration.cpp b/third_party/WebKit/Source/core/css/PropertyRegistration.cpp
index 71bbc5d70..fc59149 100644
--- a/third_party/WebKit/Source/core/css/PropertyRegistration.cpp
+++ b/third_party/WebKit/Source/core/css/PropertyRegistration.cpp
@@ -4,7 +4,6 @@
 
 #include "core/css/PropertyRegistration.h"
 
-#include "core/animation/CSSValueInterpolationType.h"
 #include "core/css/CSSStyleSheet.h"
 #include "core/css/CSSSyntaxDescriptor.h"
 #include "core/css/CSSValueList.h"
@@ -61,17 +60,6 @@
   return true;
 }
 
-InterpolationTypes interpolationTypesForSyntax(const AtomicString& propertyName,
-                                               const CSSSyntaxDescriptor&) {
-  PropertyHandle property(propertyName);
-  InterpolationTypes interpolationTypes;
-  // TODO(alancutter): Read the syntax descriptor and add the appropriate
-  // CSSInterpolationType subclasses.
-  interpolationTypes.push_back(
-      WTF::makeUnique<CSSValueInterpolationType>(property));
-  return interpolationTypes;
-}
-
 void PropertyRegistration::registerProperty(
     ExecutionContext* executionContext,
     const PropertyDescriptor& descriptor,
@@ -106,7 +94,7 @@
   }
 
   InterpolationTypes interpolationTypes =
-      interpolationTypesForSyntax(atomicName, syntaxDescriptor);
+      syntaxDescriptor.createInterpolationTypes(atomicName);
 
   if (descriptor.hasInitialValue()) {
     CSSTokenizer tokenizer(descriptor.initialValue());
diff --git a/third_party/WebKit/Source/core/css/SVGCSSValueKeywords.json5 b/third_party/WebKit/Source/core/css/SVGCSSValueKeywords.json5
index 23811899..719ae3e 100644
--- a/third_party/WebKit/Source/core/css/SVGCSSValueKeywords.json5
+++ b/third_party/WebKit/Source/core/css/SVGCSSValueKeywords.json5
@@ -1,286 +1,286 @@
 {
-//
-// SVG CSS value names
-//
-
-data: [
-  // CSS_PROP_*_COLOR
   //
-  "aliceblue",
-  "antiquewhite",
-  // aqua
-  "aquamarine",
-  "azure",
-  "beige",
-  "bisque",
-  // black
-  "blanchedalmond",
-  // blue
-  "blueviolet",
-  "brown",
-  "burlywood",
-  "cadetblue",
-  "chartreuse",
-  "chocolate",
-  "coral",
-  "cornflowerblue",
-  "cornsilk",
-  "crimson",
-  "cyan",
-  "darkblue",
-  "darkcyan",
-  "darkgoldenrod",
-  "darkgray",
-  "darkgreen",
-  "darkgrey",
-  "darkkhaki",
-  "darkmagenta",
-  "darkolivegreen",
-  "darkorange",
-  "darkorchid",
-  "darkred",
-  "darksalmon",
-  "darkseagreen",
-  "darkslateblue",
-  "darkslategray",
-  "darkslategrey",
-  "darkturquoise",
-  "darkviolet",
-  "deeppink",
-  "deepskyblue",
-  "dimgray",
-  "dimgrey",
-  "dodgerblue",
-  "firebrick",
-  "floralwhite",
-  "forestgreen",
-  // fuchsia
-  "gainsboro",
-  "ghostwhite",
-  "gold",
-  "goldenrod",
-  // gray
-  // grey
-  // green
-  "greenyellow",
-  "honeydew",
-  "hotpink",
-  "indianred",
-  "indigo",
-  "ivory",
-  "khaki",
-  "lavender",
-  "lavenderblush",
-  "lawngreen",
-  "lemonchiffon",
-  "lightblue",
-  "lightcoral",
-  "lightcyan",
-  "lightgoldenrodyellow",
-  "lightgray",
-  "lightgreen",
-  "lightgrey",
-  "lightpink",
-  "lightsalmon",
-  "lightseagreen",
-  "lightskyblue",
-  "lightslategray",
-  "lightslategrey",
-  "lightsteelblue",
-  "lightyellow",
-  // lime
-  "limegreen",
-  "linen",
-  "magenta",
-  // maroon
-  "mediumaquamarine",
-  "mediumblue",
-  "mediumorchid",
-  "mediumpurple",
-  "mediumseagreen",
-  "mediumslateblue",
-  "mediumspringgreen",
-  "mediumturquoise",
-  "mediumvioletred",
-  "midnightblue",
-  "mintcream",
-  "mistyrose",
-  "moccasin",
-  "navajowhite",
-  // navy
-  "oldlace",
-  // olive
-  "olivedrab",
-  // orange
-  "orangered",
-  "orchid",
-  "palegoldenrod",
-  "palegreen",
-  "paleturquoise",
-  "palevioletred",
-  "papayawhip",
-  "peachpuff",
-  "peru",
-  "pink",
-  "plum",
-  "powderblue",
-  // purple
-  "rebeccapurple",
-  // red
-  "rosybrown",
-  "royalblue",
-  "saddlebrown",
-  "salmon",
-  "sandybrown",
-  "seagreen",
-  "seashell",
-  "sienna",
-  // silver
-  "skyblue",
-  "slateblue",
-  "slategray",
-  "slategrey",
-  "snow",
-  "springgreen",
-  "steelblue",
-  "tan",
-  // teal
-  "thistle",
-  "tomato",
-  "turquoise",
-  "violet",
-  "wheat",
-  // white
-  "whitesmoke",
-  // yellow
-  "yellowgreen",
+  // SVG CSS value names
+  //
 
-  // mask-type / mask-mode
-  "alpha",
-  "luminance",
+  data: [
+    // CSS_PROP_*_COLOR
+    //
+    "aliceblue",
+    "antiquewhite",
+    // aqua
+    "aquamarine",
+    "azure",
+    "beige",
+    "bisque",
+    // black
+    "blanchedalmond",
+    // blue
+    "blueviolet",
+    "brown",
+    "burlywood",
+    "cadetblue",
+    "chartreuse",
+    "chocolate",
+    "coral",
+    "cornflowerblue",
+    "cornsilk",
+    "crimson",
+    "cyan",
+    "darkblue",
+    "darkcyan",
+    "darkgoldenrod",
+    "darkgray",
+    "darkgreen",
+    "darkgrey",
+    "darkkhaki",
+    "darkmagenta",
+    "darkolivegreen",
+    "darkorange",
+    "darkorchid",
+    "darkred",
+    "darksalmon",
+    "darkseagreen",
+    "darkslateblue",
+    "darkslategray",
+    "darkslategrey",
+    "darkturquoise",
+    "darkviolet",
+    "deeppink",
+    "deepskyblue",
+    "dimgray",
+    "dimgrey",
+    "dodgerblue",
+    "firebrick",
+    "floralwhite",
+    "forestgreen",
+    // fuchsia
+    "gainsboro",
+    "ghostwhite",
+    "gold",
+    "goldenrod",
+    // gray
+    // grey
+    // green
+    "greenyellow",
+    "honeydew",
+    "hotpink",
+    "indianred",
+    "indigo",
+    "ivory",
+    "khaki",
+    "lavender",
+    "lavenderblush",
+    "lawngreen",
+    "lemonchiffon",
+    "lightblue",
+    "lightcoral",
+    "lightcyan",
+    "lightgoldenrodyellow",
+    "lightgray",
+    "lightgreen",
+    "lightgrey",
+    "lightpink",
+    "lightsalmon",
+    "lightseagreen",
+    "lightskyblue",
+    "lightslategray",
+    "lightslategrey",
+    "lightsteelblue",
+    "lightyellow",
+    // lime
+    "limegreen",
+    "linen",
+    "magenta",
+    // maroon
+    "mediumaquamarine",
+    "mediumblue",
+    "mediumorchid",
+    "mediumpurple",
+    "mediumseagreen",
+    "mediumslateblue",
+    "mediumspringgreen",
+    "mediumturquoise",
+    "mediumvioletred",
+    "midnightblue",
+    "mintcream",
+    "mistyrose",
+    "moccasin",
+    "navajowhite",
+    // navy
+    "oldlace",
+    // olive
+    "olivedrab",
+    // orange
+    "orangered",
+    "orchid",
+    "palegoldenrod",
+    "palegreen",
+    "paleturquoise",
+    "palevioletred",
+    "papayawhip",
+    "peachpuff",
+    "peru",
+    "pink",
+    "plum",
+    "powderblue",
+    // purple
+    "rebeccapurple",
+    // red
+    "rosybrown",
+    "royalblue",
+    "saddlebrown",
+    "salmon",
+    "sandybrown",
+    "seagreen",
+    "seashell",
+    "sienna",
+    // silver
+    "skyblue",
+    "slateblue",
+    "slategray",
+    "slategrey",
+    "snow",
+    "springgreen",
+    "steelblue",
+    "tan",
+    // teal
+    "thistle",
+    "tomato",
+    "turquoise",
+    "violet",
+    "wheat",
+    // white
+    "whitesmoke",
+    // yellow
+    "yellowgreen",
 
-  // CSS_PROP_CLIP_PATH
-  // CSS_PROP_CLIP_RULE
-  // nonzero and evenodd part of core CSS values now.
+    // mask-type / mask-mode
+    "alpha",
+    "luminance",
 
-  // CSS_PROP_MASK
-  // CSS_PROP_OPACITY
-  "accumulate",
-  "new",
+    // CSS_PROP_CLIP_PATH
+    // CSS_PROP_CLIP_RULE
+    // nonzero and evenodd part of core CSS values now.
 
-  // CSS_PROP_FILTER
-  // CSS_PROP_FLOOD_COLOR
-  //currentColor
+    // CSS_PROP_MASK
+    // CSS_PROP_OPACITY
+    "accumulate",
+    "new",
 
-  // CSS_PROP_FLOOD_OPACITY
-  // CSS_PROP_LIGHTING_COLOR
-  //currentColor
+    // CSS_PROP_FILTER
+    // CSS_PROP_FLOOD_COLOR
+    //currentColor
 
-  // CSS_PROP_STOP_COLOR
-  // CSS_PROP_STOP_OPACITY
-  // CSS_PROP_COLOR_INTERPOLATION
-  //auto
-  "sRGB",
-  "linearRGB",
+    // CSS_PROP_FLOOD_OPACITY
+    // CSS_PROP_LIGHTING_COLOR
+    //currentColor
 
-  // CSS_PROP_COLOR_INTERPOLATION_FILTERS
-  //auto
-  //sRGB
-  //linearRGB
+    // CSS_PROP_STOP_COLOR
+    // CSS_PROP_STOP_OPACITY
+    // CSS_PROP_COLOR_INTERPOLATION
+    //auto
+    "sRGB",
+    "linearRGB",
 
-  // CSS_PROP_COLOR_PROFILE
-  //sRGB
+    // CSS_PROP_COLOR_INTERPOLATION_FILTERS
+    //auto
+    //sRGB
+    //linearRGB
 
-  // CSS_PROP_COLOR_RENDERING
-  //auto
-  //optimizeSpeed
-  //optimizeQuality
+    // CSS_PROP_COLOR_PROFILE
+    //sRGB
 
-  //// CSS_PROP_FILL
-  //currentColor
+    // CSS_PROP_COLOR_RENDERING
+    //auto
+    //optimizeSpeed
+    //optimizeQuality
 
-  // CSS_PROP_FILL_OPACITY
-  // CSS_PROP_FILL_RULE
-  //nonzero
-  //evenodd
+    //// CSS_PROP_FILL
+    //currentColor
 
-  // CSS_PROP_IMAGE_RENDERING
-  //auto
-  //optimizeSpeed
-  //optimizeQuality
+    // CSS_PROP_FILL_OPACITY
+    // CSS_PROP_FILL_RULE
+    //nonzero
+    //evenodd
 
-  // CSS_PROP_MARKER
-  // CSS_PROP_MARKER_END
-  // CSS_PROP_MARKER_MID
-  // CSS_PROP_MARKER_START
-  // CSS_PROP_SHAPE_RENDERING
-  //auto
-  //optimizeSpeed
-  "crispEdges",
-  //geometricPrecision
+    // CSS_PROP_IMAGE_RENDERING
+    //auto
+    //optimizeSpeed
+    //optimizeQuality
 
-  // CSS_PROP_STROKE
-  // CSS_PROP_STROKE_DASHARRAY
-  // CSS_PROP_STROKE_DASHOFFSET
-  // CSS_PROP_STROKE_LINECAP
-  "butt",
-  // round
-  // square
+    // CSS_PROP_MARKER
+    // CSS_PROP_MARKER_END
+    // CSS_PROP_MARKER_MID
+    // CSS_PROP_MARKER_START
+    // CSS_PROP_SHAPE_RENDERING
+    //auto
+    //optimizeSpeed
+    "crispEdges",
+    //geometricPrecision
 
-  // CSS_PROP_STROKE_LINEJOIN
-  "miter",
-  // round
-  "bevel",
+    // CSS_PROP_STROKE
+    // CSS_PROP_STROKE_DASHARRAY
+    // CSS_PROP_STROKE_DASHOFFSET
+    // CSS_PROP_STROKE_LINECAP
+    "butt",
+    // round
+    // square
 
-  // CSS_PROP_STROKE_MITERLIMIT
-  // CSS_PROP_STROKE_OPACITY
-  // CSS_PROP_STROKE_WIDTH
+    // CSS_PROP_STROKE_LINEJOIN
+    "miter",
+    // round
+    "bevel",
 
-  // CSS_PROP_ALIGNMENT_BASELINE
-  //auto
-  // baseline
-  "before-edge",
-  "after-edge",
-  //middle
-  "central",
-  "text-before-edge",
-  "text-after-edge",
-  "ideographic",
-  "hanging",
-  "mathematical",
+    // CSS_PROP_STROKE_MITERLIMIT
+    // CSS_PROP_STROKE_OPACITY
+    // CSS_PROP_STROKE_WIDTH
 
-  // CSS_PROP_BASELINE_SHIFT
-  //baseline
-  // sub
-  // super
+    // CSS_PROP_ALIGNMENT_BASELINE
+    //auto
+    // baseline
+    "before-edge",
+    "after-edge",
+    //middle
+    "central",
+    "text-before-edge",
+    "text-after-edge",
+    "ideographic",
+    "hanging",
+    "mathematical",
 
-  // CSS_PROP_DOMINANT_BASELINE
-  //auto
-  "use-script",
-  "no-change",
-  "reset-size",
+    // CSS_PROP_BASELINE_SHIFT
+    //baseline
+    // sub
+    // super
 
-  // CSS_PROP_GLYPH_ORIENTATION_HORIZONTAL
+    // CSS_PROP_DOMINANT_BASELINE
+    //auto
+    "use-script",
+    "no-change",
+    "reset-size",
 
-  // CSS_PROP_GLYPH_ORIENTATION_VERTICAL
-  // CSS_PROP_TEXT_ANCHOR
-  // start
-  // middle
-  // end
+    // CSS_PROP_GLYPH_ORIENTATION_HORIZONTAL
 
-  // CSS_PROP_BUFFERED_RENDERING
-  // auto
-  // static
-  "dynamic",
+    // CSS_PROP_GLYPH_ORIENTATION_VERTICAL
+    // CSS_PROP_TEXT_ANCHOR
+    // start
+    // middle
+    // end
 
-  // CSS_PROP_VECTOR_EFFECT
-  // none
-  "non-scaling-stroke",
+    // CSS_PROP_BUFFERED_RENDERING
+    // auto
+    // static
+    "dynamic",
 
-  // CSS_PROP_PAINT_ORDER
-  // normal
-  // fill
-  // stroke
-  // markers
-]
+    // CSS_PROP_VECTOR_EFFECT
+    // none
+    "non-scaling-stroke",
+
+    // CSS_PROP_PAINT_ORDER
+    // normal
+    // fill
+    // stroke
+    // markers
+  ]
 }
diff --git a/third_party/WebKit/Source/core/editing/InputMethodController.cpp b/third_party/WebKit/Source/core/editing/InputMethodController.cpp
index 50c4d95d..dc57718 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodController.cpp
+++ b/third_party/WebKit/Source/core/editing/InputMethodController.cpp
@@ -885,7 +885,8 @@
   // Emits an object replacement character for each replaced element so that
   // it is exposed to IME and thus could be deleted by IME on android.
   info.value = plainText(EphemeralRange::rangeOfContents(*element),
-                         TextIteratorEmitsObjectReplacementCharacter);
+                         TextIteratorEmitsObjectReplacementCharacter |
+                             TextIteratorEmitsSpaceForNbsp);
 
   if (info.value.isEmpty())
     return info;
diff --git a/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp b/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
index 7077f02..717609f 100644
--- a/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
+++ b/third_party/WebKit/Source/core/editing/InputMethodControllerTest.cpp
@@ -1111,6 +1111,20 @@
   EXPECT_EQ(WebTextInputTypeTelephone, controller().textInputType());
 }
 
+TEST_F(InputMethodControllerTest, ReflectsSpaceWithoutNbspMangling) {
+  insertHTMLElement("<div id='sample' contenteditable></div>", "sample");
+
+  Vector<CompositionUnderline> underlines;
+  controller().commitText(String("  "), underlines, 0);
+
+  // In a contenteditable, multiple spaces or a space at the edge needs to be
+  // nbsp to affect layout properly, but it confuses some IMEs (particularly
+  // Vietnamese, see crbug.com/663880) to have their spaces reflected back to
+  // them as nbsp.
+  EXPECT_EQ(' ', controller().textInputInfo().value.ascii()[0]);
+  EXPECT_EQ(' ', controller().textInputInfo().value.ascii()[1]);
+}
+
 TEST_F(InputMethodControllerTest, SetCompositionPlainTextWithUnderline) {
   insertHTMLElement("<div id='sample' contenteditable></div>", "sample");
 
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
index 69bc785..dfa646c 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
@@ -168,9 +168,7 @@
       m_handleShadowRoot(false),
       m_firstLetterStartOffset(kInvalidOffset),
       m_remainingTextStartOffset(kInvalidOffset),
-      // The call to emitsOriginalText() must occur after m_behavior is
-      // initialized.
-      m_textState(emitsOriginalText()) {
+      m_textState(m_behavior) {
   DCHECK(start.isNotNull());
   DCHECK(end.isNotNull());
 
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorFlags.h b/third_party/WebKit/Source/core/editing/iterators/TextIteratorFlags.h
index 6f06e8e..8183136c 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorFlags.h
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorFlags.h
@@ -44,6 +44,7 @@
   TextIteratorForWindowFind = 1 << 11,
   TextIteratorExcludeAutofilledValue = 1 << 12,
   TextIteratorCollapseTrailingSpace = 1 << 13,
+  TextIteratorEmitsSpaceForNbsp = 1 << 14,
 };
 typedef unsigned TextIteratorBehaviorFlags;
 
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
index 06262fb..ca82e93 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.cpp
@@ -31,7 +31,7 @@
 
 namespace blink {
 
-TextIteratorTextState::TextIteratorTextState(bool emitsOriginalText)
+TextIteratorTextState::TextIteratorTextState(TextIteratorBehaviorFlags behavior)
     : m_textLength(0),
       m_singleCharacterBuffer(0),
       m_positionNode(nullptr),
@@ -39,7 +39,8 @@
       m_positionEndOffset(0),
       m_hasEmitted(false),
       m_lastCharacter(0),
-      m_emitsOriginalText(emitsOriginalText),
+      m_emitsOriginalText(behavior & TextIteratorEmitsOriginalText),
+      m_emitsSpaceForNbsp(behavior & TextIteratorEmitsSpaceForNbsp),
       m_textStartOffset(0) {}
 
 UChar TextIteratorTextState::characterAt(unsigned index) const {
@@ -149,6 +150,8 @@
   DCHECK(textNode);
   m_text =
       m_emitsOriginalText ? layoutObject->originalText() : layoutObject->text();
+  if (m_emitsSpaceForNbsp)
+    m_text.replace(noBreakSpaceCharacter, spaceCharacter);
   DCHECK(!m_text.isEmpty());
   DCHECK_LE(0, textStartOffset);
   DCHECK_LT(textStartOffset, static_cast<int>(m_text.length()));
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
index fc4cb53..9ad5bad 100644
--- a/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
+++ b/third_party/WebKit/Source/core/editing/iterators/TextIteratorTextState.h
@@ -29,6 +29,7 @@
 #include "core/CoreExport.h"
 #include "core/dom/Range.h"
 #include "core/editing/iterators/ForwardsTextBuffer.h"
+#include "core/editing/iterators/TextIteratorFlags.h"
 #include "wtf/text/WTFString.h"
 
 namespace blink {
@@ -39,7 +40,7 @@
   STACK_ALLOCATED();
 
  public:
-  explicit TextIteratorTextState(bool emitsOriginalText);
+  explicit TextIteratorTextState(TextIteratorBehaviorFlags);
   ~TextIteratorTextState() {}
 
   const String& string() const { return m_text; }
@@ -98,7 +99,8 @@
   // any other content
   bool m_hasEmitted;
   UChar m_lastCharacter;
-  bool m_emitsOriginalText;
+  const bool m_emitsOriginalText;
+  const bool m_emitsSpaceForNbsp;
 
   // Stores the length of :first-letter when we are at the remaining text.
   // Equals to 0 in all other cases.
diff --git a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp
index f8cc545..527414b 100644
--- a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp
+++ b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.cpp
@@ -34,7 +34,10 @@
     : m_frameHost(&frameHost) {}
 
 EventHandlerRegistry::~EventHandlerRegistry() {
-  checkConsistency();
+  for (size_t i = 0; i < EventHandlerClassCount; ++i) {
+    EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
+    checkConsistency(handlerClass);
+  }
 }
 
 bool EventHandlerRegistry::eventTypeToClass(
@@ -73,13 +76,13 @@
 
 const EventTargetSet* EventHandlerRegistry::eventHandlerTargets(
     EventHandlerClass handlerClass) const {
-  checkConsistency();
+  checkConsistency(handlerClass);
   return &m_targets[handlerClass];
 }
 
 bool EventHandlerRegistry::hasEventHandlers(
     EventHandlerClass handlerClass) const {
-  checkConsistency();
+  checkConsistency(handlerClass);
   return m_targets[handlerClass].size();
 }
 
@@ -308,24 +311,22 @@
   }
 }
 
-void EventHandlerRegistry::checkConsistency() const {
+void EventHandlerRegistry::checkConsistency(
+    EventHandlerClass handlerClass) const {
 #if DCHECK_IS_ON()
-  for (size_t i = 0; i < EventHandlerClassCount; ++i) {
-    EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
-    const EventTargetSet* targets = &m_targets[handlerClass];
-    for (const auto& eventTarget : *targets) {
-      if (Node* node = eventTarget.key->toNode()) {
-        // See the comment for |documentDetached| if either of these assertions
-        // fails.
-        ASSERT(node->document().frameHost());
-        ASSERT(node->document().frameHost() == m_frameHost);
-      } else if (LocalDOMWindow* window = eventTarget.key->toLocalDOMWindow()) {
-        // If any of these assertions fail, LocalDOMWindow failed to unregister
-        // its handlers properly.
-        ASSERT(window->frame());
-        ASSERT(window->frame()->host());
-        ASSERT(window->frame()->host() == m_frameHost);
-      }
+  const EventTargetSet* targets = &m_targets[handlerClass];
+  for (const auto& eventTarget : *targets) {
+    if (Node* node = eventTarget.key->toNode()) {
+      // See the comment for |documentDetached| if either of these assertions
+      // fails.
+      DCHECK(node->document().frameHost());
+      DCHECK(node->document().frameHost() == m_frameHost);
+    } else if (LocalDOMWindow* window = eventTarget.key->toLocalDOMWindow()) {
+      // If any of these assertions fail, LocalDOMWindow failed to unregister
+      // its handlers properly.
+      DCHECK(window->frame());
+      DCHECK(window->frame()->host());
+      DCHECK(window->frame()->host() == m_frameHost);
     }
   }
 #endif  // DCHECK_IS_ON()
diff --git a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h
index a6186a0..5802697 100644
--- a/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h
+++ b/third_party/WebKit/Source/core/frame/EventHandlerRegistry.h
@@ -118,7 +118,7 @@
 
   void updateAllEventHandlers(ChangeOperation, EventTarget&);
 
-  void checkConsistency() const;
+  void checkConsistency(EventHandlerClass) const;
 
   Member<FrameHost> m_frameHost;
   EventTargetSet m_targets[EventHandlerClassCount];
diff --git a/third_party/WebKit/Source/core/frame/Settings.json5 b/third_party/WebKit/Source/core/frame/Settings.json5
index 7be353a..7ddfe07 100644
--- a/third_party/WebKit/Source/core/frame/Settings.json5
+++ b/third_party/WebKit/Source/core/frame/Settings.json5
@@ -1,932 +1,932 @@
 {
-// Defines properties which are available on the Settings object.
-//
-// Please think carefully before adding a new Setting.  Some questions to
-// consider are:
-// - Should this be a RuntimeEnabledFeature instead? Settings are for things
-//   which we support either values of at runtime.  Features are set at renderer
-//   process startup and are never changed. Features also tend to be set to a
-//   value based on the platform or the stability of the code in question, where
-//   as settings both codepaths need to be stable.
-// - How will you ensure test coverage of all relevant values of your setting?
-// - Is the default value appropriate for other platforms or ports which may
-//   not be aware of your setting?
-// - Can your setting result in behavior differences observable to web
-//   developers?
-// - Should this setting ideally be removed in the future?  If so please file
-//   a bug and reference it in the comments for your setting.
-//
-// One reason to add a Setting is to manage the risk associated with adding a
-// new feature.  For example, we may choose to ship a new UI behavior or
-// performance optimization to ChromeOS users first (in order to gather feedback
-// and metrics on its use from the wild) before attempting to ship it to
-// Windows.
-//
-// FIXME: Add support for global settings.
-// FIXME: Add support for custom getters/setters.
+  // Defines properties which are available on the Settings object.
+  //
+  // Please think carefully before adding a new Setting.  Some questions to
+  // consider are:
+  // - Should this be a RuntimeEnabledFeature instead? Settings are for things
+  //   which we support either values of at runtime.  Features are set at renderer
+  //   process startup and are never changed. Features also tend to be set to a
+  //   value based on the platform or the stability of the code in question, where
+  //   as settings both codepaths need to be stable.
+  // - How will you ensure test coverage of all relevant values of your setting?
+  // - Is the default value appropriate for other platforms or ports which may
+  //   not be aware of your setting?
+  // - Can your setting result in behavior differences observable to web
+  //   developers?
+  // - Should this setting ideally be removed in the future?  If so please file
+  //   a bug and reference it in the comments for your setting.
+  //
+  // One reason to add a Setting is to manage the risk associated with adding a
+  // new feature.  For example, we may choose to ship a new UI behavior or
+  // performance optimization to ChromeOS users first (in order to gather feedback
+  // and metrics on its use from the wild) before attempting to ship it to
+  // Windows.
+  //
+  // FIXME: Add support for global settings.
+  // FIXME: Add support for custom getters/setters.
 
-// Valid parameters for data entries below.
-parameters: {
+  // Valid parameters for data entries below.
+  parameters: {
     type: {
-        default: "bool"
+      default: "bool"
     },
     initial: {},
     invalidate: {},
-},
-
-data: [
-  {
-    name: "defaultTextEncodingName",
-    type: "String",
   },
 
-  // Do not hide chars typed in password fields immediately, but let the last char stay
-  // visible for N seconds, configured by the passwordEchoDurationInSeconds setting
-  // FIXME: Enable automatically if passwordEchoDurationInSeconds is set to a positive value.
-  {
-    name: "passwordEchoEnabled",
-    initial: false,
-  },
+  data: [
+    {
+      name: "defaultTextEncodingName",
+      type: "String",
+    },
 
-  // Configure how long the last char should say visible in seconds.
-  {
-    name: "passwordEchoDurationInSeconds",
-    initial: 1,
-    type: "double",
-  },
+    // Do not hide chars typed in password fields immediately, but let the last char stay
+    // visible for N seconds, configured by the passwordEchoDurationInSeconds setting
+    // FIXME: Enable automatically if passwordEchoDurationInSeconds is set to a positive value.
+    {
+      name: "passwordEchoEnabled",
+      initial: false,
+    },
 
-  // Sets the magnification value for validation message timer.  If the
-  // magnification value is N, a validation message disappears automatically after
-  // <message length> * N / 1000 seconds.  If N is equal to or less than 0, a
-  // validation message doesn't disappears automaticaly.
-  {
-    name: "validationMessageTimerMagnification",
-    initial: 50,
-    type: "int",
-  },
+    // Configure how long the last char should say visible in seconds.
+    {
+      name: "passwordEchoDurationInSeconds",
+      initial: 1,
+      type: "double",
+    },
 
-  // Number of pixels below which 2D canvas is rendered in software
-  // even if hardware acceleration is enabled.
-  // Hardware acceleration is useful for large canvases where it can avoid the
-  // pixel bandwidth between the CPU and GPU. But GPU acceleration comes at
-  // a price - extra back-buffer and texture copy. Small canvases are also
-  // widely used for stylized fonts. Anti-aliasing text in hardware at that
-  // scale is generally slower. So below a certain size it is better to
-  // draw canvas in software.
-  {
-    name: "minimumAccelerated2dCanvasSize",
-    initial: "257*256",
-    type: "int",
-  },
+    // Sets the magnification value for validation message timer.  If the
+    // magnification value is N, a validation message disappears automatically after
+    // <message length> * N / 1000 seconds.  If N is equal to or less than 0, a
+    // validation message doesn't disappears automaticaly.
+    {
+      name: "validationMessageTimerMagnification",
+      initial: 50,
+      type: "int",
+    },
 
-  {
-    name: "minimumFontSize",
-    initial: 0,
-    invalidate: "Style",
-    type: "int",
-  },
-  {
-    name: "minimumLogicalFontSize",
-    initial: 0,
-    invalidate: "Style",
-    type: "int",
-  },
-  {
-    name: "defaultFontSize",
-    initial: 0,
-    invalidate: "Style",
-    type: "int",
-  },
-  {
-    name: "defaultFixedFontSize",
-    initial: 0,
-    invalidate: "Style",
-    type: "int",
-  },
+    // Number of pixels below which 2D canvas is rendered in software
+    // even if hardware acceleration is enabled.
+    // Hardware acceleration is useful for large canvases where it can avoid the
+    // pixel bandwidth between the CPU and GPU. But GPU acceleration comes at
+    // a price - extra back-buffer and texture copy. Small canvases are also
+    // widely used for stylized fonts. Anti-aliasing text in hardware at that
+    // scale is generally slower. So below a certain size it is better to
+    // draw canvas in software.
+    {
+      name: "minimumAccelerated2dCanvasSize",
+      initial: "257*256",
+      type: "int",
+    },
 
-  {
-    name: "editingBehaviorType",
-    initial: "editingBehaviorTypeForPlatform()",
-    type: "EditingBehaviorType",
-  },
+    {
+      name: "minimumFontSize",
+      initial: 0,
+      invalidate: "Style",
+      type: "int",
+    },
+    {
+      name: "minimumLogicalFontSize",
+      initial: 0,
+      invalidate: "Style",
+      type: "int",
+    },
+    {
+      name: "defaultFontSize",
+      initial: 0,
+      invalidate: "Style",
+      type: "int",
+    },
+    {
+      name: "defaultFixedFontSize",
+      initial: 0,
+      invalidate: "Style",
+      type: "int",
+    },
 
-  {
-    name: "localStorageEnabled",
-    initial: false,
-  },
-  {
-    name: "allowUniversalAccessFromFileURLs",
-    initial: true,
-  },
-  {
-    name: "allowFileAccessFromFileURLs",
-    initial: true,
-  },
-  {
-    name: "javaScriptCanOpenWindowsAutomatically",
-    initial: false,
-  },
-  {
-    name: "supportsMultipleWindows",
-    initial: true,
-  },
-  {
-    name: "javaScriptCanAccessClipboard",
-    initial: false,
-  },
-  {
-    name: "shouldPrintBackgrounds",
-    initial: false,
-  },
-  {
-    name: "shouldClearDocumentBackground",
-    initial: true,
-  },
+    {
+      name: "editingBehaviorType",
+      initial: "editingBehaviorTypeForPlatform()",
+      type: "EditingBehaviorType",
+    },
 
-  {
-    name: "textAreasAreResizable",
-    initial: false,
-    invalidate: "Style",
-  },
-  {
-    name: "acceleratedCompositingEnabled",
-    initial: true,
-    invalidate: "AcceleratedCompositing",
-  },
+    {
+      name: "localStorageEnabled",
+      initial: false,
+    },
+    {
+      name: "allowUniversalAccessFromFileURLs",
+      initial: true,
+    },
+    {
+      name: "allowFileAccessFromFileURLs",
+      initial: true,
+    },
+    {
+      name: "javaScriptCanOpenWindowsAutomatically",
+      initial: false,
+    },
+    {
+      name: "supportsMultipleWindows",
+      initial: true,
+    },
+    {
+      name: "javaScriptCanAccessClipboard",
+      initial: false,
+    },
+    {
+      name: "shouldPrintBackgrounds",
+      initial: false,
+    },
+    {
+      name: "shouldClearDocumentBackground",
+      initial: true,
+    },
 
-  {
-    name: "offlineWebApplicationCacheEnabled",
-    initial: true,
-  },
-  {
-    name: "allowScriptsToCloseWindows",
-    initial: false,
-  },
+    {
+      name: "textAreasAreResizable",
+      initial: false,
+      invalidate: "Style",
+    },
+    {
+      name: "acceleratedCompositingEnabled",
+      initial: true,
+      invalidate: "AcceleratedCompositing",
+    },
 
-  // FIXME: This should really be disabled by default as it makes platforms that
-  // don't support the feature download files they can't use by.
-  // Leaving enabled for now to not change existing behavior.
-  {
-    name: "downloadableBinaryFontsEnabled",
-    initial: true,
-  },
+    {
+      name: "offlineWebApplicationCacheEnabled",
+      initial: true,
+    },
+    {
+      name: "allowScriptsToCloseWindows",
+      initial: false,
+    },
 
-  {
-    name: "xssAuditorEnabled",
-    initial: false,
-  },
+    // FIXME: This should really be disabled by default as it makes platforms that
+    // don't support the feature download files they can't use by.
+    // Leaving enabled for now to not change existing behavior.
+    {
+      name: "downloadableBinaryFontsEnabled",
+      initial: true,
+    },
 
-  {
-    name: "preferCompositingToLCDTextEnabled",
-    initial: false,
-    invalidate: "AcceleratedCompositing",
-  },
+    {
+      name: "xssAuditorEnabled",
+      initial: false,
+    },
 
-  // 3D canvas (WebGL) support.
-  {
-    name: "webGLEnabled",
-    initial: false,
-  },
+    {
+      name: "preferCompositingToLCDTextEnabled",
+      initial: false,
+      invalidate: "AcceleratedCompositing",
+    },
 
-  {
-    name: "webGLErrorsToConsoleEnabled",
-    initial: true,
-  },
-  {
-    name: "antialiased2dCanvasEnabled",
-    initial: true,
-  },
-  {
-    name: "antialiasedClips2dCanvasEnabled",
-    initial: true,
-  },
-  {
-    name: "accelerated2dCanvasMSAASampleCount",
-    initial: 0,
-    type: "int",
-  },
+    // 3D canvas (WebGL) support.
+    {
+      name: "webGLEnabled",
+      initial: false,
+    },
 
-  {
-    name: "hyperlinkAuditingEnabled",
-    initial: false,
-  },
-  {
-    name: "allowRunningOfInsecureContent",
-    initial: true,
-  },
+    {
+      name: "webGLErrorsToConsoleEnabled",
+      initial: true,
+    },
+    {
+      name: "antialiased2dCanvasEnabled",
+      initial: true,
+    },
+    {
+      name: "antialiasedClips2dCanvasEnabled",
+      initial: true,
+    },
+    {
+      name: "accelerated2dCanvasMSAASampleCount",
+      initial: 0,
+      type: "int",
+    },
 
-  {
-    name: "mediaControlsOverlayPlayButtonEnabled",
-    initial: false,
-  },
-  {
-    name: "mediaPlaybackRequiresUserGesture",
-    initial: false,
-  },
+    {
+      name: "hyperlinkAuditingEnabled",
+      initial: false,
+    },
+    {
+      name: "allowRunningOfInsecureContent",
+      initial: true,
+    },
 
-  // This flags overrides mediaPlaybackRequiresUserGesture
-  {
-    name: "crossOriginMediaPlaybackRequiresUserGesture",
-    initial: false,
-  },
+    {
+      name: "mediaControlsOverlayPlayButtonEnabled",
+      initial: false,
+    },
+    {
+      name: "mediaPlaybackRequiresUserGesture",
+      initial: false,
+    },
 
-  {
-    name: "presentationRequiresUserGesture",
-    initial: true,
-  },
+    // This flags overrides mediaPlaybackRequiresUserGesture
+    {
+      name: "crossOriginMediaPlaybackRequiresUserGesture",
+      initial: false,
+    },
 
-  {
-    name: "scrollAnimatorEnabled",
-    initial: true,
-  },
+    {
+      name: "presentationRequiresUserGesture",
+      initial: true,
+    },
 
-  // Used to disable threaded, compositor scrolling for testing purposes.
-  // crbug.com/410974 tracks removal once alternative solutions for selective
-  // main thread scrolling are supported.
-  {
-    name: "threadedScrollingEnabled",
-    initial: true,
-    invalidate: "Style",
-  },
+    {
+      name: "scrollAnimatorEnabled",
+      initial: true,
+    },
 
-  // Used in layout tests for gesture tap highlights. Makes the highlights square
-  // (rather than rounded) to make it possible to reftest the results.
-  {
-    name: "mockGestureTapHighlightsEnabled",
-    initial: false,
-  },
+    // Used to disable threaded, compositor scrolling for testing purposes.
+    // crbug.com/410974 tracks removal once alternative solutions for selective
+    // main thread scrolling are supported.
+    {
+      name: "threadedScrollingEnabled",
+      initial: true,
+      invalidate: "Style",
+    },
 
-  {
-    name: "shouldRespectImageOrientation",
-    initial: false,
-  },
+    // Used in layout tests for gesture tap highlights. Makes the highlights square
+    // (rather than rounded) to make it possible to reftest the results.
+    {
+      name: "mockGestureTapHighlightsEnabled",
+      initial: false,
+    },
 
-  // Limited use by features which behave differently depending on the input
-  // devices available.  For example, the pointer and hover media queries.
-  // Note that we need to be careful when basing behavior or UI on this -
-  // just because a device is present doesn't mean the user cares about it
-  // or uses it (i.e. Chromebook Pixel users generally don't want to give up
-  // screen real estate just because they happen to have a touchscreen).
-  {
-    name: "deviceSupportsTouch",
-    initial: false,
-  },
+    {
+      name: "shouldRespectImageOrientation",
+      initial: false,
+    },
 
-  // This value indicates the number of simultaneous multi-touch points supported
-  // by the currently connected screen/digitizer that supports the most points.
-  // From Pointer Events spec:
-  //   http://www.w3.org/TR/pointerevents///widl-Navigator-maxTouchPoints
-  {
-    name: "maxTouchPoints",
-    initial: 0,
-    type: "int",
-  },
+    // Limited use by features which behave differently depending on the input
+    // devices available.  For example, the pointer and hover media queries.
+    // Note that we need to be careful when basing behavior or UI on this -
+    // just because a device is present doesn't mean the user cares about it
+    // or uses it (i.e. Chromebook Pixel users generally don't want to give up
+    // screen real estate just because they happen to have a touchscreen).
+    {
+      name: "deviceSupportsTouch",
+      initial: false,
+    },
 
-  // Whether touch gestures should be "fuzzed" to nearest touch targets.
-  // It's expected that this is enabled everywhere by default, but it may be
-  // disabled for testing purposes as the algorithm is not yet perfect.
-  // crbug.com/304895 tracks removal once we're satisfied with the algorithm.
-  {
-    name: "touchAdjustmentEnabled",
-    initial: true,
-  },
+    // This value indicates the number of simultaneous multi-touch points supported
+    // by the currently connected screen/digitizer that supports the most points.
+    // From Pointer Events spec:
+    //   http://www.w3.org/TR/pointerevents///widl-Navigator-maxTouchPoints
+    {
+      name: "maxTouchPoints",
+      initial: 0,
+      type: "int",
+    },
 
-  // Determines whether WebViewClient::didTapMultipleTargets will be used for
-  // touch disambiguation.
-  {
-    name: "multiTargetTapNotificationEnabled",
-    initial: true,
-  },
+    // Whether touch gestures should be "fuzzed" to nearest touch targets.
+    // It's expected that this is enabled everywhere by default, but it may be
+    // disabled for testing purposes as the algorithm is not yet perfect.
+    // crbug.com/304895 tracks removal once we're satisfied with the algorithm.
+    {
+      name: "touchAdjustmentEnabled",
+      initial: true,
+    },
 
-  {
-    name: "syncXHRInDocumentsEnabled",
-    initial: true,
-  },
-  {
-    name: "cookieEnabled",
-    initial: true,
-  },
-  {
-    name: "navigateOnDragDrop",
-    initial: true,
-  },
-  {
-    name: "DOMPasteAllowed",
-    initial: false,
-  },
+    // Determines whether WebViewClient::didTapMultipleTargets will be used for
+    // touch disambiguation.
+    {
+      name: "multiTargetTapNotificationEnabled",
+      initial: true,
+    },
 
-  {
-    name: "allowCustomScrollbarInMainFrame",
-    initial: true,
-  },
-  {
-    name: "webSecurityEnabled",
-    initial: true,
-  },
+    {
+      name: "syncXHRInDocumentsEnabled",
+      initial: true,
+    },
+    {
+      name: "cookieEnabled",
+      initial: true,
+    },
+    {
+      name: "navigateOnDragDrop",
+      initial: true,
+    },
+    {
+      name: "DOMPasteAllowed",
+      initial: false,
+    },
 
-  // Special keyboard navigation mode intented for platforms with no
-  // proper mouse or touch support, such as a TV controller with a remote.
-  {
-    name: "spatialNavigationEnabled",
-    initial: false,
-  },
+    {
+      name: "allowCustomScrollbarInMainFrame",
+      initial: true,
+    },
+    {
+      name: "webSecurityEnabled",
+      initial: true,
+    },
 
-  // This setting adds a means to enable/disable touch initiated drag & drop. If
-  // enabled, the user can initiate drag using long press.
-  // crbug.com/304894 tracks removal once it's been enabled on all platforms.
-  {
-    name: "touchDragDropEnabled",
-    initial: false,
-  },
+    // Special keyboard navigation mode intented for platforms with no
+    // proper mouse or touch support, such as a TV controller with a remote.
+    {
+      name: "spatialNavigationEnabled",
+      initial: false,
+    },
 
-  // Some apps could have a default video poster if it is not set.
-  {
-    name: "defaultVideoPosterURL",
-    type: "String",
-  },
+    // This setting adds a means to enable/disable touch initiated drag & drop. If
+    // enabled, the user can initiate drag using long press.
+    // crbug.com/304894 tracks removal once it's been enabled on all platforms.
+    {
+      name: "touchDragDropEnabled",
+      initial: false,
+    },
 
-  {
-    name: "smartInsertDeleteEnabled",
-    initial: false,
-  },
-  {
-    name: "selectTrailingWhitespaceEnabled",
-    initial: "defaultSelectTrailingWhitespaceEnabled",
-  },
+    // Some apps could have a default video poster if it is not set.
+    {
+      name: "defaultVideoPosterURL",
+      type: "String",
+    },
 
-  {
-    name: "selectionIncludesAltImageText",
-    initial: false,
-  },
+    {
+      name: "smartInsertDeleteEnabled",
+      initial: false,
+    },
+    {
+      name: "selectTrailingWhitespaceEnabled",
+      initial: "defaultSelectTrailingWhitespaceEnabled",
+    },
 
-  {
-    name: "selectionStrategy",
-    initial: "SelectionStrategy::Character",
-    type: "SelectionStrategy",
-  },
+    {
+      name: "selectionIncludesAltImageText",
+      initial: false,
+    },
 
-  //////////////// Settings used by Android WebView below ////////////////
+    {
+      name: "selectionStrategy",
+      initial: "SelectionStrategy::Character",
+      type: "SelectionStrategy",
+    },
 
-  {
-    name: "useLegacyBackgroundSizeShorthandBehavior",
-    initial: false,
-  },
+    //////////////// Settings used by Android WebView below ////////////////
 
-  // This quirk is to maintain compatibility with Android apps built on
-  // the Android SDK prior to and including version 18.
-  // Presumably, this can be removed any time after 2015.
-  // See http://crbug.com/282130.
-  {
-    name: "viewportMetaZeroValuesQuirk",
-    initial: false,
-  },
+    {
+      name: "useLegacyBackgroundSizeShorthandBehavior",
+      initial: false,
+    },
 
-  // Another Android SDK <= 18 quirk, removable 2015.
-  // See http://crbug.com/295287
-  {
-    name: "ignoreMainFrameOverflowHiddenQuirk",
-    initial: false,
-  },
+    // This quirk is to maintain compatibility with Android apps built on
+    // the Android SDK prior to and including version 18.
+    // Presumably, this can be removed any time after 2015.
+    // See http://crbug.com/282130.
+    {
+      name: "viewportMetaZeroValuesQuirk",
+      initial: false,
+    },
 
-  // Yet another Android SDK <= 18 quirk, removable 2015.
-  // See http://crbug.com/305236
-  {
-    name: "reportScreenSizeInPhysicalPixelsQuirk",
-    initial: false,
-  },
+    // Another Android SDK <= 18 quirk, removable 2015.
+    // See http://crbug.com/295287
+    {
+      name: "ignoreMainFrameOverflowHiddenQuirk",
+      initial: false,
+    },
 
-  // One more Android SDK <= 18 quirk, removable 2015.
-  // See http://crbug.com/306548
-  {
-    name: "viewportMetaMergeContentQuirk",
-    initial: false,
-  },
+    // Yet another Android SDK <= 18 quirk, removable 2015.
+    // See http://crbug.com/305236
+    {
+      name: "reportScreenSizeInPhysicalPixelsQuirk",
+      initial: false,
+    },
 
-  // This quirk is to maintain compatibility with Android apps.
-  // It will be possible to remove it once WebSettings.{get|set}UseWideViewPort
-  // API function will be removed.
-  // See http://crbug.com/288037.
-  {
-    name: "wideViewportQuirkEnabled",
-    initial: false,
-  },
+    // One more Android SDK <= 18 quirk, removable 2015.
+    // See http://crbug.com/306548
+    {
+      name: "viewportMetaMergeContentQuirk",
+      initial: false,
+    },
 
-  // Used by the android_webview to support a horizontal height auto-sizing
-  // mode.
-  {
-    name: "forceZeroLayoutHeight",
-    initial: false,
-    invalidate: "ViewportDescription",
-  },
+    // This quirk is to maintain compatibility with Android apps.
+    // It will be possible to remove it once WebSettings.{get|set}UseWideViewPort
+    // API function will be removed.
+    // See http://crbug.com/288037.
+    {
+      name: "wideViewportQuirkEnabled",
+      initial: false,
+    },
 
-  {
-    name: "mainFrameClipsContent",
-    initial: true,
-  },
+    // Used by the android_webview to support a horizontal height auto-sizing
+    // mode.
+    {
+      name: "forceZeroLayoutHeight",
+      initial: false,
+      invalidate: "ViewportDescription",
+    },
 
-  // For android.webkit.WebSettings.setUseWideViewport()
-  // http://developer.android.com/reference/android/webkit/WebSettings.html//setUseWideViewPort(boolean)
-  {
-    name: "useWideViewport",
-    initial: true,
-    invalidate: "ViewportDescription",
-  },
+    {
+      name: "mainFrameClipsContent",
+      initial: true,
+    },
 
-  // For android.webkit.WebSettings.setLoadWithOverviewMode()
-  // http://developer.android.com/reference/android/webkit/WebSettings.html//setLoadWithOverviewMode(boolean)
-  {
-    name: "loadWithOverviewMode",
-    initial: true,
-    invalidate: "ViewportDescription",
-  },
+    // For android.webkit.WebSettings.setUseWideViewport()
+    // http://developer.android.com/reference/android/webkit/WebSettings.html//setUseWideViewPort(boolean)
+    {
+      name: "useWideViewport",
+      initial: true,
+      invalidate: "ViewportDescription",
+    },
 
-  // Used by android_webview to support legacy apps that inject script into a top-level initial empty
-  // document and expect it to persist on navigation, even though the origin is unique. Note that this
-  // behavior violates the requirements described by [Initialising a new Document object] in
-  // https://html.spec.whatwg.org/multipage/browsers.html//navigating-across-documents.
-  {
-    name: "shouldReuseGlobalForUnownedMainFrame",
-    initial: false,
-  },
+    // For android.webkit.WebSettings.setLoadWithOverviewMode()
+    // http://developer.android.com/reference/android/webkit/WebSettings.html//setLoadWithOverviewMode(boolean)
+    {
+      name: "loadWithOverviewMode",
+      initial: true,
+      invalidate: "ViewportDescription",
+    },
 
-  //////////////// End of settings used by Android WebView ////////////////
+    // Used by android_webview to support legacy apps that inject script into a top-level initial empty
+    // document and expect it to persist on navigation, even though the origin is unique. Note that this
+    // behavior violates the requirements described by [Initialising a new Document object] in
+    // https://html.spec.whatwg.org/multipage/browsers.html//navigating-across-documents.
+    {
+      name: "shouldReuseGlobalForUnownedMainFrame",
+      initial: false,
+    },
 
+    //////////////// End of settings used by Android WebView ////////////////
 
-  // Touch based text selection and editing on desktop.
-  // crbug.com/304873 tracks removal once it's been enabled on all platforms.
-  {
-    name: "touchEditingEnabled",
-    initial: false,
-  },
 
-  // If true, scrollers will use overlay scrollbars.  These do not take up any
-  // layout width, are drawn using solid color quads by the compositor, and fade away
-  // after a timeout.
-  {
-    name: "useSolidColorScrollbars",
-    initial: false,
-  },
+    // Touch based text selection and editing on desktop.
+    // crbug.com/304873 tracks removal once it's been enabled on all platforms.
+    {
+      name: "touchEditingEnabled",
+      initial: false,
+    },
 
-  // Experiment to have all APIs reflect the layout viewport.
-  // crbug.com/489206 tracks the experiment.
-  {
-    name: "inertVisualViewport",
-    initial: false,
-  },
+    // If true, scrollers will use overlay scrollbars.  These do not take up any
+    // layout width, are drawn using solid color quads by the compositor, and fade away
+    // after a timeout.
+    {
+      name: "useSolidColorScrollbars",
+      initial: false,
+    },
 
-  // The rubber-band overscroll effect is implemented in Blink and is being moved
-  // to the compositor thread. This will be set to true and eventually removed.
-  // crbug.com/133097
-  {
-    name: "rubberBandingOnCompositorThread",
-    initial: false,
-  },
+    // Experiment to have all APIs reflect the layout viewport.
+    // crbug.com/489206 tracks the experiment.
+    {
+      name: "inertVisualViewport",
+      initial: false,
+    },
 
-  // Font scale factor for accessibility, applied as part of text autosizing.
-  {
-    name: "accessibilityFontScaleFactor",
-    initial: "1.0",
-    invalidate: "TextAutosizing",
-    type: "double",
-  },
+    // The rubber-band overscroll effect is implemented in Blink and is being moved
+    // to the compositor thread. This will be set to true and eventually removed.
+    // crbug.com/133097
+    {
+      name: "rubberBandingOnCompositorThread",
+      initial: false,
+    },
+
+    // Font scale factor for accessibility, applied as part of text autosizing.
+    {
+      name: "accessibilityFontScaleFactor",
+      initial: "1.0",
+      invalidate: "TextAutosizing",
+      type: "double",
+    },
 
-  // Only used by Layout Tests and inspector emulation.
-  {
-    name: "mediaTypeOverride",
-    initial: "\"\"",
-    invalidate: "MediaQuery",
-    type: "String",
-  },
-  {
-    name: "displayModeOverride",
-    initial: "WebDisplayModeUndefined",
-    invalidate: "MediaQuery",
-    type: "WebDisplayMode",
-  },
+    // Only used by Layout Tests and inspector emulation.
+    {
+      name: "mediaTypeOverride",
+      initial: "\"\"",
+      invalidate: "MediaQuery",
+      type: "String",
+    },
+    {
+      name: "displayModeOverride",
+      initial: "WebDisplayModeUndefined",
+      invalidate: "MediaQuery",
+      type: "WebDisplayMode",
+    },
 
-  // loadsImagesAutomatically only suppresses the network load of
-  // the image URL. A cached image will still be rendered if requested.
-  {
-    name: "loadsImagesAutomatically",
-    initial: false,
-    invalidate: "ImageLoading",
-  },
-  {
-    name: "imagesEnabled",
-    initial: true,
-    invalidate: "ImageLoading",
-  },
-  {
-    name: "imageAnimationPolicy",
-    initial: "ImageAnimationPolicyAllowed",
-    type: "ImageAnimationPolicy",
-  },
+    // loadsImagesAutomatically only suppresses the network load of
+    // the image URL. A cached image will still be rendered if requested.
+    {
+      name: "loadsImagesAutomatically",
+      initial: false,
+      invalidate: "ImageLoading",
+    },
+    {
+      name: "imagesEnabled",
+      initial: true,
+      invalidate: "ImageLoading",
+    },
+    {
+      name: "imageAnimationPolicy",
+      initial: "ImageAnimationPolicyAllowed",
+      type: "ImageAnimationPolicy",
+    },
 
-  // Number of outstanding and pending tokens allowed in the background HTML
-  // parser. A value of 0 indicates the parser should use its default value.
-  {
-    name: "backgroundHtmlParserOutstandingTokenLimit",
-    initial: 0,
-    type: "unsigned",
-  },
-  {
-    name: "backgroundHtmlParserPendingTokenLimit",
-    initial: 0,
-    type: "unsigned",
-  },
+    // Number of outstanding and pending tokens allowed in the background HTML
+    // parser. A value of 0 indicates the parser should use its default value.
+    {
+      name: "backgroundHtmlParserOutstandingTokenLimit",
+      initial: 0,
+      type: "unsigned",
+    },
+    {
+      name: "backgroundHtmlParserPendingTokenLimit",
+      initial: 0,
+      type: "unsigned",
+    },
 
-  // Html preload scanning is a fast, early scan of HTML documents to find loadable
-  // resources before the parser advances to them. If it is disabled, resources will
-  // be loaded later.
-  {
-    name: "doHtmlPreloadScanning",
-    initial: true,
-  },
+    // Html preload scanning is a fast, early scan of HTML documents to find loadable
+    // resources before the parser advances to them. If it is disabled, resources will
+    // be loaded later.
+    {
+      name: "doHtmlPreloadScanning",
+      initial: true,
+    },
 
-  {
-    name: "pluginsEnabled",
-    initial: false,
-  },
+    {
+      name: "pluginsEnabled",
+      initial: false,
+    },
 
-  {
-    name: "viewportEnabled",
-    initial: false,
-    invalidate: "ViewportDescription",
-  },
-  {
-    name: "viewportMetaEnabled",
-    initial: false,
-    invalidate: "ViewportDescription",
-  },
+    {
+      name: "viewportEnabled",
+      initial: false,
+      invalidate: "ViewportDescription",
+    },
+    {
+      name: "viewportMetaEnabled",
+      initial: false,
+      invalidate: "ViewportDescription",
+    },
 
-  {
-    name: "dnsPrefetchingEnabled",
-    initial: false,
-    invalidate: "DNSPrefetching",
-  },
+    {
+      name: "dnsPrefetchingEnabled",
+      initial: false,
+      invalidate: "DNSPrefetching",
+    },
 
-  {
-    name: "dataSaverEnabled",
-    initial: false,
-  },
+    {
+      name: "dataSaverEnabled",
+      initial: false,
+    },
 
-  // FIXME: This is a temporary flag and should be removed
-  // when squashing is ready. (crbug.com/261605)
-  {
-    name: "layerSquashingEnabled",
-    initial: false,
-  },
+    // FIXME: This is a temporary flag and should be removed
+    // when squashing is ready. (crbug.com/261605)
+    {
+      name: "layerSquashingEnabled",
+      initial: false,
+    },
 
-  // Clients that execute script should call ScriptController::canExecuteScripts()
-  // instead of this function. ScriptController::canExecuteScripts() checks the
-  // HTML sandbox, plugin sandboxing, and other important details.
-  {
-    name: "scriptEnabled",
-    initial: false,
-  },
+    // Clients that execute script should call ScriptController::canExecuteScripts()
+    // instead of this function. ScriptController::canExecuteScripts() checks the
+    // HTML sandbox, plugin sandboxing, and other important details.
+    {
+      name: "scriptEnabled",
+      initial: false,
+    },
 
-  // Forces initialization of main world, even if no scripts will be executed.
-  // Used by inspector to report all contexts.
-  {
-    name: "forceMainWorldInitialization",
-    initial: false,
-    invalidate: "DOMWorlds",
-  },
+    // Forces initialization of main world, even if no scripts will be executed.
+    // Used by inspector to report all contexts.
+    {
+      name: "forceMainWorldInitialization",
+      initial: false,
+      invalidate: "DOMWorlds",
+    },
 
-  // Compensates for poor text legibility on mobile devices. This value is
-  // multiplied by the font scale factor when performing text autosizing of
-  // websites that do not set an explicit viewport description.
-  {
-    name: "deviceScaleAdjustment",
-    initial: "1.0",
-    invalidate: "TextAutosizing",
-    type: "double",
-  },
+    // Compensates for poor text legibility on mobile devices. This value is
+    // multiplied by the font scale factor when performing text autosizing of
+    // websites that do not set an explicit viewport description.
+    {
+      name: "deviceScaleAdjustment",
+      initial: "1.0",
+      invalidate: "TextAutosizing",
+      type: "double",
+    },
 
-  // This value indicates the maximum number of bytes a document is allowed to
-  // transmit in Beacons (via navigator.sendBeacon()) -- Beacons are intended to be
-  // smaller payloads transmitted as a page is unloading, not a general (one-way)
-  // network transmission API. The spec <https://w3c.github.io/beacon/> does not
-  // proscribe an upper limit, but allows for it -- the underlying API will return
-  // 'false' in that case.
-  {
-    name: "maxBeaconTransmission",
-    initial: 65536,
-    type: "int",
-  },
+    // This value indicates the maximum number of bytes a document is allowed to
+    // transmit in Beacons (via navigator.sendBeacon()) -- Beacons are intended to be
+    // smaller payloads transmitted as a page is unloading, not a general (one-way)
+    // network transmission API. The spec <https://w3c.github.io/beacon/> does not
+    // proscribe an upper limit, but allows for it -- the underlying API will return
+    // 'false' in that case.
+    {
+      name: "maxBeaconTransmission",
+      initial: 65536,
+      type: "int",
+    },
 
-  // This value is set to false if the platform does not support fullscreen.
-  // When set to false all the requests to enter fullscreen will return an error
-  // (fullscreenerror or webkitfullscreenerror) as specified in the standard:
-  // http://fullscreen.spec.whatwg.org///dom-element-requestfullscreen
-  {
-    name: "fullscreenSupported",
-    initial: true,
-  },
+    // This value is set to false if the platform does not support fullscreen.
+    // When set to false all the requests to enter fullscreen will return an error
+    // (fullscreenerror or webkitfullscreenerror) as specified in the standard:
+    // http://fullscreen.spec.whatwg.org///dom-element-requestfullscreen
+    {
+      name: "fullscreenSupported",
+      initial: true,
+    },
 
-  // V8 supports different types of caching. Used by V8 bindings.
-  {
-    name: "v8CacheOptions",
-    initial: "V8CacheOptionsDefault",
-    type: "V8CacheOptions",
-  },
+    // V8 supports different types of caching. Used by V8 bindings.
+    {
+      name: "v8CacheOptions",
+      initial: "V8CacheOptionsDefault",
+      type: "V8CacheOptions",
+    },
 
-  // V8 code cache for CacheStorage supports three types of strategies (none, normal and aggressive).
-  {
-    name: "v8CacheStrategiesForCacheStorage",
-    initial: "V8CacheStrategiesForCacheStorage::Default",
-    type: "V8CacheStrategiesForCacheStorage",
-  },
+    // V8 code cache for CacheStorage supports three types of strategies (none, normal and aggressive).
+    {
+      name: "v8CacheStrategiesForCacheStorage",
+      initial: "V8CacheStrategiesForCacheStorage::Default",
+      type: "V8CacheStrategiesForCacheStorage",
+    },
 
-  // These values are bit fields for the properties of available pointing devices
-  // and may take on multiple values (e.g. laptop with touchpad and touchscreen
-  // has pointerType coarse *and* fine).
-  {
-    name: "availablePointerTypes",
-    initial: "PointerTypeNone",
-    invalidate: "MediaQuery",
-    type: "int",
-  },
-  {
-    name: "availableHoverTypes",
-    initial: "HoverTypeNone",
-    invalidate: "MediaQuery",
-    type: "int",
-  },
+    // These values are bit fields for the properties of available pointing devices
+    // and may take on multiple values (e.g. laptop with touchpad and touchscreen
+    // has pointerType coarse *and* fine).
+    {
+      name: "availablePointerTypes",
+      initial: "PointerTypeNone",
+      invalidate: "MediaQuery",
+      type: "int",
+    },
+    {
+      name: "availableHoverTypes",
+      initial: "HoverTypeNone",
+      invalidate: "MediaQuery",
+      type: "int",
+    },
 
-  // These values specify properties of the user's primary pointing device only.
-  {
-    name: "primaryPointerType",
-    initial: "PointerTypeNone",
-    invalidate: "MediaQuery",
-    type: "PointerType",
-  },
-  {
-    name: "primaryHoverType",
-    initial: "HoverTypeNone",
-    invalidate: "MediaQuery",
-    type: "HoverType",
-  },
+    // These values specify properties of the user's primary pointing device only.
+    {
+      name: "primaryPointerType",
+      initial: "PointerTypeNone",
+      invalidate: "MediaQuery",
+      type: "PointerType",
+    },
+    {
+      name: "primaryHoverType",
+      initial: "HoverTypeNone",
+      invalidate: "MediaQuery",
+      type: "HoverType",
+    },
 
-  // Whether accessibility support is enabled at all.
-  {
-    name: "accessibilityEnabled",
-    initial: false,
-    invalidate: "AccessibilityState",
-  },
+    // Whether accessibility support is enabled at all.
+    {
+      name: "accessibilityEnabled",
+      initial: false,
+      invalidate: "AccessibilityState",
+    },
 
-  // If true, the value in password fields is exposed to assistive technologies.
-  {
-    name: "accessibilityPasswordValuesEnabled",
-    initial: false,
-  },
+    // If true, the value in password fields is exposed to assistive technologies.
+    {
+      name: "accessibilityPasswordValuesEnabled",
+      initial: false,
+    },
 
-  // If true, static text nodes expose inline text box children.
-  {
-    name: "inlineTextBoxAccessibilityEnabled",
-    initial: false,
-  },
+    // If true, static text nodes expose inline text box children.
+    {
+      name: "inlineTextBoxAccessibilityEnabled",
+      initial: false,
+    },
 
-  // If true, context menu will be shown on mouse up instead of mouse down.
-  // Typically enabled on Windows to match platform convention.
-  {
-    name: "showContextMenuOnMouseUp",
-    initial: false,
-  },
+    // If true, context menu will be shown on mouse up instead of mouse down.
+    // Typically enabled on Windows to match platform convention.
+    {
+      name: "showContextMenuOnMouseUp",
+      initial: false,
+    },
 
-  // If true, context menu will be shown on any long press event.
-  // Used on Android to prevent a context menu from being shown in certain situations
-  // (i.e. long pressing an empty div)
-  {
-    name: "alwaysShowContextMenuOnTouch",
-    initial: true,
-  },
+    // If true, context menu will be shown on any long press event.
+    // Used on Android to prevent a context menu from being shown in certain situations
+    // (i.e. long pressing an empty div)
+    {
+      name: "alwaysShowContextMenuOnTouch",
+      initial: true,
+    },
 
-  {
-    name: "disableReadingFromCanvas",
-    initial: false,
-  },
-  {
-    name: "strictMixedContentChecking",
-    initial: false,
-  },
-  {
-    name: "strictMixedContentCheckingForPlugin",
-    initial: false,
-  },
-  {
-    name: "strictPowerfulFeatureRestrictions",
-    initial: false,
-  },
-  {
-    name: "strictlyBlockBlockableMixedContent",
-    initial: false,
-  },
-  {
-    name: "allowGeolocationOnInsecureOrigins",
-    initial: false,
-  },
-  {
-    name: "logDnsPrefetchAndPreconnect",
-    initial: false,
-  },
-  {
-    name: "logPreload",
-    initial: false,
-  },
+    {
+      name: "disableReadingFromCanvas",
+      initial: false,
+    },
+    {
+      name: "strictMixedContentChecking",
+      initial: false,
+    },
+    {
+      name: "strictMixedContentCheckingForPlugin",
+      initial: false,
+    },
+    {
+      name: "strictPowerfulFeatureRestrictions",
+      initial: false,
+    },
+    {
+      name: "strictlyBlockBlockableMixedContent",
+      initial: false,
+    },
+    {
+      name: "allowGeolocationOnInsecureOrigins",
+      initial: false,
+    },
+    {
+      name: "logDnsPrefetchAndPreconnect",
+      initial: false,
+    },
+    {
+      name: "logPreload",
+      initial: false,
+    },
 
-  // These values specify the UA intial viewport style.
-  // It is dynamically set by the inspector for mobile emulation and can be
-  // used by content embedders to specify custom style on certain platforms.
-  {
-    name: "viewportStyle",
-    initial: "WebViewportStyle::Default",
-    invalidate: "ViewportRule",
-    type: "WebViewportStyle",
-  },
+    // These values specify the UA intial viewport style.
+    // It is dynamically set by the inspector for mobile emulation and can be
+    // used by content embedders to specify custom style on certain platforms.
+    {
+      name: "viewportStyle",
+      initial: "WebViewportStyle::Default",
+      invalidate: "ViewportRule",
+      type: "WebViewportStyle",
+    },
 
-  // Automatic track selection is performed based on user preference for track kind specified
-  // by this setting.
-  {
-    name: "textTrackKindUserPreference",
-    initial: "TextTrackKindUserPreference::Default",
-    invalidate: "TextTrackKindUserPreference",
-    type: "TextTrackKindUserPreference",
-  },
+    // Automatic track selection is performed based on user preference for track kind specified
+    // by this setting.
+    {
+      name: "textTrackKindUserPreference",
+      initial: "TextTrackKindUserPreference::Default",
+      invalidate: "TextTrackKindUserPreference",
+      type: "TextTrackKindUserPreference",
+    },
 
-  // User style overrides for captions and subtitles
-  {
-    name: "textTrackBackgroundColor",
-    type: "String",
-  },
-  {
-    name: "textTrackFontFamily",
-    type: "String",
-  },
-  {
-    name: "textTrackFontStyle",
-    type: "String",
-  },
-  {
-    name: "textTrackFontVariant",
-    type: "String",
-  },
-  {
-    name: "textTrackTextColor",
-    type: "String",
-  },
-  {
-    name: "textTrackTextShadow",
-    type: "String",
-  },
-  {
-    name: "textTrackTextSize",
-    type: "String",
-  },
+    // User style overrides for captions and subtitles
+    {
+      name: "textTrackBackgroundColor",
+      type: "String",
+    },
+    {
+      name: "textTrackFontFamily",
+      type: "String",
+    },
+    {
+      name: "textTrackFontStyle",
+      type: "String",
+    },
+    {
+      name: "textTrackFontVariant",
+      type: "String",
+    },
+    {
+      name: "textTrackTextColor",
+      type: "String",
+    },
+    {
+      name: "textTrackTextShadow",
+      type: "String",
+    },
+    {
+      name: "textTrackTextSize",
+      type: "String",
+    },
 
-  // Margin for title-safe placement of cues with overscan, gives top and bottom margin size as
-  // percentage of video element height (for horizontal text) into which cues will not be placed.
-  {
-    name: "textTrackMarginPercentage",
-    initial: 0,
-    type: "double",
-  },
+    // Margin for title-safe placement of cues with overscan, gives top and bottom margin size as
+    // percentage of video element height (for horizontal text) into which cues will not be placed.
+    {
+      name: "textTrackMarginPercentage",
+      initial: 0,
+      type: "double",
+    },
 
-  {
-    name: "lowPriorityIframes",
-    initial: false,
-  },
+    {
+      name: "lowPriorityIframes",
+      initial: false,
+    },
 
-  {
-    name: "progressBarCompletion",
-    initial: "ProgressBarCompletion::LoadEvent",
-    type: "ProgressBarCompletion",
-  },
+    {
+      name: "progressBarCompletion",
+      initial: "ProgressBarCompletion::LoadEvent",
+      type: "ProgressBarCompletion",
+    },
 
-  {
-    name: "historyEntryRequiresUserGesture",
-    initial: false,
-  },
+    {
+      name: "historyEntryRequiresUserGesture",
+      initial: false,
+    },
 
-  // Do we want to try to save screen real estate in the media player by hiding
-  // the volume slider / mute button?
-  {
-    name: "preferHiddenVolumeControls",
-    initial: false,
-  },
+    // Do we want to try to save screen real estate in the media player by hiding
+    // the volume slider / mute button?
+    {
+      name: "preferHiddenVolumeControls",
+      initial: false,
+    },
 
-  // Whether to disallow network fetches for parser blocking scripts in the main
-  // frame inserted via document.write, for users on 2G or connections that are
-  // effectively 2G.
-  {
-    name: "disallowFetchForDocWrittenScriptsInMainFrameIfEffectively2G",
-    initial: false,
-  },
+    // Whether to disallow network fetches for parser blocking scripts in the main
+    // frame inserted via document.write, for users on 2G or connections that are
+    // effectively 2G.
+    {
+      name: "disallowFetchForDocWrittenScriptsInMainFrameIfEffectively2G",
+      initial: false,
+    },
 
-  // Whether to disallow network fetches for parser blocking scripts in the main
-  // frame inserted via document.write, for users on slow connections.
-  {
-    name: "disallowFetchForDocWrittenScriptsInMainFrameOnSlowConnections",
-    initial: false,
-  },
+    // Whether to disallow network fetches for parser blocking scripts in the main
+    // frame inserted via document.write, for users on slow connections.
+    {
+      name: "disallowFetchForDocWrittenScriptsInMainFrameOnSlowConnections",
+      initial: false,
+    },
 
-  // Whether to disallow network fetches for parser blocking scripts in the main
-  // frame inserted via document.write, regardless of connection type.
-  {
-    name: "disallowFetchForDocWrittenScriptsInMainFrame",
-    initial: false,
-  },
+    // Whether to disallow network fetches for parser blocking scripts in the main
+    // frame inserted via document.write, regardless of connection type.
+    {
+      name: "disallowFetchForDocWrittenScriptsInMainFrame",
+      initial: false,
+    },
 
-  // Whether to invalidate device-dependent media queries and restore scroll positions
-  // on frame resize assuming device rotation.
-  {
-    name: "mainFrameResizesAreOrientationChanges",
-    initial: false,
-  },
+    // Whether to invalidate device-dependent media queries and restore scroll positions
+    // on frame resize assuming device rotation.
+    {
+      name: "mainFrameResizesAreOrientationChanges",
+      initial: false,
+    },
 
-  // Ability to override the default 'passive' value in AddEventListenerOptions. This
-  // is useful to demonstrate the power of passive event listeners. This can be removed
-  // when there is greater adoption, interventions to force it on and associated devtools
-  // to enable it have been shipped.
-  {
-    name: "passiveListenerDefault",
-    initial: "PassiveListenerDefault::False",
-    type: "PassiveListenerDefault",
-  },
+    // Ability to override the default 'passive' value in AddEventListenerOptions. This
+    // is useful to demonstrate the power of passive event listeners. This can be removed
+    // when there is greater adoption, interventions to force it on and associated devtools
+    // to enable it have been shipped.
+    {
+      name: "passiveListenerDefault",
+      initial: "PassiveListenerDefault::False",
+      type: "PassiveListenerDefault",
+    },
 
-  // Use default interpolation quality to scale bitmap images if quality is not determined
-  // in other ways. This can help us writing reftests containing scaled images.
-  {
-    name: "useDefaultImageInterpolationQuality",
-    initial: false,
-  },
+    // Use default interpolation quality to scale bitmap images if quality is not determined
+    // in other ways. This can help us writing reftests containing scaled images.
+    {
+      name: "useDefaultImageInterpolationQuality",
+      initial: false,
+    },
 
-  // Variant of the ParseHTMLOnMainThread experiment. One experiment immediately
-  // tokenizes input bytes. The default is to tokenize with a post task.
-  {
-    name: "parseHTMLOnMainThreadSyncTokenize",
-    initial: false,
-  },
+    // Variant of the ParseHTMLOnMainThread experiment. One experiment immediately
+    // tokenizes input bytes. The default is to tokenize with a post task.
+    {
+      name: "parseHTMLOnMainThreadSyncTokenize",
+      initial: false,
+    },
 
-  // Variant of the ParseHTMLOnMainThread experiment. This is designed to coalesce
-  // TokenizedChunks when the experiment is running in threaded mode.
-  {
-    name: "parseHTMLOnMainThreadCoalesceChunks",
-    initial: false,
-  },
+    // Variant of the ParseHTMLOnMainThread experiment. This is designed to coalesce
+    // TokenizedChunks when the experiment is running in threaded mode.
+    {
+      name: "parseHTMLOnMainThreadCoalesceChunks",
+      initial: false,
+    },
 
-  // Whether the CSSPreloadScanner is used for externally CSS preloads. NoPreload
-  // indicates that the scanner will be used, but no preloads issued.
-  {
-    name: "cssExternalScannerNoPreload",
-    initial: false,
-  },
-  {
-    name: "cssExternalScannerPreload",
-    initial: false,
-  },
+    // Whether the CSSPreloadScanner is used for externally CSS preloads. NoPreload
+    // indicates that the scanner will be used, but no preloads issued.
+    {
+      name: "cssExternalScannerNoPreload",
+      initial: false,
+    },
+    {
+      name: "cssExternalScannerPreload",
+      initial: false,
+    },
 
-  {
-    name: "browserSideNavigationEnabled",
-    initial: false,
-  },
+    {
+      name: "browserSideNavigationEnabled",
+      initial: false,
+    },
 
-  // Some platforms have media subsystems which are too buggy to allow preloading
-  // of content by default. See http://crbug.com/612909 for details.
-  {
-    name: "forcePreloadNoneForMediaElements",
-    initial: false,
-  },
+    // Some platforms have media subsystems which are too buggy to allow preloading
+    // of content by default. See http://crbug.com/612909 for details.
+    {
+      name: "forcePreloadNoneForMediaElements",
+      initial: false,
+    },
 
-  {
-    name: "hideScrollbars",
-    initial: false,
-  },
+    {
+      name: "hideScrollbars",
+      initial: false,
+    },
 
-  // Spellchecking is enabled by default for elements that do not specify it explicitly
-  // using the "spellcheck" attribute.
-  {
-    name: "spellCheckEnabledByDefault",
-    initial: true,
-  },
+    // Spellchecking is enabled by default for elements that do not specify it explicitly
+    // using the "spellcheck" attribute.
+    {
+      name: "spellCheckEnabledByDefault",
+      initial: true,
+    },
 
-  // Whether download UI should be hidden for the current page content.
-  {
-    name: "hideDownloadUI",
-    initial: false,
-  },
+    // Whether download UI should be hidden for the current page content.
+    {
+      name: "hideDownloadUI",
+      initial: false,
+    },
 
-  // Whether or not to issue range requests for images and show placeholders.
-  {
-    name: "fetchImagePlaceholders",
-    initial: false,
-  },
+    // Whether or not to issue range requests for images and show placeholders.
+    {
+      name: "fetchImagePlaceholders",
+      initial: false,
+    },
 
-  // Whether the frame is a presentation receiver and should expose
-  // `navigator.presentation.receiver`.
-  {
-    name: "presentationReceiver",
-    initial: false,
-  },
-]
+    // Whether the frame is a presentation receiver and should expose
+    // `navigator.presentation.receiver`.
+    {
+      name: "presentationReceiver",
+      initial: false,
+    },
+  ],
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/components/CustomPreviewSection.js b/third_party/WebKit/Source/devtools/front_end/components/CustomPreviewSection.js
index 5a7761f..bf026b3 100644
--- a/third_party/WebKit/Source/devtools/front_end/components/CustomPreviewSection.js
+++ b/third_party/WebKit/Source/devtools/front_end/components/CustomPreviewSection.js
@@ -125,7 +125,7 @@
     this._header.classList.toggle('expanded', this._expanded);
     this._cachedContent.classList.toggle('hidden', !this._expanded);
     if (this._expanded)
-      this._expandIcon.setIconType('smallicon-triangle-bottom');
+      this._expandIcon.setIconType('smallicon-triangle-down');
     else
       this._expandIcon.setIconType('smallicon-triangle-right');
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
index 6b5fd065..d62855f8 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleViewMessage.js
@@ -334,7 +334,7 @@
      * @param {boolean} expand
      */
     function expandStackTrace(expand) {
-      icon.setIconType(expand ? 'smallicon-triangle-bottom' : 'smallicon-triangle-right');
+      icon.setIconType(expand ? 'smallicon-triangle-down' : 'smallicon-triangle-right');
       stackTraceElement.classList.toggle('hidden', !expand);
     }
 
@@ -1195,7 +1195,7 @@
   setCollapsed(collapsed) {
     this._collapsed = collapsed;
     if (this._expandGroupIcon)
-      this._expandGroupIcon.setIconType(this._collapsed ? 'smallicon-triangle-right' : 'smallicon-triangle-bottom');
+      this._expandGroupIcon.setIconType(this._collapsed ? 'smallicon-triangle-right' : 'smallicon-triangle-down');
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js b/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
index 0c97113..51ac5eef 100644
--- a/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
+++ b/third_party/WebKit/Source/devtools/front_end/data_grid/DataGrid.js
@@ -168,7 +168,9 @@
     if (column.sortable) {
       cell.addEventListener('click', this._clickInHeaderCell.bind(this), false);
       cell.classList.add('sortable');
-      cell.createChild('div', 'sort-order-icon-container').createChild('div', 'sort-order-icon');
+      var icon = UI.Icon.create('', 'sort-order-icon');
+      cell.createChild('div', 'sort-order-icon-container').appendChild(icon);
+      cell[DataGrid.DataGrid._sortIconSymbol] = icon;
     }
   }
 
@@ -903,6 +905,9 @@
     this._sortColumnCell = cell;
 
     cell.classList.add(sortOrder);
+    var icon = cell[DataGrid.DataGrid._sortIconSymbol];
+    icon.setIconType(
+        sortOrder === DataGrid.DataGrid.Order.Ascending ? 'smallicon-triangle-up' : 'smallicon-triangle-down');
 
     this.dispatchEventToListeners(DataGrid.DataGrid.Events.SortingChanged);
   }
@@ -1186,6 +1191,7 @@
 
 DataGrid.DataGrid._preferredWidthSymbol = Symbol('preferredWidth');
 DataGrid.DataGrid._columnIdSymbol = Symbol('columnId');
+DataGrid.DataGrid._sortIconSymbol = Symbol('sortIcon');
 
 DataGrid.DataGrid.ColumnResizePadding = 24;
 DataGrid.DataGrid.CenterResizerOverBorderAdjustment = 3;
diff --git a/third_party/WebKit/Source/devtools/front_end/data_grid/dataGrid.css b/third_party/WebKit/Source/devtools/front_end/data_grid/dataGrid.css
index 573b969..1036d23 100644
--- a/third_party/WebKit/Source/devtools/front_end/data_grid/dataGrid.css
+++ b/third_party/WebKit/Source/devtools/front_end/data_grid/dataGrid.css
@@ -159,28 +159,12 @@
 
 .data-grid th .sort-order-icon {
     margin-right: 4px;
-    background-image: url(Images/toolbarButtonGlyphs.png);
-    background-size: 352px 168px;
-    opacity: 0.5;
-    width: 8px;
-    height: 7px;
     display: none;
 }
 
-@media (-webkit-min-device-pixel-ratio: 1.1) {
-.data-grid th .sort-order-icon {
-    background-image: url(Images/toolbarButtonGlyphs_2x.png);
-}
-} /* media */
-
-.data-grid th.sort-ascending .sort-order-icon {
-    display: block;
-    background-position: -4px -111px;
-}
-
+.data-grid th.sort-ascending .sort-order-icon,
 .data-grid th.sort-descending .sort-order-icon {
     display: block;
-    background-position: -20px -99px;
 }
 
 .data-grid th:hover {
diff --git a/third_party/WebKit/Source/devtools/front_end/devices/devicesView.css b/third_party/WebKit/Source/devtools/front_end/devices/devicesView.css
index f06194cf..4a36894c 100644
--- a/third_party/WebKit/Source/devtools/front_end/devices/devicesView.css
+++ b/third_party/WebKit/Source/devtools/front_end/devices/devicesView.css
@@ -76,6 +76,7 @@
     background-color: #f3f3f3;
     flex: none;
     padding: 3px 10px;
+    overflow: hidden;
 }
 
 .devices-footer > span {
@@ -83,7 +84,8 @@
 }
 
 .discovery-view {
-    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
     padding: 15px 15px 0px 0px;
 }
 
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
index 16b841ae..daaccd60e 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/StylesSidebarPane.js
@@ -2127,7 +2127,7 @@
 
   _updateExpandElement() {
     if (this.expanded)
-      this._expandElement.setIconType('smallicon-triangle-bottom');
+      this._expandElement.setIconType('smallicon-triangle-down');
     else
       this._expandElement.setIconType('smallicon-triangle-right');
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
index 37a7b2f2..7d2e6ad13 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
@@ -203,8 +203,9 @@
         'contextmenu', event => this._innerHeaderContextMenu(new UI.ContextMenu(event)));
     var innerElement = this._waterfallHeaderElement.createChild('div');
     innerElement.textContent = Common.UIString('Waterfall');
-    this._waterfallColumnSortIcon = this._waterfallHeaderElement.createChild('div', 'sort-order-icon-container')
-                                        .createChild('div', 'sort-order-icon');
+    this._waterfallColumnSortIcon = UI.Icon.create('', 'sort-order-icon');
+    this._waterfallHeaderElement.createChild('div', 'sort-order-icon-container')
+        .appendChild(this._waterfallColumnSortIcon);
 
     /**
      * @this {Network.NetworkLogViewColumns}
@@ -263,19 +264,18 @@
     var columnId = this._dataGrid.sortColumnId();
     this._networkLogView.removeAllNodeHighlights();
     this._waterfallRequestsAreStale = true;
-    this._waterfallColumnSortIcon.classList.remove('sort-ascending', 'sort-descending');
-
     if (columnId === 'waterfall') {
       if (this._dataGrid.sortOrder() === DataGrid.DataGrid.Order.Ascending)
-        this._waterfallColumnSortIcon.classList.add('sort-ascending');
+        this._waterfallColumnSortIcon.setIconType('smallicon-triangle-up');
       else
-        this._waterfallColumnSortIcon.classList.add('sort-descending');
+        this._waterfallColumnSortIcon.setIconType('smallicon-triangle-down');
 
       var sortFunction = Network.NetworkRequestNode.RequestPropertyComparator.bind(null, this._activeWaterfallSortId);
       this._dataGrid.sortNodes(sortFunction, !this._dataGrid.isSortOrderAscending());
       this._networkLogView.dataGridSorted();
       return;
     }
+    this._waterfallColumnSortIcon.setIconType('');
 
     var columnConfig = this._columns.find(columnConfig => columnConfig.id === columnId);
     if (!columnConfig || !columnConfig.sortingFunction)
diff --git a/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css b/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css
index a434b65..133f21c 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css
+++ b/third_party/WebKit/Source/devtools/front_end/network/networkLogView.css
@@ -376,31 +376,9 @@
     bottom: 1px;
     display: flex;
     align-items: center;
-    padding-left: 0px;
 }
 
 .network-waterfall-header .sort-order-icon {
+    align-items: center;
     margin-right: 4px;
-    background-image: url(Images/toolbarButtonGlyphs.png);
-    background-size: 352px 168px;
-    opacity: 0.5;
-    width: 8px;
-    height: 7px;
-    display: none;
-}
-
-@media (-webkit-min-device-pixel-ratio: 1.1) {
-.network-waterfall-header .sort-order-icon {
-    background-image: url(Images/toolbarButtonGlyphs_2x.png);
-}
-} /* media */
-
-.network-waterfall-header .sort-ascending.sort-order-icon {
-    display: block;
-    background-position: -4px -111px;
-}
-
-.network-waterfall-header .sort-descending.sort-order-icon {
-    display: block;
-    background-position: -20px -99px;
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
index d7892a7..512b6f8 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/DebuggerModel.js
@@ -875,6 +875,7 @@
   PromiseRejection: 'promiseRejection',
   Assert: 'assert',
   DebugCommand: 'debugCommand',
+  OOM: 'OOM',
   Other: 'other'
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/DebuggerPausedMessage.js b/third_party/WebKit/Source/devtools/front_end/sources/DebuggerPausedMessage.js
index b2c3ed1..0fa179c3 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/DebuggerPausedMessage.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/DebuggerPausedMessage.js
@@ -51,6 +51,8 @@
       messageWrapper = buildWrapper(Common.UIString('Paused on assertion'));
     } else if (details.reason === SDK.DebuggerModel.BreakReason.DebugCommand) {
       messageWrapper = buildWrapper(Common.UIString('Paused on debugged function'));
+    } else if (details.reason === SDK.DebuggerModel.BreakReason.OOM) {
+      messageWrapper = buildWrapper(Common.UIString('Paused before potential out-of-memory crash'));
     } else if (details.callFrames.length) {
       var uiLocation = debuggerWorkspaceBinding.rawLocationToUILocation(details.callFrames[0].location());
       var breakpoint = uiLocation ?
@@ -65,7 +67,7 @@
 
     var errorLike = details.reason === SDK.DebuggerModel.BreakReason.Exception ||
         details.reason === SDK.DebuggerModel.BreakReason.PromiseRejection ||
-        details.reason === SDK.DebuggerModel.BreakReason.Assert;
+        details.reason === SDK.DebuggerModel.BreakReason.Assert || details.reason === SDK.DebuggerModel.BreakReason.OOM;
     status.classList.toggle('error-reason', errorLike);
     if (messageWrapper)
       status.appendChild(messageWrapper);
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
index 17940ae..036bdaee 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -805,10 +805,10 @@
     var markers = new Map();
     var recordTypes = TimelineModel.TimelineModel.RecordType;
     var zeroTime = this._model.minimumRecordTime();
-    for (var record of this._model.eventDividerRecords()) {
-      if (record.type() === recordTypes.TimeStamp || record.type() === recordTypes.ConsoleTime)
+    for (var event of this._model.eventDividers()) {
+      if (event.name === recordTypes.TimeStamp || event.name === recordTypes.ConsoleTime)
         continue;
-      markers.set(record.startTime(), Timeline.TimelineUIUtils.createDividerForRecord(record, zeroTime, 0));
+      markers.set(event.startTime, Timeline.TimelineUIUtils.createEventDivider(event, zeroTime));
     }
     this._overviewPane.setMarkers(markers);
   }
@@ -1103,26 +1103,26 @@
     function findLowUtilizationRegion(startIndex, stopIndex) {
       var /** @const */ threshold = 0.1;
       var cutIndex = startIndex;
-      var cutTime = (tasks[cutIndex].startTime() + tasks[cutIndex].endTime()) / 2;
+      var cutTime = (tasks[cutIndex].startTime + tasks[cutIndex].endTime) / 2;
       var usedTime = 0;
       var step = Math.sign(stopIndex - startIndex);
       for (var i = startIndex; i !== stopIndex; i += step) {
         var task = tasks[i];
-        var taskTime = (task.startTime() + task.endTime()) / 2;
+        var taskTime = (task.startTime + task.endTime) / 2;
         var interval = Math.abs(cutTime - taskTime);
         if (usedTime < threshold * interval) {
           cutIndex = i;
           cutTime = taskTime;
           usedTime = 0;
         }
-        usedTime += task.endTime() - task.startTime();
+        usedTime += task.duration;
       }
       return cutIndex;
     }
     var rightIndex = findLowUtilizationRegion(tasks.length - 1, 0);
     var leftIndex = findLowUtilizationRegion(0, rightIndex);
-    var leftTime = tasks[leftIndex].startTime();
-    var rightTime = tasks[rightIndex].endTime();
+    var leftTime = tasks[leftIndex].startTime;
+    var rightTime = tasks[rightIndex].endTime;
     var span = rightTime - leftTime;
     var totalSpan = this._tracingModel.maximumRecordTime() - this._tracingModel.minimumRecordTime();
     if (span < totalSpan * 0.1) {
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
index c305e95b..fa83cb8 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
@@ -951,7 +951,7 @@
    */
   static statsForTimeRange(model, startTime, endTime) {
     Timeline.TimelineUIUtils._buildRangeStatsCacheIfNeeded(model);
-    var tasks = model.mainThreadTasks().map(record => record.traceEvent());
+    var tasks = model.mainThreadTasks();
     if (!tasks.length)
       return {};
     var statsBeforeIndex = Math.min(tasks.lowerBound(startTime, (time, task) => time - task.endTime), tasks.length - 1);
@@ -1027,7 +1027,7 @@
    * @param {!TimelineModel.TimelineModel} model
    */
   static _buildRangeStatsCacheIfNeeded(model) {
-    var tasks = model.mainThreadTasks().map(record => record.traceEvent());
+    var tasks = model.mainThreadTasks();
     if (tasks.length && tasks[0][Timeline.TimelineUIUtils._categoryBreakdownCacheSymbol])
       return;
     var aggregatedStats = {};
@@ -1393,45 +1393,25 @@
   }
 
   /**
-   * @param {!TimelineModel.TimelineModel.RecordType} recordType
-   * @param {?string} title
-   * @param {number} position
-   * @return {!Element}
-   */
-  static createEventDivider(recordType, title, position) {
-    var eventDivider = createElement('div');
-    eventDivider.className = 'resources-event-divider';
-    var recordTypes = TimelineModel.TimelineModel.RecordType;
-
-    if (recordType === recordTypes.MarkDOMContent)
-      eventDivider.className += ' resources-blue-divider';
-    else if (recordType === recordTypes.MarkLoad)
-      eventDivider.className += ' resources-red-divider';
-    else if (recordType === recordTypes.MarkFirstPaint)
-      eventDivider.className += ' resources-green-divider';
-    else if (
-        recordType === recordTypes.TimeStamp || recordType === recordTypes.ConsoleTime ||
-        recordType === recordTypes.UserTiming)
-      eventDivider.className += ' resources-orange-divider';
-    else if (recordType === recordTypes.BeginFrame)
-      eventDivider.className += ' timeline-frame-divider';
-
-    if (title)
-      eventDivider.title = title;
-    eventDivider.style.left = position + 'px';
-    return eventDivider;
-  }
-
-  /**
-   * @param {!TimelineModel.TimelineModel.Record} record
+   * @param {!SDK.TracingModel.Event} event
    * @param {number} zeroTime
-   * @param {number} position
    * @return {!Element}
    */
-  static createDividerForRecord(record, zeroTime, position) {
-    var startTime = Number.millisToString(record.startTime() - zeroTime);
-    var title = Common.UIString('%s at %s', Timeline.TimelineUIUtils.eventTitle(record.traceEvent()), startTime);
-    return Timeline.TimelineUIUtils.createEventDivider(record.type(), title, position);
+  static createEventDivider(event, zeroTime) {
+    var eventDivider = createElementWithClass('div', 'resources-event-divider');
+    var startTime = Number.millisToString(event.startTime - zeroTime);
+    eventDivider.title = Common.UIString('%s at %s', Timeline.TimelineUIUtils.eventTitle(event), startTime);
+
+    var recordTypes = TimelineModel.TimelineModel.RecordType;
+    var name = event.name;
+    if (name === recordTypes.MarkDOMContent)
+      eventDivider.classList.add('resources-blue-divider');
+    else if (name === recordTypes.MarkLoad)
+      eventDivider.classList.add('resources-red-divider');
+    else if (name === recordTypes.MarkFirstPaint)
+      eventDivider.classList.add('resources-green-divider');
+
+    return eventDivider;
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
index 2c3bc72..26792154 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineFrameModel.js
@@ -237,24 +237,6 @@
   }
 
   /**
-   * @param {!Array.<string>} types
-   * @param {!TimelineModel.TimelineModel.Record} record
-   * @return {?TimelineModel.TimelineModel.Record} record
-   */
-  _findRecordRecursively(types, record) {
-    if (types.indexOf(record.type()) >= 0)
-      return record;
-    if (!record.children())
-      return null;
-    for (var i = 0; i < record.children().length; ++i) {
-      var result = this._findRecordRecursively(types, record.children()[i]);
-      if (result)
-        return result;
-    }
-    return null;
-  }
-
-  /**
    * @param {?SDK.Target} target
    * @param {!Array.<!SDK.TracingModel.Event>} events
    * @param {string} sessionId
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
index 981f65c..4e4a7e7 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
@@ -217,7 +217,6 @@
     this._inspectedTargetEvents.sort(SDK.TracingModel.Event.compareStartTime);
 
     this._processBrowserEvents(tracingModel);
-    this._buildTimelineRecords();
     this._buildGPUEvents(tracingModel);
     this._insertFirstPaintEvent();
     this._resetProcessingState();
@@ -326,10 +325,8 @@
     this._mainThreadEvents.splice(
         this._mainThreadEvents.lowerBound(firstPaintEvent, SDK.TracingModel.Event.compareStartTime), 0,
         firstPaintEvent);
-    var firstPaintRecord = new TimelineModel.TimelineModel.Record(firstPaintEvent);
-    this._eventDividerRecords.splice(
-        this._eventDividerRecords.lowerBound(firstPaintRecord, TimelineModel.TimelineModel.Record._compareStartTime), 0,
-        firstPaintRecord);
+    this._eventDividers.splice(
+        this._eventDividers.lowerBound(firstPaintEvent, SDK.TracingModel.Event.compareStartTime), 0, firstPaintEvent);
   }
 
   /**
@@ -348,27 +345,6 @@
     this._mergeAsyncEvents(this._mainThreadAsyncEventsByGroup, asyncEventsByGroup);
   }
 
-  _buildTimelineRecords() {
-    var topLevelRecords = this._buildTimelineRecordsForThread(this.mainThreadEvents());
-    for (var i = 0; i < topLevelRecords.length; i++) {
-      var record = topLevelRecords[i];
-      var event = record.traceEvent();
-      if (SDK.TracingModel.isTopLevelEvent(event) && event.duration)
-        this._mainThreadTasks.push(record);
-    }
-
-    /**
-     * @param {!TimelineModel.TimelineModel.VirtualThread} virtualThread
-     * @this {!TimelineModel.TimelineModel}
-     */
-    function processVirtualThreadEvents(virtualThread) {
-      var threadRecords = this._buildTimelineRecordsForThread(virtualThread.events);
-      topLevelRecords =
-          topLevelRecords.mergeOrdered(threadRecords, TimelineModel.TimelineModel.Record._compareStartTime);
-    }
-    this.virtualThreads().forEach(processVirtualThreadEvents.bind(this));
-  }
-
   /**
    * @param {!SDK.TracingModel} tracingModel
    */
@@ -380,41 +356,6 @@
     this._gpuEvents = thread.events().filter(event => event.name === gpuEventName);
   }
 
-  /**
-   * @param {!Array.<!SDK.TracingModel.Event>} threadEvents
-   * @return {!Array.<!TimelineModel.TimelineModel.Record>}
-   */
-  _buildTimelineRecordsForThread(threadEvents) {
-    var recordStack = [];
-    var topLevelRecords = [];
-
-    for (var i = 0, size = threadEvents.length; i < size; ++i) {
-      var event = threadEvents[i];
-      for (var top = recordStack.peekLast(); top && top._event.endTime <= event.startTime; top = recordStack.peekLast())
-        recordStack.pop();
-      if (event.phase === SDK.TracingModel.Phase.AsyncEnd || event.phase === SDK.TracingModel.Phase.NestableAsyncEnd)
-        continue;
-      var parentRecord = recordStack.peekLast();
-      // Maintain the back-end logic of old timeline, skip console.time() / console.timeEnd() that are not properly nested.
-      if (SDK.TracingModel.isAsyncBeginPhase(event.phase) && parentRecord &&
-          event.endTime > parentRecord._event.endTime)
-        continue;
-      var record = new TimelineModel.TimelineModel.Record(event);
-      if (TimelineModel.TimelineModel.isMarkerEvent(event))
-        this._eventDividerRecords.push(record);
-      if (!this._eventFilter.accept(event) && !SDK.TracingModel.isTopLevelEvent(event))
-        continue;
-      if (parentRecord)
-        parentRecord._addChild(record);
-      else
-        topLevelRecords.push(record);
-      if (event.endTime)
-        recordStack.push(record);
-    }
-
-    return topLevelRecords;
-  }
-
   _resetProcessingState() {
     this._asyncEventTracker = new TimelineModel.TimelineAsyncEventTracker();
     this._invalidationTracker = new TimelineModel.InvalidationTracker();
@@ -534,14 +475,34 @@
     }
 
     this._eventStack = [];
+    var eventStack = this._eventStack;
     var i = events.lowerBound(startTime, (time, event) => time - event.startTime);
     var length = events.length;
     for (; i < length; i++) {
       var event = events[i];
       if (endTime && event.startTime >= endTime)
         break;
+      while (eventStack.length && eventStack.peekLast().endTime <= event.startTime)
+        eventStack.pop();
       if (!this._processEvent(event))
         continue;
+      if (!SDK.TracingModel.isAsyncPhase(event.phase) && event.duration) {
+        if (eventStack.length) {
+          var parent = eventStack.peekLast();
+          parent.selfTime -= event.duration;
+          if (parent.selfTime < 0)
+            this._fixNegativeDuration(parent, event);
+        }
+        event.selfTime = event.duration;
+        if (isMainThread) {
+          if (!eventStack.length)
+            this._mainThreadTasks.push(event);
+        }
+        eventStack.push(event);
+      }
+      if (isMainThread && TimelineModel.TimelineModel.isMarkerEvent(event))
+        this._eventDividers.push(event);
+
       if (groupByFrame) {
         var frameId = TimelineModel.TimelineData.forEvent(event).frameId;
         var pageFrame = frameId && this._pageFrames.get(frameId);
@@ -567,6 +528,20 @@
   }
 
   /**
+   * @param {!SDK.TracingModel.Event} event
+   * @param {!SDK.TracingModel.Event} child
+   */
+  _fixNegativeDuration(event, child) {
+    var epsilon = 1e-3;
+    if (event.selfTime < -epsilon) {
+      console.error(
+          `Children are longer than parent at ${event.startTime} ` +
+          `(${(child.startTime - this.minimumRecordTime()).toFixed(3)} by ${(-event.selfTime).toFixed(3)}`);
+    }
+    event.selfTime = 0;
+  }
+
+  /**
    * @param {!Map<!TimelineModel.TimelineModel.AsyncEventGroup, !Array<!SDK.TracingModel.AsyncEvent>>} asyncEventsByGroup
    * @param {!Array<!SDK.TracingModel.AsyncEvent>} asyncEvents
    * @param {number=} startTime
@@ -597,11 +572,8 @@
    * @return {boolean}
    */
   _processEvent(event) {
-    var eventStack = this._eventStack;
-    while (eventStack.length && eventStack.peekLast().endTime <= event.startTime)
-      eventStack.pop();
-
     var recordTypes = TimelineModel.TimelineModel.RecordType;
+    var eventStack = this._eventStack;
 
     if (!eventStack.length) {
       if (this._currentTaskLayoutAndRecalcEvents && this._currentTaskLayoutAndRecalcEvents.length) {
@@ -822,26 +794,6 @@
           timelineData.warning = TimelineModel.TimelineModel.WarningType.IdleDeadlineExceeded;
         break;
     }
-    if (SDK.TracingModel.isAsyncPhase(event.phase))
-      return true;
-    var duration = event.duration;
-    if (!duration)
-      return true;
-    if (eventStack.length) {
-      var parent = eventStack.peekLast();
-      parent.selfTime -= duration;
-      if (parent.selfTime < 0) {
-        var epsilon = 1e-3;
-        if (parent.selfTime < -epsilon) {
-          console.error(
-              'Children are longer than parent at ' + event.startTime + ' (' +
-              (event.startTime - this.minimumRecordTime()).toFixed(3) + ') by ' + parent.selfTime.toFixed(3));
-        }
-        parent.selfTime = 0;
-      }
-    }
-    event.selfTime = duration;
-    eventStack.push(event);
     return true;
   }
 
@@ -939,12 +891,12 @@
     this._mainThreadAsyncEventsByGroup = new Map();
     /** @type {!Array<!SDK.TracingModel.Event>} */
     this._inspectedTargetEvents = [];
-    /** @type {!Array<!TimelineModel.TimelineModel.Record>} */
+    /** @type {!Array<!SDK.TracingModel.Event>} */
     this._mainThreadTasks = [];
     /** @type {!Array<!SDK.TracingModel.Event>} */
     this._gpuEvents = [];
-    /** @type {!Array<!TimelineModel.TimelineModel.Record>} */
-    this._eventDividerRecords = [];
+    /** @type {!Array<!SDK.TracingModel.Event>} */
+    this._eventDividers = [];
     /** @type {?string} */
     this._sessionId = null;
     /** @type {?number} */
@@ -991,13 +943,6 @@
   }
 
   /**
-   * @param {!Array<!SDK.TracingModel.Event>} events
-   */
-  _setMainThreadEvents(events) {
-    this._mainThreadEvents = events;
-  }
-
-  /**
    * @return {!Map<!TimelineModel.TimelineModel.AsyncEventGroup, !Array.<!SDK.TracingModel.AsyncEvent>>}
    */
   mainThreadAsyncEvents() {
@@ -1019,7 +964,7 @@
   }
 
   /**
-   * @return {!Array.<!TimelineModel.TimelineModel.Record>}
+   * @return {!Array<!SDK.TracingModel.Event>}
    */
   mainThreadTasks() {
     return this._mainThreadTasks;
@@ -1033,10 +978,10 @@
   }
 
   /**
-   * @return {!Array.<!TimelineModel.TimelineModel.Record>}
+   * @return {!Array<!SDK.TracingModel.Event>}
    */
-  eventDividerRecords() {
-    return this._eventDividerRecords;
+  eventDividers() {
+    return this._eventDividers;
   }
 
   /**
@@ -1299,92 +1244,6 @@
   }
 };
 
-/**
- * @unrestricted
- */
-TimelineModel.TimelineModel.Record = class {
-  /**
-   * @param {!SDK.TracingModel.Event} traceEvent
-   */
-  constructor(traceEvent) {
-    this._event = traceEvent;
-    this._children = [];
-  }
-
-  /**
-   * @param {!TimelineModel.TimelineModel.Record} a
-   * @param {!TimelineModel.TimelineModel.Record} b
-   * @return {number}
-   */
-  static _compareStartTime(a, b) {
-    // Never return 0 as otherwise equal records would be merged.
-    return a.startTime() <= b.startTime() ? -1 : 1;
-  }
-
-  /**
-   * @return {?SDK.Target}
-   */
-  target() {
-    var threadName = this._event.thread.name();
-    // FIXME: correctly specify target
-    return threadName === TimelineModel.TimelineModel.RendererMainThreadName ? SDK.targetManager.targets()[0] || null :
-                                                                               null;
-  }
-
-  /**
-   * @return {!Array.<!TimelineModel.TimelineModel.Record>}
-   */
-  children() {
-    return this._children;
-  }
-
-  /**
-   * @return {number}
-   */
-  startTime() {
-    return this._event.startTime;
-  }
-
-  /**
-   * @return {number}
-   */
-  endTime() {
-    return this._event.endTime || this._event.startTime;
-  }
-
-  /**
-   * @return {string}
-   */
-  thread() {
-    if (this._event.thread.name() === TimelineModel.TimelineModel.RendererMainThreadName)
-      return TimelineModel.TimelineModel.MainThreadName;
-    return this._event.thread.name();
-  }
-
-  /**
-   * @return {!TimelineModel.TimelineModel.RecordType}
-   */
-  type() {
-    return TimelineModel.TimelineModel._eventType(this._event);
-  }
-
-  /**
-   * @return {!SDK.TracingModel.Event}
-   */
-  traceEvent() {
-    return this._event;
-  }
-
-  /**
-   * @param {!TimelineModel.TimelineModel.Record} child
-   */
-  _addChild(child) {
-    this._children.push(child);
-    child.parent = this;
-  }
-};
-
-
 /** @typedef {!{page: !Array<!SDK.TracingModel.Event>, workers: !Array<!SDK.TracingModel.Event>}} */
 TimelineModel.TimelineModel.MetadataEvents;
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
index 112ef801..49d041b 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
@@ -108,7 +108,8 @@
   'smallicon-bezier': {x: -80, y: -20, width: 10, height: 10, spritesheet: 'smallicons', isMask: true},
   'smallicon-dropdown-arrow': {x: -18, y: -96, width: 12, height: 12, spritesheet: 'largeicons', isMask: true},
   'smallicon-triangle-right': {x: -4, y: -98, width: 10, height: 8, spritesheet: 'largeicons', isMask: true},
-  'smallicon-triangle-bottom': {x: -20, y: -98, width: 10, height: 8, spritesheet: 'largeicons', isMask: true},
+  'smallicon-triangle-down': {x: -20, y: -98, width: 10, height: 8, spritesheet: 'largeicons', isMask: true},
+  'smallicon-triangle-up': {x: -4, y: -111, width: 10, height: 8, spritesheet: 'largeicons', isMask: true},
   'smallicon-arrow-in-circle': {x: -10, y: -127, width: 11, height: 11, spritesheet: 'largeicons', isMask: true},
   'smallicon-cross': {x: -177, y: -98, width: 10, height: 10, spritesheet: 'largeicons'},
   'smallicon-inline-breakpoint': {x: -140, y: -20, width: 10, height: 10, spritesheet: 'smallicons'},
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 58c7457e..8888d50 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -60505,6 +60505,35 @@
   </summary>
 </histogram>
 
+<histogram name="ServiceWorker.NavigationPreload.ConcurrentTime" units="ms">
+  <owner>falken@chromium.org</owner>
+  <summary>
+    The duration of time when both (1) a service worker is being found and
+    possibly started up, (2) the navigation preload request is in-flight. The
+    measurement ends once either the worker is prepared, or the navigation
+    preload response is received (OnReceiveResponse() is called). This is a
+    rough estimate of the performance win of using navigation preload, ignoring
+    concurrency overhead. This histogram is recorded when a navigation preload
+    response is succesfully forwarded to the service worker's fetch event, with
+    the same restrictions as for
+    ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time. The sample is
+    additionally recorded to the appropriate suffixed histograms.
+  </summary>
+</histogram>
+
+<histogram name="ServiceWorker.NavigationPreload.FinishedBeforeStartWorker"
+    enum="BooleanNavPreloadFinishedFirst">
+  <owner>falken@chromium.org</owner>
+  <summary>
+    Whether the navigation preload response arrived before the activated and
+    running service worker was prepared. This histogram is recorded when a
+    navigation preload response is succesfully forwarded to the service worker's
+    fetch event, with the same restrictions as for
+    ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time. The sample is
+    additionally recorded to the appropriate suffixed histograms.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorker.NavigationPreload.HeaderSize" units="bytes">
   <owner>horo@chromium.org</owner>
   <summary>
@@ -60514,6 +60543,51 @@
   </summary>
 </histogram>
 
+<histogram name="ServiceWorker.NavigationPreload.NavPreloadAfterSWStart"
+    units="ms">
+  <owner>falken@chromium.org</owner>
+  <summary>
+    This is recorded in the case where the activated and running service worker
+    was prepared before the navigation preload response arrived. It is the
+    remaining time it took to receive the response after the worker was
+    prepared.
+
+    This histogram is recorded when a navigation preload response is succesfully
+    forwarded to the service worker's fetch event (for the case mentioned
+    above), with the same restrictions as for
+    ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time. The sample is
+    additionally recorded to the appropriate suffixed histograms.
+  </summary>
+</histogram>
+
+<histogram name="ServiceWorker.NavigationPreload.ResponseTime" units="ms">
+  <owner>falken@chromium.org</owner>
+  <summary>
+    The time taken for the navigation preload response to start, i.e., when
+    OnReceiveResponse() is called. This histogram is recorded when a navigation
+    preload response is successfully forwarded to the service worker's fetch
+    event, with the same restrictions as for
+    ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time. The sample is
+    additionally recorded to the appropriate suffixed histograms.
+  </summary>
+</histogram>
+
+<histogram name="ServiceWorker.NavigationPreload.SWStartAfterNavPreload"
+    units="ms">
+  <owner>falken@chromium.org</owner>
+  <summary>
+    This is recorded in the case where the navigation preload response arrived
+    before the activated and running service worker was prepared. It is the
+    remaining time it took to prepare the worker after the response arrived.
+
+    This histogram is recorded when a navigation preload response is succesfully
+    forwarded to the service worker's fetch event (for the case mentioned
+    above), with the same restrictions as for
+    ServiceWorker.ActivatedWorkerPreparationForMainFrame.Time. The sample is
+    additionally recorded to the appropriate suffixed histograms.
+  </summary>
+</histogram>
+
 <histogram name="ServiceWorker.NotificationClickEvent.Time" units="ms">
   <owner>peter@chromium.org</owner>
   <summary>
@@ -79742,6 +79816,11 @@
   <int value="1" label="Missing data in disk cache"/>
 </enum>
 
+<enum name="BooleanNavPreloadFinishedFirst" type="int">
+  <int value="0" label="Worker preparation finished first"/>
+  <int value="1" label="Navigation preload response arrived first"/>
+</enum>
+
 <enum name="BooleanNeedsClearing" type="int">
   <int value="0" label="Doesn't need clearing"/>
   <int value="1" label="Needs to be clearred"/>
@@ -113638,6 +113717,16 @@
       name="ServiceWorker.ActivatedWorkerPreparationForMainFrame.Type"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="NavigationPreloadOrWorkerFirst">
+  <suffix name="SWStartFirst"
+      label="The service worker finished preparing before the navigation
+             preload response arrived."/>
+  <suffix name="NavPreloadFirst"
+      label="The navigation preload response arrived before the service
+             worker finished preparing."/>
+  <affected-histogram name="ServiceWorker.NavigationPreload.ConcurrentTime"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="Net.BidirectionalStreamExperiment" separator=".">
   <owner>xunjieli@chromium.org</owner>
   <suffix name="QUIC" label="Bidirectional streams that use QUIC protocol"/>
@@ -117946,6 +118035,22 @@
   <affected-histogram name="StartupTimeBomb.Alarm"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="StartWorkerExistingProcess">
+  <suffix name="StartWorkerExistingProcess"
+      label="The worker started up in an existing process"/>
+  <affected-histogram name="ServiceWorker.NavigationPreload.ConcurrentTime"/>
+  <affected-histogram
+      name="ServiceWorker.NavigationPreload.ConcurrentTime_NavPreloadFirst"/>
+  <affected-histogram
+      name="ServiceWorker.NavigationPreload.ConcurrentTime_SWStartFirst"/>
+  <affected-histogram
+      name="ServiceWorker.NavigationPreload.FinishedBeforeStartWorker"/>
+  <affected-histogram
+      name="ServiceWorker.NavigationPreload.NavPreloadAfterSWStart"/>
+  <affected-histogram
+      name="ServiceWorker.NavigationPreload.SWStartAfterNavPreload"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="Storage.BlobAppendableItems" separator=".">
   <suffix name="Bytes" label="Appending bytes."/>
   <suffix name="File" label="Appending a file with a known size."/>
diff --git a/ui/app_list/views/app_list_folder_view.cc b/ui/app_list/views/app_list_folder_view.cc
index 40a75ed..1b69049 100644
--- a/ui/app_list/views/app_list_folder_view.cc
+++ b/ui/app_list/views/app_list_folder_view.cc
@@ -63,7 +63,7 @@
   AddChildView(items_grid_view_);
   view_model_->Add(items_grid_view_, kIndexChildItems);
 
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 
   model_->AddObserver(this);
diff --git a/ui/app_list/views/app_list_main_view.cc b/ui/app_list/views/app_list_main_view.cc
index 15954500..baafd3f 100644
--- a/ui/app_list/views/app_list_main_view.cc
+++ b/ui/app_list/views/app_list_main_view.cc
@@ -127,7 +127,7 @@
 
   search_box_view_->set_contents_view(contents_view_);
 
-  contents_view_->SetPaintToLayer(true);
+  contents_view_->SetPaintToLayer();
   contents_view_->layer()->SetFillsBoundsOpaquely(false);
   contents_view_->layer()->SetMasksToBounds(true);
 
diff --git a/ui/app_list/views/app_list_main_view_unittest.cc b/ui/app_list/views/app_list_main_view_unittest.cc
index dcb7103..e36a14e 100644
--- a/ui/app_list/views/app_list_main_view_unittest.cc
+++ b/ui/app_list/views/app_list_main_view_unittest.cc
@@ -86,7 +86,7 @@
     // NULL on Windows, and not needed for tests. It is only used to determine
     // the scale factor for preloading icons.
     main_view_ = new AppListMainView(delegate_.get());
-    main_view_->SetPaintToLayer(true);
+    main_view_->SetPaintToLayer();
     main_view_->model()->SetFoldersEnabled(true);
     search_box_view_ = new SearchBoxView(main_view_, delegate_.get());
     main_view_->Init(nullptr, 0, search_box_view_);
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc
index 0a251ef..22cbb98 100644
--- a/ui/app_list/views/app_list_view.cc
+++ b/ui/app_list/views/app_list_view.cc
@@ -86,7 +86,7 @@
  public:
   explicit AppListOverlayView(int corner_radius)
       : corner_radius_(corner_radius) {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     SetVisible(false);
     layer()->SetOpacity(0.0f);
   }
@@ -368,14 +368,14 @@
 
   app_list_main_view_ = new AppListMainView(delegate_);
   AddChildView(app_list_main_view_);
-  app_list_main_view_->SetPaintToLayer(true);
+  app_list_main_view_->SetPaintToLayer();
   app_list_main_view_->layer()->SetFillsBoundsOpaquely(false);
   app_list_main_view_->layer()->SetMasksToBounds(true);
 
   // This will be added to the |search_box_widget_| after the app list widget is
   // initialized.
   search_box_view_ = new SearchBoxView(app_list_main_view_, delegate_);
-  search_box_view_->SetPaintToLayer(true);
+  search_box_view_->SetPaintToLayer();
   search_box_view_->layer()->SetFillsBoundsOpaquely(false);
   search_box_view_->layer()->SetMasksToBounds(true);
 
@@ -397,7 +397,7 @@
   if (delegate_ && delegate_->IsSpeechRecognitionEnabled()) {
     speech_view_ = new SpeechView(delegate_);
     speech_view_->SetVisible(false);
-    speech_view_->SetPaintToLayer(true);
+    speech_view_->SetPaintToLayer();
     speech_view_->layer()->SetFillsBoundsOpaquely(false);
     speech_view_->layer()->SetOpacity(0.0f);
     AddChildView(speech_view_);
diff --git a/ui/app_list/views/apps_grid_view.cc b/ui/app_list/views/apps_grid_view.cc
index 1de02d67..1deeb85a 100644
--- a/ui/app_list/views/apps_grid_view.cc
+++ b/ui/app_list/views/apps_grid_view.cc
@@ -222,7 +222,7 @@
       bounds_animator_(this),
       activated_folder_item_view_(NULL),
       dragging_for_reparent_item_(false) {
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   // Clip any icons that are outside the grid view's bounds. These icons would
   // otherwise be visible to the user when the grid view is off screen.
   layer()->SetMasksToBounds(true);
@@ -558,7 +558,7 @@
   AppListItemView* view = new AppListItemView(this, original_drag_view->item());
   AddChildView(view);
   drag_view_ = view;
-  drag_view_->SetPaintToLayer(true);
+  drag_view_->SetPaintToLayer();
   drag_view_->layer()->SetFillsBoundsOpaquely(false);
   drag_view_->SetBoundsRect(drag_view_rect);
   drag_view_->SetDragUIState();  // Hide the title of the drag_view_.
@@ -825,7 +825,7 @@
   DCHECK_LE(index, item_list_->item_count());
   AppListItemView* view = new AppListItemView(this,
                                               item_list_->item_at(index));
-  view->SetPaintToLayer(true);
+  view->SetPaintToLayer();
   view->layer()->SetFillsBoundsOpaquely(false);
   return view;
 }
diff --git a/ui/app_list/views/folder_background_view.cc b/ui/app_list/views/folder_background_view.cc
index c4c1cc8..2d58893 100644
--- a/ui/app_list/views/folder_background_view.cc
+++ b/ui/app_list/views/folder_background_view.cc
@@ -25,7 +25,7 @@
 FolderBackgroundView::FolderBackgroundView()
     : folder_view_(NULL),
       show_state_(NO_BUBBLE) {
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 }
 
diff --git a/ui/app_list/views/pulsing_block_view.cc b/ui/app_list/views/pulsing_block_view.cc
index 3eef488..eb40fb1 100644
--- a/ui/app_list/views/pulsing_block_view.cc
+++ b/ui/app_list/views/pulsing_block_view.cc
@@ -76,7 +76,7 @@
 namespace app_list {
 
 PulsingBlockView::PulsingBlockView(const gfx::Size& size, bool start_delay) {
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 
   const int max_delay = kAnimationDurationInMs * arraysize(kAnimationOpacity);
diff --git a/ui/app_list/views/top_icon_animation_view.cc b/ui/app_list/views/top_icon_animation_view.cc
index 8e024e3..82119ee 100644
--- a/ui/app_list/views/top_icon_animation_view.cc
+++ b/ui/app_list/views/top_icon_animation_view.cc
@@ -26,7 +26,7 @@
   icon_->SetImage(resized);
   AddChildView(icon_);
 
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 }
 
diff --git a/ui/arc/notification/arc_custom_notification_view.cc b/ui/arc/notification/arc_custom_notification_view.cc
index 027fd2a0..cdb2c7d 100644
--- a/ui/arc/notification/arc_custom_notification_view.cc
+++ b/ui/arc/notification/arc_custom_notification_view.cc
@@ -238,7 +238,7 @@
     OnNotificationSurfaceAdded(surface);
 
   // Create a layer as an anchor to insert surface copy during a slide.
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   UpdatePreferredSize();
 }
 
diff --git a/ui/message_center/views/message_center_button_bar.cc b/ui/message_center/views/message_center_button_bar.cc
index 5f89a6a..7fbdcfe 100644
--- a/ui/message_center/views/message_center_button_bar.cc
+++ b/ui/message_center/views/message_center_button_bar.cc
@@ -104,7 +104,7 @@
       close_all_button_(NULL),
       settings_button_(NULL),
       quiet_mode_button_(NULL) {
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   set_background(
       views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
 
diff --git a/ui/message_center/views/message_center_view.cc b/ui/message_center/views/message_center_view.cc
index a16898d..90adbdc 100644
--- a/ui/message_center/views/message_center_view.cc
+++ b/ui/message_center/views/message_center_view.cc
@@ -99,7 +99,7 @@
   scroller_->set_background(
       views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
 
-  scroller_->SetPaintToLayer(true);
+  scroller_->SetPaintToLayer();
   scroller_->layer()->SetFillsBoundsOpaquely(false);
   scroller_->layer()->SetMasksToBounds(true);
 
diff --git a/ui/message_center/views/notifier_settings_view.cc b/ui/message_center/views/notifier_settings_view.cc
index f5794c9d..d4a7b48 100644
--- a/ui/message_center/views/notifier_settings_view.cc
+++ b/ui/message_center/views/notifier_settings_view.cc
@@ -426,7 +426,7 @@
   SetFocusBehavior(FocusBehavior::ALWAYS);
   set_background(
       views::Background::CreateSolidBackground(kMessageCenterBackgroundColor));
-  SetPaintToLayer(true);
+  SetPaintToLayer();
 
   title_label_ = new views::Label(
       l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL),
diff --git a/ui/views/animation/ink_drop.cc b/ui/views/animation/ink_drop.cc
index a82f4c51..6a1df98 100644
--- a/ui/views/animation/ink_drop.cc
+++ b/ui/views/animation/ink_drop.cc
@@ -9,7 +9,7 @@
 InkDropContainerView::InkDropContainerView() {}
 
 void InkDropContainerView::AddInkDropLayer(ui::Layer* ink_drop_layer) {
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   SetVisible(true);
   layer()->SetFillsBoundsOpaquely(false);
   layer()->Add(ink_drop_layer);
@@ -18,7 +18,7 @@
 void InkDropContainerView::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
   layer()->Remove(ink_drop_layer);
   SetVisible(false);
-  SetPaintToLayer(false);
+  DestroyLayer();
 }
 
 bool InkDropContainerView::CanProcessEventsWithinSubtree() const {
diff --git a/ui/views/animation/ink_drop_host_view.cc b/ui/views/animation/ink_drop_host_view.cc
index f5a7df2..22dd4f1 100644
--- a/ui/views/animation/ink_drop_host_view.cc
+++ b/ui/views/animation/ink_drop_host_view.cc
@@ -130,7 +130,7 @@
 
 void InkDropHostView::AddInkDropLayer(ui::Layer* ink_drop_layer) {
   old_paint_to_layer_ = layer() != nullptr;
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   ink_drop_mask_ = CreateInkDropMask();
   if (ink_drop_mask_)
@@ -148,7 +148,8 @@
   layer()->Remove(ink_drop_layer);
   // Layers safely handle destroying a mask layer before the masked layer.
   ink_drop_mask_.reset();
-  SetPaintToLayer(old_paint_to_layer_);
+  if (!old_paint_to_layer_)
+    DestroyLayer();
 }
 
 std::unique_ptr<InkDrop> InkDropHostView::CreateInkDrop() {
diff --git a/ui/views/bubble/tray_bubble_view.cc b/ui/views/bubble/tray_bubble_view.cc
index 82723fd..e446414 100644
--- a/ui/views/bubble/tray_bubble_view.cc
+++ b/ui/views/bubble/tray_bubble_view.cc
@@ -209,7 +209,7 @@
   set_notify_enter_exit_on_child(true);
   set_close_on_deactivate(init_params.close_on_deactivate);
   set_margins(gfx::Insets());
-  SetPaintToLayer(true);
+  SetPaintToLayer();
 
   bubble_content_mask_.reset(
       new TrayBubbleContentMask(bubble_border_->GetBorderCornerRadius()));
@@ -378,7 +378,7 @@
 void TrayBubbleView::ViewHierarchyChanged(
     const ViewHierarchyChangedDetails& details) {
   if (details.is_add && details.child == this) {
-    details.parent->SetPaintToLayer(true);
+    details.parent->SetPaintToLayer();
     details.parent->layer()->SetMasksToBounds(true);
   }
 }
diff --git a/ui/views/controls/button/label_button.cc b/ui/views/controls/button/label_button.cc
index 21891fc..c4bca07 100644
--- a/ui/views/controls/button/label_button.cc
+++ b/ui/views/controls/button/label_button.cc
@@ -99,7 +99,7 @@
   SetTextInternal(text);
 
   AddChildView(ink_drop_container_);
-  ink_drop_container_->SetPaintToLayer(true);
+  ink_drop_container_->SetPaintToLayer();
   ink_drop_container_->layer()->SetFillsBoundsOpaquely(false);
   ink_drop_container_->SetVisible(false);
 
@@ -423,13 +423,13 @@
 }
 
 void LabelButton::AddInkDropLayer(ui::Layer* ink_drop_layer) {
-  image()->SetPaintToLayer(true);
+  image()->SetPaintToLayer();
   image()->layer()->SetFillsBoundsOpaquely(false);
   ink_drop_container_->AddInkDropLayer(ink_drop_layer);
 }
 
 void LabelButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
-  image()->SetPaintToLayer(false);
+  image()->DestroyLayer();
   ink_drop_container_->RemoveInkDropLayer(ink_drop_layer);
 }
 
diff --git a/ui/views/controls/button/md_text_button.cc b/ui/views/controls/button/md_text_button.cc
index af31410..842b6be 100644
--- a/ui/views/controls/button/md_text_button.cc
+++ b/ui/views/controls/button/md_text_button.cc
@@ -205,7 +205,7 @@
 
   // Paint to a layer so that the canvas is snapped to pixel boundaries (useful
   // for fractional DSF).
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 }
 
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index 42b79d9..7dff005 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -442,7 +442,7 @@
   // A layer is applied to make sure that canvas bounds are snapped to pixel
   // boundaries (for the sake of drawing the arrow).
   if (UseMd()) {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
   } else {
     arrow_image_ = PlatformStyle::CreateComboboxArrow(enabled(), style);
diff --git a/ui/views/controls/focus_ring.cc b/ui/views/controls/focus_ring.cc
index c8985bf..3abf1706 100644
--- a/ui/views/controls/focus_ring.cc
+++ b/ui/views/controls/focus_ring.cc
@@ -87,7 +87,7 @@
 FocusRing::FocusRing()
     : override_color_id_(ui::NativeTheme::kColorId_NumColors) {
   // A layer is necessary to paint beyond the parent's bounds.
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 }
 
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
index 35f7f1b..9440e34 100644
--- a/ui/views/controls/scroll_view.cc
+++ b/ui/views/controls/scroll_view.cc
@@ -207,7 +207,7 @@
       a_view->set_background(
           Background::CreateSolidBackground(background_color_));
     }
-    a_view->SetPaintToLayer(true);
+    a_view->SetPaintToLayer();
     a_view->layer()->SetScrollable(
         contents_viewport_->layer(),
         base::Bind(&ScrollView::OnLayerScrolled, base::Unretained(this)));
@@ -716,7 +716,7 @@
   background_color_ = SK_ColorWHITE;
   contents_viewport_->set_background(
       Background::CreateSolidBackground(background_color_));
-  contents_viewport_->SetPaintToLayer(true);
+  contents_viewport_->SetPaintToLayer();
   contents_viewport_->layer()->SetMasksToBounds(true);
 }
 
diff --git a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
index 9d54b67..e3803b6 100644
--- a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
+++ b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
@@ -95,7 +95,7 @@
 
   // This is necessary, otherwise the thumb will be rendered below the views if
   // those views paint to their own layers.
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 }
 
@@ -177,7 +177,7 @@
 
   thickness_animation_.SetSlideDuration(kExpandDurationMs);
 
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   has_scrolltrack_ = scroller_style_ == NSScrollerStyleLegacy;
   layer()->SetOpacity(scroller_style_ == NSScrollerStyleOverlay ? 0.0f : 1.0f);
 }
diff --git a/ui/views/controls/scrollbar/overlay_scroll_bar.cc b/ui/views/controls/scrollbar/overlay_scroll_bar.cc
index 8a93b23..55b03620 100644
--- a/ui/views/controls/scrollbar/overlay_scroll_bar.cc
+++ b/ui/views/controls/scrollbar/overlay_scroll_bar.cc
@@ -34,7 +34,7 @@
 OverlayScrollBar::Thumb::~Thumb() {}
 
 void OverlayScrollBar::Thumb::Init() {
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   // Animate all changes to the layer except the first one.
   OnStateChanged();
@@ -113,7 +113,7 @@
   SetThumb(thumb);
   thumb->Init();
   set_notify_enter_exit_on_child(true);
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetMasksToBounds(true);
   layer()->SetFillsBoundsOpaquely(false);
 }
diff --git a/ui/views/controls/slide_out_view.cc b/ui/views/controls/slide_out_view.cc
index 2775c088..b222cd7 100644
--- a/ui/views/controls/slide_out_view.cc
+++ b/ui/views/controls/slide_out_view.cc
@@ -13,7 +13,7 @@
 SlideOutView::SlideOutView() {
   // If accelerated compositing is not available, this widget tracks the
   // OnSlideOut event but does not render any visible changes.
-  SetPaintToLayer(true);
+  SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
 }
 
diff --git a/ui/views/mus/BUILD.gn b/ui/views/mus/BUILD.gn
index c568ec3..4720a32 100644
--- a/ui/views/mus/BUILD.gn
+++ b/ui/views/mus/BUILD.gn
@@ -120,6 +120,7 @@
 
   deps = [
     ":mus",
+    ":views_mus_tests_catalog_source",
     "//base",
     "//base/test:test_support",
     "//mojo/edk/system",
@@ -138,7 +139,6 @@
   ]
 
   data_deps = [
-    ":views_mus_tests_catalog",
     "//ui/resources:ui_test_pak_data",
   ]
 }
@@ -191,7 +191,6 @@
   ]
 
   data_deps = [
-    ":views_mus_tests_catalog_copy",
     "//services/ui/ime/test_ime_driver",
     "//services/ui/test_wm",
   ]
@@ -252,7 +251,6 @@
   ]
 
   data_deps = [
-    ":views_mus_tests_catalog_copy",
     "//services/ui/test_wm",
   ]
 
@@ -293,13 +291,8 @@
   catalog_deps = [ "//mash:catalog" ]
 }
 
-copy("views_mus_tests_catalog_copy") {
+catalog_cpp_source("views_mus_tests_catalog_source") {
   testonly = true
-  sources = get_target_outputs(":views_mus_tests_catalog")
-  outputs = [
-    "${root_out_dir}/views_mus_tests_catalog.json",
-  ]
-  deps = [
-    ":views_mus_tests_catalog",
-  ]
+  catalog = ":views_mus_tests_catalog"
+  output_symbol_name = "kViewsMusTestCatalog"
 }
diff --git a/ui/views/mus/views_mus_test_suite.cc b/ui/views/mus/views_mus_test_suite.cc
index 57776440..50c0c92 100644
--- a/ui/views/mus/views_mus_test_suite.cc
+++ b/ui/views/mus/views_mus_test_suite.cc
@@ -9,6 +9,7 @@
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
+#include "base/json/json_reader.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "base/synchronization/waitable_event.h"
@@ -35,12 +36,12 @@
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 
+// Generated within the ":views_mus_tests_catalog_source" target.
+extern const char kViewsMusTestCatalog[];
+
 namespace views {
 namespace {
 
-const base::FilePath::CharType kCatalogFilename[] =
-    FILE_PATH_LITERAL("views_mus_tests_catalog.json");
-
 void EnsureCommandLineSwitch(const std::string& name) {
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   if (!cmd_line->HasSwitch(name))
@@ -107,8 +108,8 @@
   ServiceManagerConnection()
       : thread_("Persistent service_manager connections"),
         ipc_thread_("IPC thread") {
-    catalog::Catalog::LoadDefaultCatalogManifest(
-        base::FilePath(kCatalogFilename));
+    catalog::Catalog::SetDefaultCatalogManifest(
+        base::JSONReader::Read(kViewsMusTestCatalog));
     mojo::edk::Init();
     ipc_thread_.StartWithOptions(
         base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
diff --git a/ui/views/view.cc b/ui/views/view.cc
index d65f4bc..83d2700c 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -523,22 +523,49 @@
     }
   } else {
     if (!layer())
-      CreateLayer();
+      CreateLayer(ui::LAYER_TEXTURED);
     layer()->SetTransform(transform);
     layer()->ScheduleDraw();
   }
 }
 
-void View::SetPaintToLayer(bool paint_to_layer) {
-  if (paint_to_layer_ == paint_to_layer)
+void View::SetPaintToLayer(ui::LayerType layer_type) {
+  if (paint_to_layer_ && (layer()->type() == layer_type))
     return;
 
-  paint_to_layer_ = paint_to_layer;
-  if (paint_to_layer_ && !layer()) {
-    CreateLayer();
-  } else if (!paint_to_layer_ && layer()) {
-    DestroyLayer();
+  DestroyLayer();
+  CreateLayer(layer_type);
+  paint_to_layer_ = true;
+}
+
+void View::DestroyLayer() {
+  if (!paint_to_layer_)
+    return;
+
+  paint_to_layer_ = false;
+  if (!layer())
+    return;
+
+  ui::Layer* new_parent = layer()->parent();
+  std::vector<ui::Layer*> children = layer()->children();
+  for (size_t i = 0; i < children.size(); ++i) {
+    layer()->Remove(children[i]);
+    if (new_parent)
+      new_parent->Add(children[i]);
   }
+
+  LayerOwner::DestroyLayer();
+
+  if (new_parent)
+    ReorderLayers();
+
+  UpdateChildLayerBounds(CalculateOffsetToAncestorWithLayer(NULL));
+
+  SchedulePaint();
+
+  Widget* widget = GetWidget();
+  if (widget)
+    widget->UpdateRootLayers();
 }
 
 std::unique_ptr<ui::Layer> View::RecreateLayer() {
@@ -2184,7 +2211,7 @@
 
 // Accelerated painting --------------------------------------------------------
 
-void View::CreateLayer() {
+void View::CreateLayer(ui::LayerType layer_type) {
   // A new layer is being created for the view. So all the layers of the
   // sub-tree can inherit the visibility of the corresponding view.
   {
@@ -2193,7 +2220,7 @@
       child->UpdateChildLayerVisibility(true);
   }
 
-  SetLayer(base::MakeUnique<ui::Layer>());
+  SetLayer(base::MakeUnique<ui::Layer>(layer_type));
   layer()->set_delegate(this);
   layer()->set_name(GetClassName());
 
@@ -2260,29 +2287,6 @@
   MoveLayerToParent(layer(), gfx::Point());
 }
 
-void View::DestroyLayer() {
-  ui::Layer* new_parent = layer()->parent();
-  std::vector<ui::Layer*> children = layer()->children();
-  for (size_t i = 0; i < children.size(); ++i) {
-    layer()->Remove(children[i]);
-    if (new_parent)
-      new_parent->Add(children[i]);
-  }
-
-  LayerOwner::DestroyLayer();
-
-  if (new_parent)
-    ReorderLayers();
-
-  UpdateChildLayerBounds(CalculateOffsetToAncestorWithLayer(NULL));
-
-  SchedulePaint();
-
-  Widget* widget = GetWidget();
-  if (widget)
-    widget->UpdateRootLayers();
-}
-
 // Input -----------------------------------------------------------------------
 
 bool View::ProcessMousePressed(const ui::MouseEvent& event) {
diff --git a/ui/views/view.h b/ui/views/view.h
index e0b2325..a916376 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -334,10 +334,15 @@
   // Sets whether this view paints to a layer. A view paints to a layer if
   // either of the following are true:
   // . the view has a non-identity transform.
-  // . SetPaintToLayer(true) has been invoked.
+  // . SetPaintToLayer(ui::LayerType) has been invoked.
   // View creates the Layer only when it exists in a Widget with a non-NULL
   // Compositor.
-  void SetPaintToLayer(bool paint_to_layer);
+  void SetPaintToLayer(ui::LayerType layer_type = ui::LAYER_TEXTURED);
+
+  // Destroys the layer associated with this view, and reparents any descendants
+  // to the destroyed layer's parent. If the view does not currently have a
+  // layer, this has no effect.
+  void DestroyLayer();
 
   // Overridden from ui::LayerOwner:
   std::unique_ptr<ui::Layer> RecreateLayer() override;
@@ -1370,7 +1375,7 @@
   // Accelerated painting ------------------------------------------------------
 
   // Creates the layer and related fields for this view.
-  void CreateLayer();
+  void CreateLayer(ui::LayerType layer_type);
 
   // Recursively calls UpdateParentLayers() on all descendants, stopping at any
   // Views that have layers. Calls UpdateParentLayer() for any Views that have
@@ -1393,10 +1398,6 @@
   // this subtree.
   void OrphanLayers();
 
-  // Destroys the layer associated with this view, and reparents any descendants
-  // to the destroyed layer's parent.
-  void DestroyLayer();
-
   // Input ---------------------------------------------------------------------
 
   bool ProcessMousePressed(const ui::MouseEvent& event);
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index bb713373..1df4e8b 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -157,7 +157,7 @@
     views::View* v = new views::View;
     view->AddChildView(v);
     if (base::RandDouble() > 0.5)
-      v->SetPaintToLayer(true);
+      v->SetPaintToLayer();
     if (base::RandDouble() < 0.2)
       v->SetVisible(false);
 
@@ -184,7 +184,7 @@
   }
 
   if (!view->layer() && base::RandDouble() < 0.1)
-    view->SetPaintToLayer(true);
+    view->SetPaintToLayer();
 
   if (base::RandDouble() < 0.1)
     view->SetVisible(!view->visible());
@@ -783,15 +783,15 @@
   v1->AddChildView(v2);
 
   // Verify where the layers actually appear.
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   // x: 25 - 10(x) - 12(width) = 3
   EXPECT_EQ(gfx::Rect(3, 11, 12, 13), v1->layer()->bounds());
-  v1->SetPaintToLayer(false);
+  v1->DestroyLayer();
 
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   // x: 25 - 10(parent x) - 3(x) - 6(width) = 6
   EXPECT_EQ(gfx::Rect(6, 15, 6, 5), v2->layer()->bounds());
-  v2->SetPaintToLayer(false);
+  v2->DestroyLayer();
 
   // Paint everything once, since it has to build its cache. Then we can test
   // invalidation.
@@ -859,15 +859,15 @@
   v1->AddChildView(v2);
 
   // Verify where the layers actually appear.
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   // x: 25 - 10(x) - 12(width) = 3
   EXPECT_EQ(gfx::Rect(3, 11, 12, 13), v1->layer()->bounds());
-  v1->SetPaintToLayer(false);
+  v1->DestroyLayer();
 
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   // x: 25 - 10(parent x) - 3(x) - 6(width) = 6
   EXPECT_EQ(gfx::Rect(6, 15, 6, 5), v2->layer()->bounds());
-  v2->SetPaintToLayer(false);
+  v2->DestroyLayer();
 
   // Paint everything once, since it has to build its cache. Then we can test
   // invalidation.
@@ -935,15 +935,15 @@
   v1->AddChildView(v2);
 
   // Verify where the layers actually appear.
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   // x: 25 - 10(x) - 12(width) = 3
   EXPECT_EQ(gfx::Rect(3, 11, 12, 13), v1->layer()->bounds());
-  v1->SetPaintToLayer(false);
+  v1->DestroyLayer();
 
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   // x: 25 - 10(parent x) - 3(x) - 6(width) = 6
   EXPECT_EQ(gfx::Rect(6, 15, 6, 5), v2->layer()->bounds());
-  v2->SetPaintToLayer(false);
+  v2->DestroyLayer();
 
   // Paint everything once, since it has to build its cache. Then we can test
   // invalidation.
@@ -1011,15 +1011,15 @@
   v1->AddChildView(v2);
 
   // Verify where the layers actually appear.
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   // x: 25 - 10(x) - 12(width) = 3
   EXPECT_EQ(gfx::Rect(3, 11, 12, 13), v1->layer()->bounds());
-  v1->SetPaintToLayer(false);
+  v1->DestroyLayer();
 
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   // x: 25 - 10(parent x) - 3(x) - 6(width) = 6
   EXPECT_EQ(gfx::Rect(6, 15, 6, 5), v2->layer()->bounds());
-  v2->SetPaintToLayer(false);
+  v2->DestroyLayer();
 
   // Paint everything once, since it has to build its cache. Then we can test
   // invalidation.
@@ -1099,15 +1099,15 @@
   root_view->AddChildView(v2);
 
   // Verify where the layers actually appear.
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   // x: 25 - 10(x) - 12(width) = 3
   EXPECT_EQ(gfx::Rect(3, 11, 12, 13), v1->layer()->bounds());
-  v1->SetPaintToLayer(false);
+  v1->DestroyLayer();
 
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   // x: 25 - 3(x) - 6(width) = 16
   EXPECT_EQ(gfx::Rect(16, 4, 6, 5), v2->layer()->bounds());
-  v2->SetPaintToLayer(false);
+  v2->DestroyLayer();
 
   // Paint everything once, since it has to build its cache. Then we can test
   // invalidation.
@@ -1146,7 +1146,7 @@
   View* root_view = widget->GetRootView();
 
   TestView* v1 = new TestView;
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   v1->SetBounds(10, 11, 12, 13);
   root_view->AddChildView(v1);
 
@@ -1230,7 +1230,7 @@
   root_view->SetBounds(0, 0, 200, 200);
 
   TestPaintView* v1 = new TestPaintView;
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
 
   // Set bounds for |v1| such that it has an offset to its parent and only part
   // of it is visible. The visible bounds does not intersect with |root_view|'s
@@ -3733,7 +3733,7 @@
 
   // Create v1, give it a bounds and verify everything is set up correctly.
   View* v1 = new View;
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   EXPECT_TRUE(v1->layer() != NULL);
   v1->SetBoundsRect(gfx::Rect(20, 30, 140, 150));
   content_view->AddChildView(v1);
@@ -3746,14 +3746,14 @@
   v1->AddChildView(v2);
   EXPECT_TRUE(v2->layer() == NULL);
   v2->SetBoundsRect(gfx::Rect(10, 20, 30, 40));
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   ASSERT_TRUE(v2->layer() != NULL);
   EXPECT_EQ(v1->layer(), v2->layer()->parent());
   EXPECT_EQ(gfx::Rect(10, 20, 30, 40), v2->layer()->bounds());
 
   // Turn off v1s layer. v2 should still have a layer but its parent should have
   // changed.
-  v1->SetPaintToLayer(false);
+  v1->DestroyLayer();
   EXPECT_TRUE(v1->layer() == NULL);
   EXPECT_TRUE(v2->layer() != NULL);
   EXPECT_EQ(root_layer, v2->layer()->parent());
@@ -3792,13 +3792,13 @@
   v1->AddChildView(v2);
 
   View* v3 = new View;
-  v3->SetPaintToLayer(true);
+  v3->SetPaintToLayer();
   v2->AddChildView(v3);
   ASSERT_TRUE(v3->layer() != NULL);
 
   // At this point we have v1-v2-v3. v3 has a layer, v1 and v2 don't.
 
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   EXPECT_EQ(v1->layer(), v3->layer()->parent());
 }
 
@@ -3808,7 +3808,7 @@
 
   View* v1 = new View;
   content_view->AddChildView(v1);
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   EXPECT_TRUE(v1->layer() != NULL);
 
   TestLayerAnimator* animator = new TestLayerAnimator();
@@ -3834,7 +3834,7 @@
   View* v2 = new View;
   v2->SetBoundsRect(gfx::Rect(10, 11, 40, 50));
   v1->AddChildView(v2);
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   ASSERT_TRUE(v2->layer() != NULL);
   EXPECT_EQ(gfx::Rect(30, 41, 40, 50), v2->layer()->bounds());
 
@@ -3866,7 +3866,7 @@
   // |v1| is initially not attached to anything. So its layer will have the same
   // bounds as the view.
   View* v1 = new View;
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   v1->SetBounds(10, 10, 20, 10);
   EXPECT_EQ(gfx::Rect(10, 10, 20, 10),
             v1->layer()->bounds());
@@ -3884,12 +3884,12 @@
   v2->SetBounds(50, 10, 30, 10);
   EXPECT_FALSE(v2->layer());
   view->AddChildView(v2);
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   EXPECT_EQ(gfx::Rect(content_width - 80, 10, 30, 10),
             v2->layer()->bounds());
   gfx::Rect l2bounds = v2->layer()->bounds();
 
-  view->SetPaintToLayer(true);
+  view->SetPaintToLayer();
   EXPECT_EQ(l1bounds, v1->layer()->bounds());
   EXPECT_EQ(l2bounds, v2->layer()->bounds());
 
@@ -3899,7 +3899,7 @@
   l1bounds.set_x(l1bounds.x() + 5);
   EXPECT_EQ(l1bounds, v1->layer()->bounds());
 
-  view->SetPaintToLayer(false);
+  view->DestroyLayer();
   EXPECT_EQ(l1bounds, v1->layer()->bounds());
   EXPECT_EQ(l2bounds, v2->layer()->bounds());
 
@@ -3920,7 +3920,7 @@
 
   // Create a paints-to-layer view |v1|.
   View* v1 = new View;
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   v1->SetBounds(10, 10, 20, 10);
   view->AddChildView(v1);
   EXPECT_EQ(gfx::Rect(content_width - 30, 10, 20, 10),
@@ -3928,7 +3928,7 @@
 
   // Attach a paints-to-layer child view to |v1|.
   View* v2 = new View;
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   v2->SetBounds(3, 5, 6, 4);
   EXPECT_EQ(gfx::Rect(3, 5, 6, 4),
             v2->layer()->bounds());
@@ -3941,7 +3941,7 @@
   View* v3 = new View;
   v3->SetBounds(1, 1, 18, 8);
   View* v4 = new View;
-  v4->SetPaintToLayer(true);
+  v4->SetPaintToLayer();
   v4->SetBounds(2, 4, 6, 4);
   EXPECT_EQ(gfx::Rect(2, 4, 6, 4),
             v4->layer()->bounds());
@@ -4013,7 +4013,7 @@
   // still have a layer, but the layer should not be attached to the root
   // layer.
   View* v1 = new View;
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   EXPECT_TRUE(v1->layer());
   EXPECT_FALSE(LayerIsAncestor(widget()->GetCompositor()->root_layer(),
                                v1->layer()));
@@ -4049,7 +4049,7 @@
 
   View* v2 = new View;
   v1->AddChildView(v2);
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   EXPECT_TRUE(LayerIsAncestor(widget()->GetCompositor()->root_layer(),
                               v2->layer()));
   EXPECT_TRUE(v2->layer()->IsDrawn());
@@ -4092,7 +4092,7 @@
     return;
   PaintTrackingView* content_view = new PaintTrackingView;
   widget()->SetContentsView(content_view);
-  content_view->SetPaintToLayer(true);
+  content_view->SetPaintToLayer();
   GetRootLayer()->GetCompositor()->ScheduleDraw();
   ui::DrawWaiterForTest::WaitForCompositingEnded(
       GetRootLayer()->GetCompositor());
@@ -4142,7 +4142,7 @@
   parent_view.AddChildView(child_view);
 
   parent_view.scheduled_paint_rects_.clear();
-  child_view->SetPaintToLayer(true);
+  child_view->SetPaintToLayer();
   EXPECT_EQ(1U, parent_view.scheduled_paint_rects_.size());
 }
 
@@ -4151,7 +4151,7 @@
   parent_view.SetBounds(10, 11, 12, 13);
 
   TestView* child_view = new TestView;
-  child_view->SetPaintToLayer(true);
+  child_view->SetPaintToLayer();
   parent_view.AddChildView(child_view);
 
   parent_view.scheduled_paint_rects_.clear();
@@ -4162,7 +4162,7 @@
 // visibility changes.
 TEST_F(ViewLayerTest, VisibilityChildLayers) {
   View* v1 = new View;
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   widget()->SetContentsView(v1);
 
   View* v2 = new View;
@@ -4173,7 +4173,7 @@
   v3->SetVisible(false);
 
   View* v4 = new View;
-  v4->SetPaintToLayer(true);
+  v4->SetPaintToLayer();
   v3->AddChildView(v4);
 
   EXPECT_TRUE(v1->layer()->IsDrawn());
@@ -4209,7 +4209,7 @@
 // marking this as FLAKY.
 TEST_F(ViewLayerTest, DISABLED_ViewLayerTreesInSync) {
   View* content = new View;
-  content->SetPaintToLayer(true);
+  content->SetPaintToLayer();
   widget()->SetContentsView(content);
   widget()->Show();
 
@@ -4232,10 +4232,10 @@
   View* content = new View;
   widget()->SetContentsView(content);
   View* c1 = new View;
-  c1->SetPaintToLayer(true);
+  c1->SetPaintToLayer();
   content->AddChildView(c1);
   View* c2 = new View;
-  c2->SetPaintToLayer(true);
+  c2->SetPaintToLayer();
   content->AddChildView(c2);
 
   ui::Layer* parent_layer = c1->layer()->parent();
@@ -4255,7 +4255,7 @@
   View* content = new View;
   widget()->SetContentsView(content);
   std::unique_ptr<View> c1(new View);
-  c1->SetPaintToLayer(true);
+  c1->SetPaintToLayer();
   EXPECT_TRUE(c1->layer());
   content->AddChildView(c1.get());
 
@@ -4272,13 +4272,13 @@
 // Verify the z-order of the layers as a result of calling RecreateLayer().
 TEST_F(ViewLayerTest, RecreateLayerZOrder) {
   std::unique_ptr<View> v(new View());
-  v->SetPaintToLayer(true);
+  v->SetPaintToLayer();
 
   View* v1 = new View();
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   v->AddChildView(v1);
   View* v2 = new View();
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   v->AddChildView(v2);
 
   // Test the initial z-order.
@@ -4305,10 +4305,10 @@
   widget()->SetContentsView(v);
 
   View* v1 = new View();
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   v->AddChildView(v1);
   View* v2 = new View();
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   v->AddChildView(v2);
 
   ui::Layer* root_layer = GetRootLayer();
@@ -4333,9 +4333,9 @@
 // a View.
 TEST_F(ViewLayerTest, RecreateLayerMovesNonViewChildren) {
   View v;
-  v.SetPaintToLayer(true);
+  v.SetPaintToLayer();
   View child;
-  child.SetPaintToLayer(true);
+  child.SetPaintToLayer();
   v.AddChildView(&child);
   ASSERT_TRUE(v.layer() != NULL);
   ASSERT_EQ(1u, v.layer()->children().size());
@@ -4379,12 +4379,12 @@
 
   v11->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
   v1->SetBoundsRect(gfx::Rect(1, 1, 10, 10));
-  v11->SetPaintToLayer(true);
+  v11->SetPaintToLayer();
 
   EXPECT_EQ("0.40 0.40", ToString(v11->layer()->subpixel_position_offset()));
 
   // Creating a layer in parent should update the child view's layer offset.
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   EXPECT_EQ("-0.20 -0.20", ToString(v1->layer()->subpixel_position_offset()));
   EXPECT_EQ("-0.20 -0.20", ToString(v11->layer()->subpixel_position_offset()));
 
@@ -4394,7 +4394,7 @@
   EXPECT_EQ("0.33 0.33", ToString(v11->layer()->subpixel_position_offset()));
 
   // Deleting parent's layer should update the child view's layer's offset.
-  v1->SetPaintToLayer(false);
+  v1->DestroyLayer();
   EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset()));
 
   // Setting parent view should update the child view's layer's offset.
@@ -4545,7 +4545,7 @@
 // See comment above test for details.
 class ViewThatAddsViewInOnNativeThemeChanged : public View {
  public:
-  ViewThatAddsViewInOnNativeThemeChanged() { SetPaintToLayer(true); }
+  ViewThatAddsViewInOnNativeThemeChanged() { SetPaintToLayer(); }
   ~ViewThatAddsViewInOnNativeThemeChanged() override {}
 
   bool on_native_theme_changed_called() const {
@@ -4592,7 +4592,7 @@
 // Creates and adds a new child view to |parent| that has a layer.
 void AddViewWithChildLayer(View* parent) {
   View* child = new View;
-  child->SetPaintToLayer(true);
+  child->SetPaintToLayer();
   parent->AddChildView(child);
 }
 
@@ -4623,7 +4623,7 @@
 class NoLayerWhenHiddenView : public View {
  public:
   NoLayerWhenHiddenView() {
-    SetPaintToLayer(true);
+    SetPaintToLayer();
     set_owned_by_client();
     SetBounds(0, 0, 100, 100);
   }
@@ -4634,7 +4634,7 @@
   void VisibilityChanged(View* starting_from, bool is_visible) override {
     if (!is_visible) {
       was_hidden_ = true;
-      SetPaintToLayer(false);
+      DestroyLayer();
     }
   }
 
@@ -4701,7 +4701,7 @@
 TEST_F(ViewTest, ChildViewZOrderChanged) {
   const int kChildrenCount = 4;
   std::unique_ptr<View> view(new OrderableView());
-  view->SetPaintToLayer(true);
+  view->SetPaintToLayer();
   for (int i = 0; i < kChildrenCount; ++i)
     AddViewWithChildLayer(view.get());
   View::Views children = view->GetChildrenInZOrder();
diff --git a/ui/views/view_unittest_aura.cc b/ui/views/view_unittest_aura.cc
index 66a9fd0f..351c662a 100644
--- a/ui/views/view_unittest_aura.cc
+++ b/ui/views/view_unittest_aura.cc
@@ -36,7 +36,7 @@
 View* CreateViewWithLayer(const gfx::Rect& bounds, const char* layer_name) {
   View* view = new View();
   view->SetBoundsRect(bounds);
-  view->SetPaintToLayer(true);
+  view->SetPaintToLayer();
   view->layer()->set_name(layer_name);
   return view;
 }
diff --git a/ui/views/widget/native_widget_aura_unittest.cc b/ui/views/widget/native_widget_aura_unittest.cc
index cac43b51..4d06907e 100644
--- a/ui/views/widget/native_widget_aura_unittest.cc
+++ b/ui/views/widget/native_widget_aura_unittest.cc
@@ -485,7 +485,7 @@
   views::View* view_with_layer = new views::View;
   parent_root->AddChildView(view_with_layer);
   view_with_layer->SetBounds(0, 0, 50, 50);
-  view_with_layer->SetPaintToLayer(true);
+  view_with_layer->SetPaintToLayer();
 
   // Make sure that |child| still gets the event.
   EXPECT_EQ(child->GetNativeWindow(),
diff --git a/ui/views/widget/window_reorderer_unittest.cc b/ui/views/widget/window_reorderer_unittest.cc
index da7b542b..579e622 100644
--- a/ui/views/widget/window_reorderer_unittest.cc
+++ b/ui/views/widget/window_reorderer_unittest.cc
@@ -66,7 +66,7 @@
   // view are stacked below the layers for any windows not associated to a host
   // view.
   View* v = new View();
-  v->SetPaintToLayer(true);
+  v->SetPaintToLayer();
   v->layer()->set_name("v");
   contents_view->AddChildView(v);
 
@@ -215,7 +215,7 @@
   contents_view->AddChildView(v1);
 
   View* v11 = new View();
-  v11->SetPaintToLayer(true);
+  v11->SetPaintToLayer();
   v11->layer()->set_name("v11");
   v1->AddChildView(v11);
 
@@ -229,12 +229,12 @@
   w->GetNativeView()->SetProperty(kHostViewKey, v12);
 
   View* v13 = new View();
-  v13->SetPaintToLayer(true);
+  v13->SetPaintToLayer();
   v13->layer()->set_name("v13");
   v1->AddChildView(v13);
 
   View* v2 = new View();
-  v2->SetPaintToLayer(true);
+  v2->SetPaintToLayer();
   v2->layer()->set_name("v2");
   contents_view->AddChildView(v2);
 
@@ -244,7 +244,7 @@
             ui::test::ChildLayerNamesAsString(*parent_window->layer()));
 
   // |w|'s layer should be stacked above |v1|'s layer.
-  v1->SetPaintToLayer(true);
+  v1->SetPaintToLayer();
   v1->layer()->set_name("v1");
   EXPECT_EQ("w", ChildWindowNamesAsString(*parent_window));
   EXPECT_EQ("v1 w v2",