diff --git a/DEPS b/DEPS
index 5284173..cb4626a 100644
--- a/DEPS
+++ b/DEPS
@@ -41,11 +41,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '66b914c7b222035257a01875071cf5b8b68bd51b',
+  'skia_revision': '311cc6b39a3e5f792dc9081ea19e42b930426b99',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '399c4c84bf472db59a46f7aae96e361094791d43',
+  'v8_revision': '2bd685dadeb1eac3466a94e607883274c8b7bddc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index b63e33a..98d77cc 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -4,9 +4,11 @@
 
 #include "ash/shelf/shelf_tooltip_manager.h"
 
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_view.h"
+#include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/wm/window_util.h"
@@ -217,6 +219,10 @@
   if (event->type() != ui::ET_MOUSE_MOVED)
     return;
 
+  // A workaround for crbug.com/756163, likely not needed as Mus/Mash matures.
+  if (Shell::GetAshConfig() != Config::CLASSIC && event->location().IsOrigin())
+    return;
+
   gfx::Point point = event->location();
   views::View::ConvertPointFromWidget(shelf_view_, &point);
   views::View* view = shelf_view_->GetTooltipHandlerForPoint(point);
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 8e5a74e..b7009355 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -9,7 +9,6 @@
 #include "ash/public/cpp/config.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/root_window_controller.h"
-#include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shell.h"
@@ -68,15 +67,6 @@
 // Color of the separator.
 const SkColor kPaletteSeparatorColor = SkColorSetARGB(0x1E, 0x00, 0x00, 0x00);
 
-// Returns true if we are in a user session that can show the stylus tools.
-bool IsInUserSession() {
-  SessionController* session_controller = Shell::Get()->session_controller();
-  return !session_controller->IsUserSessionBlocked() &&
-         session_controller->GetSessionState() ==
-             session_manager::SessionState::ACTIVE &&
-         !session_controller->IsKioskSession();
-}
-
 // Returns true if the |palette_tray| is on an internal display or on every
 // display if requested from the command line.
 bool ShouldShowOnDisplay(PaletteTray* palette_tray) {
@@ -288,8 +278,10 @@
 
   // Don't do anything if the palette should not be shown or if the user has
   // disabled it all-together.
-  if (!IsInUserSession() || !palette_delegate->ShouldShowPalette())
+  if (!palette_utils::IsInUserSession() ||
+      !palette_delegate->ShouldShowPalette()) {
     return;
+  }
 
   // Auto show/hide the palette if allowed by the user.
   if (palette_delegate->ShouldAutoOpenPalette()) {
@@ -549,7 +541,7 @@
 void PaletteTray::UpdateIconVisibility() {
   SetVisible(has_seen_stylus_ && is_palette_enabled_ &&
              palette_utils::HasStylusInput() && ShouldShowOnDisplay(this) &&
-             IsInUserSession());
+             palette_utils::IsInUserSession());
 }
 
 // TestApi. For testing purposes.
diff --git a/ash/system/palette/palette_tray_unittest.cc b/ash/system/palette/palette_tray_unittest.cc
index 54bddd18..e8561729 100644
--- a/ash/system/palette/palette_tray_unittest.cc
+++ b/ash/system/palette/palette_tray_unittest.cc
@@ -392,6 +392,34 @@
   EXPECT_TRUE(highlighter_test_api.IsShowingHighlighter());
   generator.ReleaseTouch();
 
+  // The barrel button should not work on the lock screen.
+  highlighter_test_api.DestroyPointerView();
+  GetSessionControllerClient()->RequestLockScreen();
+  EXPECT_FALSE(test_api_->GetPaletteToolManager()->IsToolActive(
+      PaletteToolId::METALAYER));
+
+  generator.MoveTouch(gfx::Point(1, 1));
+  generator.set_flags(ui::EF_LEFT_MOUSE_BUTTON);
+  generator.PressTouch();
+  generator.set_flags(ui::EF_NONE);
+  generator.MoveTouch(gfx::Point(2, 2));
+  EXPECT_FALSE(test_api_->GetPaletteToolManager()->IsToolActive(
+      PaletteToolId::METALAYER));
+  EXPECT_FALSE(highlighter_test_api.IsShowingHighlighter());
+  generator.ReleaseTouch();
+
+  // Unlock the screen, the barrel button should work again.
+  GetSessionControllerClient()->UnlockScreen();
+  generator.MoveTouch(gfx::Point(1, 1));
+  generator.set_flags(ui::EF_LEFT_MOUSE_BUTTON);
+  generator.PressTouch();
+  generator.set_flags(ui::EF_NONE);
+  generator.MoveTouch(gfx::Point(2, 2));
+  EXPECT_TRUE(test_api_->GetPaletteToolManager()->IsToolActive(
+      PaletteToolId::METALAYER));
+  EXPECT_TRUE(highlighter_test_api.IsShowingHighlighter());
+  generator.ReleaseTouch();
+
   // Disable the metalayer support.
   // This should deactivate both the palette tool and the highlighter.
   Shell::Get()->NotifyVoiceInteractionContextEnabled(false);
diff --git a/ash/system/palette/palette_utils.cc b/ash/system/palette/palette_utils.cc
index dff37640..e46b127b 100644
--- a/ash/system/palette/palette_utils.cc
+++ b/ash/system/palette/palette_utils.cc
@@ -6,6 +6,7 @@
 
 #include "ash/ash_switches.h"
 #include "ash/palette_delegate.h"
+#include "ash/session/session_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/system/palette/palette_tray.h"
@@ -84,5 +85,13 @@
   g_has_stylus_input_for_testing = true;
 }
 
+bool IsInUserSession() {
+  SessionController* session_controller = Shell::Get()->session_controller();
+  return !session_controller->IsUserSessionBlocked() &&
+         session_controller->GetSessionState() ==
+             session_manager::SessionState::ACTIVE &&
+         !session_controller->IsKioskSession();
+}
+
 }  // namespace palette_utils
 }  // namespace ash
diff --git a/ash/system/palette/palette_utils.h b/ash/system/palette/palette_utils.h
index ef28bbc..45d9b34c1 100644
--- a/ash/system/palette/palette_utils.h
+++ b/ash/system/palette/palette_utils.h
@@ -40,6 +40,9 @@
 // Forcibly say the device is has stylus input for testing purposes.
 ASH_EXPORT void SetHasStylusInputForTesting();
 
+// Returns true if we are in a user session that can show the stylus tools.
+ASH_EXPORT bool IsInUserSession();
+
 }  // namespace palette_utils
 }  // namespace ash
 
diff --git a/ash/system/palette/tools/metalayer_mode.cc b/ash/system/palette/tools/metalayer_mode.cc
index a17acd5..c4e252e8 100644
--- a/ash/system/palette/tools/metalayer_mode.cc
+++ b/ash/system/palette/tools/metalayer_mode.cc
@@ -81,6 +81,9 @@
   if (!feature_enabled())
     return;
 
+  if (!palette_utils::IsInUserSession())
+    return;
+
   // The metalayer tool is already selected, no need to do anything.
   if (enabled())
     return;
diff --git a/ash/system/power/power_button_display_controller.cc b/ash/system/power/power_button_display_controller.cc
index b7ab992..38d319d 100644
--- a/ash/system/power/power_button_display_controller.cc
+++ b/ash/system/power/power_button_display_controller.cc
@@ -10,6 +10,7 @@
 #include "ash/touch/touch_devices_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/logging.h"
+#include "base/time/tick_clock.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "ui/events/devices/input_device_manager.h"
 #include "ui/events/devices/stylus_state.h"
@@ -36,8 +37,9 @@
 
 }  // namespace
 
-PowerButtonDisplayController::PowerButtonDisplayController()
-    : weak_ptr_factory_(this) {
+PowerButtonDisplayController::PowerButtonDisplayController(
+    base::TickClock* tick_clock)
+    : tick_clock_(tick_clock), weak_ptr_factory_(this) {
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
       this);
   // TODO(mash): Provide a way for this class to observe stylus events:
@@ -90,6 +92,9 @@
   else
     screen_state_ = user_initiated ? ScreenState::OFF : ScreenState::OFF_AUTO;
 
+  if (screen_state_ != old_state)
+    screen_state_last_changed_ = tick_clock_->NowTicks();
+
   // Disable the touchscreen when the screen is turned off due to inactivity:
   // https://crbug.com/743291
   if ((screen_state_ == ScreenState::OFF_AUTO) !=
diff --git a/ash/system/power/power_button_display_controller.h b/ash/system/power/power_button_display_controller.h
index 3815c43..c7705e3 100644
--- a/ash/system/power/power_button_display_controller.h
+++ b/ash/system/power/power_button_display_controller.h
@@ -8,10 +8,15 @@
 #include "ash/ash_export.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
 #include "chromeos/dbus/power_manager_client.h"
 #include "ui/events/devices/input_device_event_observer.h"
 #include "ui/events/event_handler.h"
 
+namespace base {
+class TickClock;
+}  // namespace base
+
 namespace ash {
 
 // PowerButtonDisplayController performs display-related tasks (e.g. forcing
@@ -34,10 +39,13 @@
     OFF_AUTO,
   };
 
-  PowerButtonDisplayController();
+  explicit PowerButtonDisplayController(base::TickClock* tick_clock);
   ~PowerButtonDisplayController() override;
 
   ScreenState screen_state() const { return screen_state_; }
+  base::TimeTicks screen_state_last_changed() const {
+    return screen_state_last_changed_;
+  }
 
   // Updates the power manager's backlights-forced-off state and enables or
   // disables the touchscreen. No-op if |backlights_forced_off_| already equals
@@ -77,6 +85,12 @@
   // Current forced-off state of backlights.
   bool backlights_forced_off_ = false;
 
+  // Saves the most recent timestamp that screen state changed.
+  base::TimeTicks screen_state_last_changed_;
+
+  // Time source for performed action times.
+  base::TickClock* tick_clock_;  // Not owned.
+
   base::WeakPtrFactory<PowerButtonDisplayController> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PowerButtonDisplayController);
diff --git a/ash/system/power/power_event_observer.cc b/ash/system/power/power_event_observer.cc
index c461256..7184174 100644
--- a/ash/system/power/power_event_observer.cc
+++ b/ash/system/power/power_event_observer.cc
@@ -9,7 +9,6 @@
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/wm/lock_state_controller.h"
-#include "ash/wm/power_button_controller.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
@@ -71,11 +70,6 @@
   }
 }
 
-void PowerEventObserver::BrightnessChanged(int level, bool user_initiated) {
-  Shell::Get()->power_button_controller()->OnScreenBrightnessChanged(
-      static_cast<double>(level));
-}
-
 void PowerEventObserver::SuspendImminent() {
   SessionController* controller = Shell::Get()->session_controller();
 
diff --git a/ash/system/power/power_event_observer.h b/ash/system/power/power_event_observer.h
index 63365b6..502484fa 100644
--- a/ash/system/power/power_event_observer.h
+++ b/ash/system/power/power_event_observer.h
@@ -30,7 +30,6 @@
   void OnLockAnimationsComplete();
 
   // chromeos::PowerManagerClient::Observer overrides:
-  void BrightnessChanged(int level, bool user_initiated) override;
   void SuspendImminent() override;
   void SuspendDone(const base::TimeDelta& sleep_duration) override;
 
diff --git a/ash/system/power/tablet_power_button_controller.cc b/ash/system/power/tablet_power_button_controller.cc
index d3e045e..232e7132 100644
--- a/ash/system/power/tablet_power_button_controller.cc
+++ b/ash/system/power/tablet_power_button_controller.cc
@@ -31,16 +31,19 @@
 // Amount of time the power button must be held to start the pre-shutdown
 // animation when in tablet mode. This differs depending on whether the screen
 // is on or off when the power button is initially pressed.
-constexpr int kShutdownWhenScreenOnTimeoutMs = 500;
+constexpr base::TimeDelta kShutdownWhenScreenOnTimeout =
+    base::TimeDelta::FromMilliseconds(500);
 // TODO(derat): This is currently set to a high value to work around delays in
 // powerd's reports of button-up events when the preceding button-down event
 // turns the display on. Set it to a lower value once powerd no longer blocks on
 // asking Chrome to turn the display on: http://crbug.com/685734
-constexpr int kShutdownWhenScreenOffTimeoutMs = 2000;
+constexpr base::TimeDelta kShutdownWhenScreenOffTimeout =
+    base::TimeDelta::FromMilliseconds(2000);
 
 // Amount of time since last SuspendDone() that power button event needs to be
 // ignored.
-constexpr int kIgnorePowerButtonAfterResumeMs = 2000;
+constexpr base::TimeDelta kIgnorePowerButtonAfterResumeDelay =
+    base::TimeDelta::FromSeconds(2);
 
 // Returns true if device is a convertible/tablet device, otherwise false.
 bool IsTabletModeSupported() {
@@ -81,11 +84,16 @@
 
 }  // namespace
 
+constexpr base::TimeDelta TabletPowerButtonController::kScreenStateChangeDelay;
+
+constexpr base::TimeDelta
+    TabletPowerButtonController::kIgnoreRepeatedButtonUpDelay;
+
 TabletPowerButtonController::TestApi::TestApi(
     TabletPowerButtonController* controller)
     : controller_(controller) {}
 
-TabletPowerButtonController::TestApi::~TestApi() {}
+TabletPowerButtonController::TestApi::~TestApi() = default;
 
 bool TabletPowerButtonController::TestApi::ShutdownTimerIsRunning() const {
   return controller_->shutdown_timer_.IsRunning();
@@ -156,12 +164,19 @@
     // backlight has been turned back on before seeing the power button events
     // that woke the system. Avoid forcing off display just after resuming to
     // ensure that we don't turn the display off in response to the events.
-    if (timestamp - last_resume_time_ <=
-        base::TimeDelta::FromMilliseconds(kIgnorePowerButtonAfterResumeMs)) {
+    if (timestamp - last_resume_time_ <= kIgnorePowerButtonAfterResumeDelay)
+      force_off_on_button_up_ = false;
+
+    // The actual display may remain off for a short period after powerd asks
+    // Chrome to turn it on. If the user presses the power button again during
+    // this time, they probably intend to turn the display on. Avoid forcing off
+    // in this case.
+    if (timestamp - display_controller_->screen_state_last_changed() <=
+        kScreenStateChangeDelay) {
       force_off_on_button_up_ = false;
     }
 
-    last_button_down_time_ = tick_clock_->NowTicks();
+    last_button_down_time_ = timestamp;
     screen_off_when_power_button_down_ =
         display_controller_->screen_state() !=
         PowerButtonDisplayController::ScreenState::ON;
@@ -173,7 +188,7 @@
       return;
 
     const base::TimeTicks previous_up_time = last_button_up_time_;
-    last_button_up_time_ = tick_clock_->NowTicks();
+    last_button_up_time_ = timestamp;
 
     if (max_accelerometer_samples_) {
       base::TimeDelta duration = last_button_up_time_ - last_button_down_time_;
@@ -187,8 +202,7 @@
       lock_state_controller_->CancelShutdownAnimation();
 
     // Ignore the event if it comes too soon after the last one.
-    if (timestamp - previous_up_time <=
-        base::TimeDelta::FromMilliseconds(kIgnoreRepeatedButtonUpMs)) {
+    if (timestamp - previous_up_time <= kIgnoreRepeatedButtonUpDelay) {
       shutdown_timer_.Stop();
       return;
     }
@@ -348,9 +362,9 @@
 }
 
 void TabletPowerButtonController::StartShutdownTimer() {
-  base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
-      screen_off_when_power_button_down_ ? kShutdownWhenScreenOffTimeoutMs
-                                         : kShutdownWhenScreenOnTimeoutMs);
+  base::TimeDelta timeout = screen_off_when_power_button_down_
+                                ? kShutdownWhenScreenOffTimeout
+                                : kShutdownWhenScreenOnTimeout;
   shutdown_timer_.Start(FROM_HERE, timeout, this,
                         &TabletPowerButtonController::OnShutdownTimeout);
 }
diff --git a/ash/system/power/tablet_power_button_controller.h b/ash/system/power/tablet_power_button_controller.h
index 8e65c56..4067b0e 100644
--- a/ash/system/power/tablet_power_button_controller.h
+++ b/ash/system/power/tablet_power_button_controller.h
@@ -73,10 +73,16 @@
   // Public for tests.
   static constexpr float kGravity = 9.80665f;
 
+  // Amount of time since last screen state change that power button event needs
+  // to be ignored.
+  static constexpr base::TimeDelta kScreenStateChangeDelay =
+      base::TimeDelta::FromMilliseconds(500);
+
   // Ignore button-up events occurring within this many milliseconds of the
   // previous button-up event. This prevents us from falling behind if the power
   // button is pressed repeatedly.
-  static constexpr int kIgnoreRepeatedButtonUpMs = 500;
+  static constexpr base::TimeDelta kIgnoreRepeatedButtonUpDelay =
+      base::TimeDelta::FromMilliseconds(500);
 
   TabletPowerButtonController(PowerButtonDisplayController* display_controller,
                               base::TickClock* tick_clock);
diff --git a/ash/system/power/tablet_power_button_controller_unittest.cc b/ash/system/power/tablet_power_button_controller_unittest.cc
index 4425daf..de37e9ff 100644
--- a/ash/system/power/tablet_power_button_controller_unittest.cc
+++ b/ash/system/power/tablet_power_button_controller_unittest.cc
@@ -74,7 +74,7 @@
         std::make_unique<LockStateControllerTestApi>(lock_state_controller_);
     generator_ = &AshTestBase::GetEventGenerator();
 
-    power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+    power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
     EXPECT_FALSE(power_manager_client_->backlights_forced_off());
 
     // Advance a long duration from initialized last resume time in
@@ -191,8 +191,9 @@
   // off is not ignored since we will ignore the repeated power button up if
   // they come too close.
   void AdvanceClockToAvoidIgnoring() {
-    tick_clock_->Advance(base::TimeDelta::FromMilliseconds(
-        TabletPowerButtonController::kIgnoreRepeatedButtonUpMs + 1));
+    tick_clock_->Advance(
+        TabletPowerButtonController::kIgnoreRepeatedButtonUpDelay +
+        base::TimeDelta::FromMilliseconds(1));
   }
 
   // Ownership is passed on to chromeos::DBusThreadManager.
@@ -245,12 +246,12 @@
   EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   EXPECT_FALSE(test_api_->ShutdownTimerIsRunning());
   EXPECT_TRUE(power_manager_client_->backlights_forced_off());
 
   PressPowerButton();
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
   ReleasePowerButton();
@@ -273,11 +274,11 @@
   AdvanceClockToAvoidIgnoring();
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   EXPECT_TRUE(power_manager_client_->backlights_forced_off());
 
   PressPowerButton();
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
   test_api_->TriggerShutdownTimeout();
   EXPECT_TRUE(lock_state_test_api_->shutdown_timer_is_running());
@@ -288,10 +289,10 @@
 
 // Tests tapping power button when screen is idle off.
 TEST_F(TabletPowerButtonControllerTest, TappingPowerButtonWhenScreenIsIdleOff) {
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   PressPowerButton();
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   ReleasePowerButton();
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
 }
@@ -301,10 +302,10 @@
 TEST_F(TabletPowerButtonControllerTest,
        TappingPowerButtonWhenSuspendedWithoutBacklightsForcedOff) {
   power_manager_client_->SendSuspendImminent();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   // There is a power button pressed here, but PowerButtonEvent is sent later.
   power_manager_client_->SendSuspendDone();
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
 
   // Send the power button event after a short delay and check that backlights
   // are not forced off.
@@ -321,7 +322,7 @@
   PressPowerButton();
   EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   EXPECT_FALSE(test_api_->ShutdownTimerIsRunning());
   EXPECT_TRUE(power_manager_client_->backlights_forced_off());
 }
@@ -332,7 +333,7 @@
        TappingPowerButtonWhenSuspendedWithBacklightsForcedOff) {
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
   power_manager_client_->SendSuspendImminent();
   // There is a power button pressed here, but PowerButtonEvent is sent later.
@@ -344,7 +345,7 @@
   // are not forced off.
   tick_clock_->Advance(base::TimeDelta::FromMilliseconds(500));
   PressPowerButton();
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   ReleasePowerButton();
   EXPECT_FALSE(test_api_->ShutdownTimerIsRunning());
@@ -356,7 +357,7 @@
   PressPowerButton();
   EXPECT_TRUE(test_api_->ShutdownTimerIsRunning());
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   EXPECT_FALSE(test_api_->ShutdownTimerIsRunning());
   EXPECT_TRUE(power_manager_client_->backlights_forced_off());
 }
@@ -369,27 +370,27 @@
   // KeyEvent should SetBacklightsForcedOff(false).
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
   generator_->PressKey(ui::VKEY_L, ui::EF_NONE);
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
 
   // Regular mouse event should SetBacklightsForcedOff(false).
   AdvanceClockToAvoidIgnoring();
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
   generator_->MoveMouseBy(1, 1);
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
 
   // Synthesized mouse event should not SetBacklightsForcedOff(false).
   AdvanceClockToAvoidIgnoring();
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
   generator_->set_flags(ui::EF_IS_SYNTHESIZED);
   generator_->MoveMouseBy(1, 1);
@@ -404,7 +405,7 @@
 
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
   generator_->PressKey(ui::VKEY_L, ui::EF_NONE);
   EXPECT_TRUE(power_manager_client_->backlights_forced_off());
@@ -445,11 +446,11 @@
   ASSERT_TRUE(GetGlobalTouchscreenEnabled());
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   EXPECT_FALSE(GetGlobalTouchscreenEnabled());
 
   PressPowerButton();
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   ReleasePowerButton();
   EXPECT_TRUE(GetGlobalTouchscreenEnabled());
 
@@ -458,22 +459,22 @@
   AdvanceClockToAvoidIgnoring();
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
   ASSERT_FALSE(GetGlobalTouchscreenEnabled());
   generator_->PressKey(ui::VKEY_L, ui::EF_NONE);
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   EXPECT_TRUE(GetGlobalTouchscreenEnabled());
 
   // MouseEvent on laptop mode when screen is off.
   AdvanceClockToAvoidIgnoring();
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
   ASSERT_FALSE(GetGlobalTouchscreenEnabled());
   generator_->MoveMouseBy(1, 1);
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   EXPECT_TRUE(GetGlobalTouchscreenEnabled());
 }
 
@@ -546,14 +547,14 @@
   // Set backlights forced off for starting point.
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
 
   // Test that a pressing-releasing operation after a short duration, backlights
   // forced off is stopped since we don't drop request for power button pressed.
   tick_clock_->Advance(base::TimeDelta::FromMilliseconds(200));
   PressPowerButton();
-  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, false);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
   ReleasePowerButton();
   EXPECT_FALSE(power_manager_client_->backlights_forced_off());
 
@@ -568,7 +569,7 @@
   tick_clock_->Advance(base::TimeDelta::FromMilliseconds(800));
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   EXPECT_TRUE(power_manager_client_->backlights_forced_off());
 }
 
@@ -737,7 +738,7 @@
 TEST_F(TabletPowerButtonControllerTest, SuspendDoneStopsForcingOff) {
   PressPowerButton();
   ReleasePowerButton();
-  power_manager_client_->SendBrightnessChanged(0, false);
+  power_manager_client_->SendBrightnessChanged(0, true);
   ASSERT_TRUE(power_manager_client_->backlights_forced_off());
 
   // Simulate an edge case that system resumes because of tablet power button
@@ -814,4 +815,35 @@
   EXPECT_TRUE(GetGlobalTouchscreenEnabled());
 }
 
+// Tests that during the interval that the display is turning on, tablet power
+// button should not set display off (crbug.com/735225).
+TEST_F(TabletPowerButtonControllerTest,
+       IgnoreForcingOffWhenDisplayIsTurningOn) {
+  PressPowerButton();
+  ReleasePowerButton();
+  power_manager_client_->SendBrightnessChanged(0, true);
+  ASSERT_TRUE(power_manager_client_->backlights_forced_off());
+
+  // Trigger a key event to stop backlights forcing off. Chrome will receive
+  // brightness changed signal. But we may still have display off state.
+  generator_->PressKey(ui::VKEY_L, ui::EF_NONE);
+  power_manager_client_->SendBrightnessChanged(kNonZeroBrightness, true);
+  EXPECT_FALSE(power_manager_client_->backlights_forced_off());
+
+  // Since display could still be off, ignore forcing off.
+  tick_clock_->Advance(TabletPowerButtonController::kScreenStateChangeDelay -
+                       base::TimeDelta::FromMilliseconds(1));
+  PressPowerButton();
+  ReleasePowerButton();
+  EXPECT_FALSE(power_manager_client_->backlights_forced_off());
+
+  // After avoiding ignoring the repeated power button releases, we should be
+  // able to set display off.
+  AdvanceClockToAvoidIgnoring();
+  PressPowerButton();
+  ReleasePowerButton();
+  power_manager_client_->SendBrightnessChanged(0, true);
+  EXPECT_TRUE(power_manager_client_->backlights_forced_off());
+}
+
 }  // namespace ash
diff --git a/ash/wm/lock_state_controller_unittest.cc b/ash/wm/lock_state_controller_unittest.cc
index 14171e5..a764e734 100644
--- a/ash/wm/lock_state_controller_unittest.cc
+++ b/ash/wm/lock_state_controller_unittest.cc
@@ -912,14 +912,14 @@
 
   // When the screen brightness is at 0%, we shouldn't do anything in response
   // to power button presses.
-  power_button_controller_->OnScreenBrightnessChanged(0.0);
+  power_manager_client_->SendBrightnessChanged(0, true);
   PressPowerButton();
   EXPECT_FALSE(test_api_->is_animating_lock());
   ReleasePowerButton();
 
   // After increasing the brightness to 10%, we should start the timer like
   // usual.
-  power_button_controller_->OnScreenBrightnessChanged(10.0);
+  power_manager_client_->SendBrightnessChanged(10, true);
   PressPowerButton();
   EXPECT_TRUE(test_api_->is_animating_lock());
   ReleasePowerButton();
@@ -947,7 +947,7 @@
 
   // When all of the displays are turned off (e.g. due to user inactivity), the
   // power button should be ignored.
-  power_button_controller_->OnScreenBrightnessChanged(0.0);
+  power_manager_client_->SendBrightnessChanged(0, true);
   internal_display->set_current_mode(nullptr);
   external_display->set_current_mode(nullptr);
   power_button_controller_->OnDisplayModeChanged(outputs);
diff --git a/ash/wm/power_button_controller.cc b/ash/wm/power_button_controller.cc
index 3c531435..72f4f756 100644
--- a/ash/wm/power_button_controller.cc
+++ b/ash/wm/power_button_controller.cc
@@ -39,7 +39,8 @@
     : lock_state_controller_(Shell::Get()->lock_state_controller()),
       tick_clock_(new base::DefaultTickClock) {
   ProcessCommandLine();
-  display_controller_ = std::make_unique<PowerButtonDisplayController>();
+  display_controller_ =
+      std::make_unique<PowerButtonDisplayController>(tick_clock_.get());
   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(
       this);
   chromeos::AccelerometerReader::GetInstance()->AddObserver(this);
@@ -55,10 +56,6 @@
       this);
 }
 
-void PowerButtonController::OnScreenBrightnessChanged(double percent) {
-  brightness_is_zero_ = percent <= 0.001;
-}
-
 void PowerButtonController::OnPowerButtonEvent(
     bool down,
     const base::TimeTicks& timestamp) {
@@ -227,6 +224,10 @@
       internal_display_off && external_display_on;
 }
 
+void PowerButtonController::BrightnessChanged(int level, bool user_initiated) {
+  brightness_is_zero_ = level == 0;
+}
+
 void PowerButtonController::PowerButtonEventReceived(
     bool down,
     const base::TimeTicks& timestamp) {
@@ -245,6 +246,9 @@
     std::unique_ptr<base::TickClock> tick_clock) {
   DCHECK(tick_clock);
   tick_clock_ = std::move(tick_clock);
+
+  display_controller_ =
+      std::make_unique<PowerButtonDisplayController>(tick_clock_.get());
 }
 
 bool PowerButtonController::TriggerDisplayOffTimerForTesting() {
diff --git a/ash/wm/power_button_controller.h b/ash/wm/power_button_controller.h
index a7fb8457..41adbcb 100644
--- a/ash/wm/power_button_controller.h
+++ b/ash/wm/power_button_controller.h
@@ -40,9 +40,6 @@
     has_legacy_power_button_ = legacy;
   }
 
-  // Called when the current screen brightness changes.
-  void OnScreenBrightnessChanged(double percent);
-
   // Called when the power or lock buttons are pressed or released.
   void OnPowerButtonEvent(bool down, const base::TimeTicks& timestamp);
   void OnLockButtonEvent(bool down, const base::TimeTicks& timestamp);
@@ -57,6 +54,7 @@
       const display::DisplayConfigurator::DisplayStateList& outputs) override;
 
   // Overridden from chromeos::PowerManagerClient::Observer:
+  void BrightnessChanged(int level, bool user_initiated) override;
   void PowerButtonEventReceived(bool down,
                                 const base::TimeTicks& timestamp) override;
 
diff --git a/base/task_scheduler/scheduler_single_thread_task_runner_manager.cc b/base/task_scheduler/scheduler_single_thread_task_runner_manager.cc
index 39e45a705..a3a493f 100644
--- a/base/task_scheduler/scheduler_single_thread_task_runner_manager.cc
+++ b/base/task_scheduler/scheduler_single_thread_task_runner_manager.cc
@@ -11,7 +11,6 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/debug/stack_trace.h"
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
@@ -36,6 +35,22 @@
 
 namespace {
 
+// Boolean indicating whether there's a SchedulerSingleThreadTaskRunnerManager
+// instance alive in this process. This variable should only be set when the
+// SchedulerSingleThreadTaskRunnerManager instance is brought up (on the main
+// thread; before any tasks are posted) and decremented when the instance is
+// brought down (i.e., only when unit tests tear down the task environment and
+// never in production). This makes the variable const while worker threads are
+// up and as such it doesn't need to be atomic. It is used to tell when a task
+// is posted from the main thread after the task environment was brought down in
+// unit tests so that SchedulerSingleThreadTaskRunnerManager bound TaskRunners
+// can return false on PostTask, letting such callers know they should complete
+// necessary work synchronously. Note: |!g_manager_is_alive| is generally
+// equivalent to |!TaskScheduler::GetInstance()| but has the advantage of being
+// valid in task_scheduler unit tests that don't instantiate a full
+// TaskScheduler.
+bool g_manager_is_alive = false;
+
 // Allows for checking the PlatformThread::CurrentRef() against a set
 // PlatformThreadRef atomically without using locks.
 class AtomicThreadRefChecker {
@@ -258,6 +273,9 @@
   bool PostDelayedTask(const Location& from_here,
                        OnceClosure closure,
                        TimeDelta delay) override {
+    if (!g_manager_is_alive)
+      return false;
+
     auto task =
         std::make_unique<Task>(from_here, std::move(closure), traits_, delay);
     task->single_thread_task_runner_ref = this;
@@ -284,6 +302,8 @@
   }
 
   bool RunsTasksInCurrentSequence() const override {
+    if (!g_manager_is_alive)
+      return false;
     return GetDelegate()->RunsTasksInCurrentSequence();
   }
 
@@ -291,12 +311,24 @@
   ~SchedulerSingleThreadTaskRunner() override {
     // Only unregister if this is a DEDICATED SingleThreadTaskRunner. SHARED
     // task runner SchedulerWorkers are managed separately as they are reused.
-    if (thread_mode_ == SingleThreadTaskRunnerThreadMode::DEDICATED) {
-      // Note: This will crash if SchedulerSingleThreadTaskRunnerManager is
-      // incorrectly destroyed first in tests (in production the TaskScheduler
-      // and all of its state are intentionally leaked after
-      // TaskScheduler::Shutdown(). See
-      // ~SchedulerSingleThreadTaskRunnerManager() for more details.
+    // |g_manager_is_alive| avoids a use-after-free should this
+    // SchedulerSingleThreadTaskRunner outlive its manager. It is safe to access
+    // |g_manager_is_alive| without synchronization primitives as it is const
+    // for the lifetime of the manager and ~SchedulerSingleThreadTaskRunner()
+    // either happens prior to the end of JoinForTesting() (which happens-before
+    // manager's destruction) or on main thread after the task environment's
+    // entire destruction (which happens-after the manager's destruction). Yes,
+    // there's a theoretical use case where the last ref to this
+    // SchedulerSingleThreadTaskRunner is handed to a thread not controlled by
+    // task_scheduler and that this ends up causing
+    // ~SchedulerSingleThreadTaskRunner() to race with
+    // ~SchedulerSingleThreadTaskRunnerManager() but this is intentionally not
+    // supported (and it doesn't matter in production where we leak the task
+    // environment for such reasons). TSan should catch this weird paradigm
+    // should anyone elect to use it in a unit test and the error would point
+    // here.
+    if (g_manager_is_alive &&
+        thread_mode_ == SingleThreadTaskRunnerThreadMode::DEDICATED) {
       outer_->UnregisterSchedulerWorker(worker_);
     }
   }
@@ -339,36 +371,14 @@
                 "The size of |shared_com_scheduler_workers_| must match "
                 "|shared_scheduler_workers_|");
 #endif  // defined(OS_WIN)
+  DCHECK(!g_manager_is_alive);
+  g_manager_is_alive = true;
 }
 
 SchedulerSingleThreadTaskRunnerManager::
     ~SchedulerSingleThreadTaskRunnerManager() {
-#if DCHECK_IS_ON()
-  size_t workers_unregistered_during_join =
-      subtle::NoBarrier_Load(&workers_unregistered_during_join_);
-  // Log an ERROR instead of DCHECK'ing as it's often useful to have both the
-  // stack trace of this call and the crash stack trace of the upcoming
-  // out-of-order ~SchedulerSingleThreadTaskRunner() call to know what to flip.
-  DLOG_IF(ERROR, workers_unregistered_during_join != workers_.size())
-      << "Expect incoming crash in ~SchedulerSingleThreadTaskRunner()!!! There "
-         "cannot be outstanding SingleThreadTaskRunners upon destruction "
-         "of SchedulerSingleThreadTaskRunnerManager in tests "
-      << workers_.size() - workers_unregistered_during_join << " outstanding). "
-      << "Hint 1: If you're hitting this it's most likely because your test "
-         "fixture is destroying its TaskScheduler too early (e.g. via "
-         "base::test::~ScopedTaskEnvironment() or "
-         "content::~TestBrowserThreadBundle()). Refer to the following stack "
-         "trace to know what caused this destruction as well as to the "
-         "upcoming crash in ~SchedulerSingleThreadTaskRunner() to know what "
-         "should have happened before. "
-         "Hint 2: base::test::ScopedTaskEnvironment et al. should typically "
-         "be the first member in a test fixture to ensure it's initialized "
-         "first and destroyed last.\n"
-#if !defined(OS_NACL)  // We don't build base/debug/stack_trace.cc for NaCl.
-      << base::debug::StackTrace().ToString()
-#endif  // !defined(OS_NACL)
-      ;
-#endif  // DCHECK_IS_ON()
+  DCHECK(g_manager_is_alive);
+  g_manager_is_alive = false;
 }
 
 void SchedulerSingleThreadTaskRunnerManager::Start() {
@@ -534,14 +544,9 @@
   {
     AutoSchedulerLock auto_lock(lock_);
 
-    // We might be joining, so record that a worker was unregistered for
-    // verification at destruction.
-    if (workers_.empty()) {
-#if DCHECK_IS_ON()
-      subtle::NoBarrier_AtomicIncrement(&workers_unregistered_during_join_, 1);
-#endif
+    // Skip when joining (the join logic takes care of the rest).
+    if (workers_.empty())
       return;
-    }
 
     auto worker_iter =
         std::find_if(workers_.begin(), workers_.end(),
diff --git a/base/task_scheduler/scheduler_single_thread_task_runner_manager.h b/base/task_scheduler/scheduler_single_thread_task_runner_manager.h
index 1b4e51a..34eb81fd 100644
--- a/base/task_scheduler/scheduler_single_thread_task_runner_manager.h
+++ b/base/task_scheduler/scheduler_single_thread_task_runner_manager.h
@@ -9,9 +9,7 @@
 #include <string>
 #include <vector>
 
-#include "base/atomicops.h"
 #include "base/base_export.h"
-#include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/task_scheduler/environment_config.h"
@@ -110,10 +108,6 @@
   TaskTracker* const task_tracker_;
   DelayedTaskManager* const delayed_task_manager_;
 
-#if DCHECK_IS_ON()
-  subtle::Atomic32 workers_unregistered_during_join_ = 0;
-#endif
-
   // Synchronizes access to all members below.
   SchedulerLock lock_;
   std::vector<scoped_refptr<SchedulerWorker>> workers_;
diff --git a/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc b/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
index 38b80f64..bca4b4b9e 100644
--- a/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
+++ b/base/task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc
@@ -48,7 +48,8 @@
   }
 
   void TearDown() override {
-    TearDownSingleThreadTaskRunnerManager();
+    if (single_thread_task_runner_manager_)
+      TearDownSingleThreadTaskRunnerManager();
     service_thread_.Stop();
   }
 
@@ -331,6 +332,19 @@
             TimeDelta::FromMilliseconds(250) + TestTimeouts::tiny_timeout());
 }
 
+// Verify that posting tasks after the single-thread manager is destroyed fails
+// but doesn't crash.
+TEST_P(TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
+       PostTaskAfterDestroy) {
+  auto task_runner = single_thread_task_runner_manager_
+                         ->CreateSingleThreadTaskRunnerWithTraits(
+                             "A", TaskTraits(), GetParam());
+  EXPECT_TRUE(task_runner->PostTask(FROM_HERE, BindOnce(&DoNothing)));
+  task_tracker_.Shutdown();
+  TearDownSingleThreadTaskRunnerManager();
+  EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
+}
+
 INSTANTIATE_TEST_CASE_P(
     AllModes,
     TaskSchedulerSingleThreadTaskRunnerManagerCommonTest,
diff --git a/base/task_scheduler/scheduler_worker_pool.cc b/base/task_scheduler/scheduler_worker_pool.cc
index 50e1985..bf575cf 100644
--- a/base/task_scheduler/scheduler_worker_pool.cc
+++ b/base/task_scheduler/scheduler_worker_pool.cc
@@ -16,6 +16,21 @@
 
 namespace {
 
+// The number of SchedulerWorkerPool that are alive in this process. This
+// variable should only be incremented when the SchedulerWorkerPool instances
+// are brought up (on the main thread; before any tasks are posted) and
+// decremented when the same instances are brought down (i.e., only when unit
+// tests tear down the task environment and never in production). This makes the
+// variable const while worker threads are up and as such it doesn't need to be
+// atomic. It is used to tell when a task is posted from the main thread after
+// the task environment was brought down in unit tests so that
+// SchedulerWorkerPool bound TaskRunners can return false on PostTask, letting
+// such callers know they should complete necessary work synchronously. Note:
+// |!g_active_pools_count| is generally equivalent to
+// |!TaskScheduler::GetInstance()| but has the advantage of being valid in
+// task_scheduler unit tests that don't instantiate a full TaskScheduler.
+int g_active_pools_count = 0;
+
 // SchedulerWorkerPool that owns the current thread, if any.
 LazyInstance<ThreadLocalPointer<const SchedulerWorkerPool>>::Leaky
     tls_current_worker_pool = LAZY_INSTANCE_INITIALIZER;
@@ -42,6 +57,9 @@
   bool PostDelayedTask(const Location& from_here,
                        OnceClosure closure,
                        TimeDelta delay) override {
+    if (!g_active_pools_count)
+      return false;
+
     // Post the task as part of a one-off single-task Sequence.
     return worker_pool_->PostTaskWithSequence(
         std::make_unique<Task>(from_here, std::move(closure), traits_, delay),
@@ -77,6 +95,9 @@
   bool PostDelayedTask(const Location& from_here,
                        OnceClosure closure,
                        TimeDelta delay) override {
+    if (!g_active_pools_count)
+      return false;
+
     std::unique_ptr<Task> task =
         std::make_unique<Task>(from_here, std::move(closure), traits_, delay);
     task->sequenced_task_runner_ref = this;
@@ -154,6 +175,12 @@
     : task_tracker_(task_tracker), delayed_task_manager_(delayed_task_manager) {
   DCHECK(task_tracker_);
   DCHECK(delayed_task_manager_);
+  ++g_active_pools_count;
+}
+
+SchedulerWorkerPool::~SchedulerWorkerPool() {
+  --g_active_pools_count;
+  DCHECK_GE(g_active_pools_count, 0);
 }
 
 void SchedulerWorkerPool::BindToCurrentThread() {
diff --git a/base/task_scheduler/scheduler_worker_pool.h b/base/task_scheduler/scheduler_worker_pool.h
index c7cb2d1..088beb0 100644
--- a/base/task_scheduler/scheduler_worker_pool.h
+++ b/base/task_scheduler/scheduler_worker_pool.h
@@ -24,7 +24,7 @@
 // Interface for a worker pool.
 class BASE_EXPORT SchedulerWorkerPool {
  public:
-  virtual ~SchedulerWorkerPool() = default;
+  virtual ~SchedulerWorkerPool();
 
   // Returns a TaskRunner whose PostTask invocations result in scheduling tasks
   // in this SchedulerWorkerPool using |traits|. Tasks may run in any order and
diff --git a/base/task_scheduler/scheduler_worker_pool_unittest.cc b/base/task_scheduler/scheduler_worker_pool_unittest.cc
index 1ea3e4f..a1ddb3bf 100644
--- a/base/task_scheduler/scheduler_worker_pool_unittest.cc
+++ b/base/task_scheduler/scheduler_worker_pool_unittest.cc
@@ -102,7 +102,8 @@
 
   void TearDown() override {
     service_thread_.Stop();
-    worker_pool_->JoinForTesting();
+    if (worker_pool_)
+      worker_pool_->JoinForTesting();
   }
 
   void CreateWorkerPool() {
@@ -214,6 +215,19 @@
   EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
 }
 
+// Verify that posting tasks after the pool was destroyed fails but doesn't
+// crash.
+TEST_P(TaskSchedulerWorkerPoolTest, PostAfterDestroy) {
+  StartWorkerPool();
+  auto task_runner = test::CreateTaskRunnerWithExecutionMode(
+      worker_pool_.get(), GetParam().execution_mode);
+  EXPECT_TRUE(task_runner->PostTask(FROM_HERE, BindOnce(&DoNothing)));
+  task_tracker_.Shutdown();
+  worker_pool_->JoinForTesting();
+  worker_pool_.reset();
+  EXPECT_FALSE(task_runner->PostTask(FROM_HERE, BindOnce(&ShouldNotRun)));
+}
+
 // Verify that a Task runs shortly after its delay expires.
 TEST_P(TaskSchedulerWorkerPoolTest, PostDelayedTask) {
   StartWorkerPool();
@@ -266,7 +280,7 @@
   task_ran.Wait();
 }
 
-// Verify that after tasks posted before Start run after Start.
+// Verify that tasks posted before Start run after Start.
 TEST_P(TaskSchedulerWorkerPoolTest, PostBeforeStart) {
   WaitableEvent task_1_scheduled(WaitableEvent::ResetPolicy::MANUAL,
                                  WaitableEvent::InitialState::NOT_SIGNALED);
diff --git a/base/task_scheduler/task_scheduler.cc b/base/task_scheduler/task_scheduler.cc
index 53ff541..e01426e 100644
--- a/base/task_scheduler/task_scheduler.cc
+++ b/base/task_scheduler/task_scheduler.cc
@@ -40,6 +40,11 @@
 #if !defined(OS_NACL)
 // static
 void TaskScheduler::CreateAndStartWithDefaultParams(StringPiece name) {
+  Create(name);
+  GetInstance()->StartWithDefaultParams();
+}
+
+void TaskScheduler::StartWithDefaultParams() {
   // Values were chosen so that:
   // * There are few background threads.
   // * Background threads never outnumber foreground threads.
@@ -52,12 +57,10 @@
 
   constexpr TimeDelta kSuggestedReclaimTime = TimeDelta::FromSeconds(30);
 
-  Create(name);
-  GetInstance()->Start(
-      {{kBackgroundMaxThreads, kSuggestedReclaimTime},
-       {kBackgroundBlockingMaxThreads, kSuggestedReclaimTime},
-       {kForegroundMaxThreads, kSuggestedReclaimTime},
-       {kForegroundBlockingMaxThreads, kSuggestedReclaimTime}});
+  Start({{kBackgroundMaxThreads, kSuggestedReclaimTime},
+         {kBackgroundBlockingMaxThreads, kSuggestedReclaimTime},
+         {kForegroundMaxThreads, kSuggestedReclaimTime},
+         {kForegroundBlockingMaxThreads, kSuggestedReclaimTime}});
 }
 #endif  // !defined(OS_NACL)
 
diff --git a/base/task_scheduler/task_scheduler.h b/base/task_scheduler/task_scheduler.h
index 0a794901..6e30af0 100644
--- a/base/task_scheduler/task_scheduler.h
+++ b/base/task_scheduler/task_scheduler.h
@@ -165,6 +165,10 @@
   // afterwards. CHECKs on failure. For tests, prefer
   // base::test::ScopedTaskEnvironment (ensures isolation).
   static void CreateAndStartWithDefaultParams(StringPiece name);
+
+  // Same as CreateAndStartWithDefaultParams() but allows callers to split the
+  // Create() and StartWithDefaultParams() calls.
+  void StartWithDefaultParams();
 #endif  // !defined(OS_NACL)
 
   // Creates a ready to start task scheduler. |name| is used to label threads
diff --git a/base/trace_event/memory_infra_background_whitelist.cc b/base/trace_event/memory_infra_background_whitelist.cc
index c9faefc..38101c3 100644
--- a/base/trace_event/memory_infra_background_whitelist.cc
+++ b/base/trace_event/memory_infra_background_whitelist.cc
@@ -15,7 +15,7 @@
 
 // The names of dump providers whitelisted for background tracing. Dump
 // providers can be added here only if the background mode dump has very
-// less performance and memory overhead.
+// little processor and memory overhead.
 const char* const kDumpProviderWhitelist[] = {
     "android::ResourceManagerImpl",
     "BlinkGC",
@@ -77,6 +77,7 @@
     "gpu/gl/textures/share_group_0x?",
     "java_heap",
     "java_heap/allocated_objects",
+    "leveldatabase/0x?",
     "leveldb/leveldb_proto/0x?",
     "leveldb/mojo/0x?",
     "leveldb/mojo/0x?/block_cache",
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index ce9b370..75ac7b26 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1241,16 +1241,13 @@
 
         # TODO(hans): https://crbug.com/763392
         "-Wno-tautological-unsigned-zero-compare",
-      ]
-      if (llvm_force_head_revision) {
-        cflags += [
-          # TODO(hans): https://crbug.com/766891
-          "-Wno-null-pointer-arithmetic",
 
-          # TODO(hans): https://crbug.com/767059
-          "-Wno-tautological-unsigned-enum-zero-compare",
-        ]
-      }
+        # TODO(hans): https://crbug.com/766891
+        "-Wno-null-pointer-arithmetic",
+
+        # TODO(hans): https://crbug.com/767059
+        "-Wno-tautological-unsigned-enum-zero-compare",
+      ]
     } else if (use_xcode_clang && xcode_version_int >= 830) {
       # This is necessary to allow a progressive transition from using xcode 8.0
       # to 8.3 or more recent. Remove when all bots are migrated to 8.3.
diff --git a/chrome/VERSION b/chrome/VERSION
index b2b13ce..eeaa714 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=63
 MINOR=0
-BUILD=3221
+BUILD=3222
 PATCH=0
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 92c66281..f6891940 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2604,6 +2604,8 @@
       "metrics/desktop_session_duration/desktop_session_duration_observer.h",
       "metrics/desktop_session_duration/desktop_session_duration_tracker.cc",
       "metrics/desktop_session_duration/desktop_session_duration_tracker.h",
+      "metrics/upgrade_metrics_provider.cc",
+      "metrics/upgrade_metrics_provider.h",
       "net/disk_cache_dir_policy_handler.cc",
       "net/disk_cache_dir_policy_handler.h",
       "notifications/fullscreen_notification_blocker.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 433a9aa..31713a0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3128,11 +3128,6 @@
      flag_descriptions::kOmniboxDisplayTitleForCurrentUrlDescription,
      kOsDesktop, FEATURE_VALUE_TYPE(omnibox::kDisplayTitleForCurrentUrl)},
 
-    {"enable-color-correct-rendering",
-     flag_descriptions::kColorCorrectRenderingName,
-     flag_descriptions::kColorCorrectRenderingDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kColorCorrectRendering)},
-
     {"force-color-profile", flag_descriptions::kForceColorProfileName,
      flag_descriptions::kForceColorProfileDescription, kOsAll,
      MULTI_VALUE_TYPE(kForceColorProfileChoices)},
diff --git a/chrome/browser/browser_process_impl_unittest.cc b/chrome/browser/browser_process_impl_unittest.cc
index 3104d1f..5299c8f 100644
--- a/chrome/browser/browser_process_impl_unittest.cc
+++ b/chrome/browser/browser_process_impl_unittest.cc
@@ -30,13 +30,14 @@
       : stashed_browser_process_(g_browser_process),
         loop_(base::MessageLoop::TYPE_UI),
         ui_thread_(content::BrowserThread::UI, &loop_),
-        file_thread_(
-            new content::TestBrowserThread(content::BrowserThread::FILE)),
         io_thread_(new content::TestBrowserThread(content::BrowserThread::IO)),
         command_line_(base::CommandLine::NO_PROGRAM),
         browser_process_impl_(
             new BrowserProcessImpl(base::ThreadTaskRunnerHandle::Get().get(),
                                    command_line_)) {
+    // Create() and StartWithDefaultParams() TaskScheduler in seperate steps to
+    // properly simulate the browser process' lifecycle.
+    base::TaskScheduler::Create("BrowserProcessImplTest");
     base::SetRecordActionTaskRunner(loop_.task_runner());
     browser_process_impl_->SetApplicationLocale("en");
   }
@@ -52,9 +53,7 @@
   // The UI thread needs to be alive while BrowserProcessImpl is alive, and is
   // managed separately.
   void StartSecondaryThreads() {
-    base::TaskScheduler::CreateAndStartWithDefaultParams(
-        "BrowserProcessImplTest");
-    file_thread_->StartIOThread();
+    base::TaskScheduler::GetInstance()->StartWithDefaultParams();
     io_thread_->StartIOThread();
   }
 
@@ -66,8 +65,6 @@
     base::RunLoop().RunUntilIdle();
     io_thread_.reset();
     base::RunLoop().RunUntilIdle();
-    file_thread_.reset();
-    base::RunLoop().RunUntilIdle();
     base::TaskScheduler::GetInstance()->Shutdown();
     base::TaskScheduler::GetInstance()->JoinForTesting();
     base::TaskScheduler::SetInstance(nullptr);
@@ -84,7 +81,6 @@
 #if defined(OS_WIN)
   base::win::ScopedCOMInitializer scoped_com_initializer_;
 #endif
-  std::unique_ptr<content::TestBrowserThread> file_thread_;
   std::unique_ptr<content::TestBrowserThread> io_thread_;
   base::CommandLine command_line_;
   std::unique_ptr<BrowserProcessImpl> browser_process_impl_;
diff --git a/chrome/browser/chromeos/arc/arc_support_host.cc b/chrome/browser/chromeos/arc/arc_support_host.cc
index 60858b0..b44272f 100644
--- a/chrome/browser/chromeos/arc/arc_support_host.cc
+++ b/chrome/browser/chromeos/arc/arc_support_host.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
+#include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/grit/generated_resources.h"
@@ -97,6 +98,9 @@
 // "onSendFeedbackClicked" is fired when a user clicks "Send Feedback" button.
 constexpr char kEventOnSendFeedbackClicked[] = "onSendFeedbackClicked";
 
+// "onOpenSettingsPageClicked" is fired when a user clicks settings link.
+constexpr char kEventOnOpenSettingsPageClicked[] = "onOpenSettingsPageClicked";
+
 void RequestOpenApp(Profile* profile) {
   const extensions::Extension* extension =
       extensions::ExtensionRegistry::Get(profile)->GetInstalledExtension(
@@ -626,6 +630,8 @@
   } else if (event == kEventOnSendFeedbackClicked) {
     DCHECK(error_delegate_);
     error_delegate_->OnSendFeedbackClicked();
+  } else if (event == kEventOnOpenSettingsPageClicked) {
+    chrome::ShowSettingsSubPageForProfile(profile_, std::string());
   } else {
     LOG(ERROR) << "Unknown message: " << event;
     NOTREACHED();
diff --git a/chrome/browser/chromeos/arc/notification/arc_boot_error_notification.cc b/chrome/browser/chromeos/arc/notification/arc_boot_error_notification.cc
index 3f2c47bc..571f1cc 100644
--- a/chrome/browser/chromeos/arc/notification/arc_boot_error_notification.cc
+++ b/chrome/browser/chromeos/arc/notification/arc_boot_error_notification.cc
@@ -7,6 +7,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/profiles/profile.h"
@@ -22,6 +23,7 @@
 #include "ui/message_center/notification.h"
 #include "ui/message_center/notification_types.h"
 #include "ui/message_center/notifier_settings.h"
+#include "ui/message_center/public/cpp/message_center_switches.h"
 
 namespace arc {
 
@@ -79,18 +81,36 @@
       user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId();
   notifier_id.profile_id = account_id.GetUserEmail();
 
+  std::unique_ptr<message_center::Notification> notification;
+  if (message_center::IsNewStyleNotificationEnabled()) {
+    notification = message_center::Notification::CreateSystemNotification(
+        message_center::NOTIFICATION_TYPE_SIMPLE, kLowDiskSpaceId,
+        l10n_util::GetStringUTF16(
+            IDS_ARC_CRITICALLY_LOW_DISK_NOTIFICATION_TITLE),
+        l10n_util::GetStringUTF16(
+            IDS_ARC_CRITICALLY_LOW_DISK_NOTIFICATION_MESSAGE),
+        gfx::Image(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+            IDR_DISK_SPACE_NOTIFICATION_CRITICAL)),
+        l10n_util::GetStringUTF16(IDS_ARC_NOTIFICATION_DISPLAY_SOURCE), GURL(),
+        notifier_id, optional_fields,
+        new LowDiskSpaceErrorNotificationDelegate(context),
+        kNotificationStorageFullIcon,
+        message_center::SystemNotificationWarningLevel::CRITICAL_WARNING);
+  } else {
+    notification = std::make_unique<message_center::Notification>(
+        message_center::NOTIFICATION_TYPE_SIMPLE, kLowDiskSpaceId,
+        l10n_util::GetStringUTF16(
+            IDS_ARC_CRITICALLY_LOW_DISK_NOTIFICATION_TITLE),
+        l10n_util::GetStringUTF16(
+            IDS_ARC_CRITICALLY_LOW_DISK_NOTIFICATION_MESSAGE),
+        gfx::Image(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+            IDR_DISK_SPACE_NOTIFICATION_CRITICAL)),
+        l10n_util::GetStringUTF16(IDS_ARC_NOTIFICATION_DISPLAY_SOURCE), GURL(),
+        notifier_id, optional_fields,
+        new LowDiskSpaceErrorNotificationDelegate(context));
+  }
   message_center::MessageCenter::Get()->AddNotification(
-      base::MakeUnique<message_center::Notification>(
-          message_center::NOTIFICATION_TYPE_SIMPLE, kLowDiskSpaceId,
-          l10n_util::GetStringUTF16(
-              IDS_ARC_CRITICALLY_LOW_DISK_NOTIFICATION_TITLE),
-          l10n_util::GetStringUTF16(
-              IDS_ARC_CRITICALLY_LOW_DISK_NOTIFICATION_MESSAGE),
-          gfx::Image(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
-              IDR_DISK_SPACE_NOTIFICATION_CRITICAL)),
-          l10n_util::GetStringUTF16(IDS_ARC_NOTIFICATION_DISPLAY_SOURCE),
-          GURL(), notifier_id, optional_fields,
-          new LowDiskSpaceErrorNotificationDelegate(context)));
+      std::move(notification));
 }
 
 // Singleton factory for ArcBootErrorNotificationFactory.
diff --git a/chrome/browser/extensions/api/automation/automation_apitest.cc b/chrome/browser/extensions/api/automation/automation_apitest.cc
index 5e299f0d..68a3b47 100644
--- a/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -286,6 +286,13 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(AutomationApiTest, TreeChangeIndirect) {
+  StartEmbeddedTestServer();
+  ASSERT_TRUE(
+      RunExtensionSubtest("automation/tests/tabs", "tree_change_indirect.html"))
+      << message_;
+}
+
 IN_PROC_BROWSER_TEST_F(AutomationApiTest, DocumentSelection) {
   StartEmbeddedTestServer();
   ASSERT_TRUE(
diff --git a/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc b/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc
index 97fa36f..4a9ea22 100644
--- a/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc
+++ b/chrome/browser/extensions/api/mdns/mdns_api_unittest.cc
@@ -23,7 +23,6 @@
 #include "extensions/browser/event_router_factory.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
-#include "extensions/common/extension_messages.h"
 #include "extensions/common/manifest_constants.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -365,15 +364,15 @@
         .Times(0);
     EventRouter::Get(browser_context())
         ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName,
-                                   render_process_host(), kExtId, base::nullopt,
-                                   filter, false);
+                                   render_process_host(), kExtId, filter,
+                                   false);
 
     EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local"))
         .Times(0);
     EventRouter::Get(browser_context())
         ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName,
-                                      render_process_host(), kExtId,
-                                      base::nullopt, filter, false);
+                                      render_process_host(), kExtId, filter,
+                                      false);
   }
   {
     base::DictionaryValue filter;
@@ -385,15 +384,15 @@
                 RegisterDnsSdListener("_testing._tcp.local"));
     EventRouter::Get(browser_context())
         ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName,
-                                   render_process_host(), kExtId, base::nullopt,
-                                   filter, false);
+                                   render_process_host(), kExtId, filter,
+                                   false);
 
     EXPECT_CALL(*dns_sd_registry(),
                 UnregisterDnsSdListener("_testing._tcp.local"));
     EventRouter::Get(browser_context())
         ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName,
-                                      render_process_host(), kExtId,
-                                      base::nullopt, filter, false);
+                                      render_process_host(), kExtId, filter,
+                                      false);
   }
 }
 
@@ -409,17 +408,15 @@
   ASSERT_TRUE(dns_sd_registry());
   // Test that the extension is able to listen to a non-whitelisted service
   EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local"));
-
   EventRouter::Get(browser_context())
       ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName,
-                                 render_process_host(), kExtId, base::nullopt,
-                                 filter, false);
+                                 render_process_host(), kExtId, filter, false);
 
   EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local"));
   EventRouter::Get(browser_context())
       ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName,
-                                    render_process_host(), kExtId,
-                                    base::nullopt, filter, false);
+                                    render_process_host(), kExtId, filter,
+                                    false);
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index 3f0cbdb..6f33f80c 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -950,10 +950,4 @@
   run_loop.Run();  // Wait until the message is handled by push service.
 }
 
-IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, FilteredEvents) {
-  // Extensions APIs from SW are only enabled on trunk.
-  ScopedCurrentChannel current_channel_override(version_info::Channel::UNKNOWN);
-  ASSERT_TRUE(RunExtensionTest("service_worker/filtered_events"));
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/test_extension_prefs.cc b/chrome/browser/extensions/test_extension_prefs.cc
index 12c9e8e..cfe98ee 100644
--- a/chrome/browser/extensions/test_extension_prefs.cc
+++ b/chrome/browser/extensions/test_extension_prefs.cc
@@ -16,6 +16,7 @@
 #include "base/sequenced_task_runner.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/clock.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/chrome_app_sorting.h"
 #include "chrome/browser/extensions/test_extension_system.h"
@@ -45,24 +46,22 @@
 
 namespace {
 
-// A TimeProvider which returns an incrementally later time each time
-// GetCurrentTime is called.
-class IncrementalTimeProvider : public ExtensionPrefs::TimeProvider {
+// A Clock which returns an incrementally later time each time Now() is called.
+class IncrementalClock : public base::Clock {
  public:
-  IncrementalTimeProvider() : current_time_(base::Time::Now()) {
-  }
+  IncrementalClock() : current_time_(base::Time::Now()) {}
 
-  ~IncrementalTimeProvider() override {}
+  ~IncrementalClock() override {}
 
-  base::Time GetCurrentTime() const override {
+  base::Time Now() override {
     current_time_ += base::TimeDelta::FromSeconds(10);
     return current_time_;
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(IncrementalTimeProvider);
+  base::Time current_time_;
 
-  mutable base::Time current_time_;
+  DISALLOW_COPY_AND_ASSIGN(IncrementalClock);
 };
 
 }  // namespace
@@ -126,8 +125,7 @@
       std::vector<ExtensionPrefsObserver*>(),
       // Guarantee that no two extensions get the same installation time
       // stamp and we can reliably assert the installation order in the tests.
-      std::unique_ptr<ExtensionPrefs::TimeProvider>(
-          new IncrementalTimeProvider())));
+      std::make_unique<IncrementalClock>()));
   ExtensionPrefsFactory::GetInstance()->SetInstanceForTesting(&profile_,
                                                               std::move(prefs));
   // Hack: After recreating ExtensionPrefs, the AppSorting also needs to be
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 650a07f..c2921c3 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -111,10 +111,6 @@
 const char kCloudImportName[] = "Cloud Import";
 const char kCloudImportDescription[] = "Allows the cloud-import feature.";
 
-const char kColorCorrectRenderingName[] = "Color correct rendering";
-const char kColorCorrectRenderingDescription[] =
-    "Enables color correct rendering of web content.";
-
 const char kForceColorProfileSRGB[] = "sRGB";
 const char kForceColorProfileP3[] = "Display P3 D65";
 const char kForceColorProfileColorSpin[] = "Color spin with gamma 2.4";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 7e3d2624..448076c 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -93,9 +93,6 @@
 extern const char kCloudImportName[];
 extern const char kCloudImportDescription[];
 
-extern const char kColorCorrectRenderingName[];
-extern const char kColorCorrectRenderingDescription[];
-
 extern const char kForceColorProfileSRGB[];
 extern const char kForceColorProfileP3[];
 extern const char kForceColorProfileColorSpin[];
diff --git a/chrome/browser/io_thread_unittest.cc b/chrome/browser/io_thread_unittest.cc
index d158d79..2dbc01f 100644
--- a/chrome/browser/io_thread_unittest.cc
+++ b/chrome/browser/io_thread_unittest.cc
@@ -173,13 +173,10 @@
 
  private:
   base::ShadowingAtExitManager at_exit_manager_;
+
+  // |pref_service_| must outlive |io_thread_|.
   TestingPrefServiceSimple pref_service_;
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  scoped_refptr<extensions::EventRouterForwarder> event_router_forwarder_;
-#endif
-  policy::PolicyMap policy_map_;
-  policy::MockPolicyService policy_service_;
-  SystemNetworkContextManager system_network_context_manager_;
+
   // The ordering of the declarations of |io_thread_object_| and
   // |thread_bundle_| matters. An IOThread cannot be deleted until all of
   // the globals have been reset to their initial state via CleanUp. As
@@ -188,6 +185,14 @@
   // the bundle is deleted first.
   std::unique_ptr<IOThread> io_thread_;
   content::TestBrowserThreadBundle thread_bundle_;
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  scoped_refptr<extensions::EventRouterForwarder> event_router_forwarder_;
+#endif
+  policy::PolicyMap policy_map_;
+  // |policy_service_| must be instantiated after |thread_bundle_|.
+  policy::MockPolicyService policy_service_;
+  SystemNetworkContextManager system_network_context_manager_;
 };
 
 TEST_F(IOThreadTestWithIOThreadObject, UpdateNegotiateDisableCnameLookup) {
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 6efbc806..6ea9d7e6 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -138,6 +138,10 @@
 #include "components/signin/core/browser/signin_status_metrics_provider.h"
 #endif  // !defined(OS_CHROMEOS)
 
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+#include "chrome/browser/metrics/upgrade_metrics_provider.h"
+#endif  //  !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+
 namespace {
 
 // This specifies the amount of time to wait for all renderers to send their
@@ -650,6 +654,11 @@
   metrics_service_->RegisterMetricsProvider(
       std::unique_ptr<metrics::MetricsProvider>(
           new CertificateReportingMetricsProvider()));
+
+#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
+  metrics_service_->RegisterMetricsProvider(
+      std::unique_ptr<metrics::MetricsProvider>(new UpgradeMetricsProvider()));
+#endif  //! defined(OS_ANDROID) && !defined(OS_CHROMEOS)
 }
 
 void ChromeMetricsServiceClient::RegisterUKMProviders() {
diff --git a/chrome/browser/metrics/upgrade_metrics_provider.cc b/chrome/browser/metrics/upgrade_metrics_provider.cc
new file mode 100644
index 0000000..53db7002
--- /dev/null
+++ b/chrome/browser/metrics/upgrade_metrics_provider.cc
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/metrics/upgrade_metrics_provider.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "chrome/browser/upgrade_detector.h"
+
+UpgradeMetricsProvider::UpgradeMetricsProvider() {}
+
+UpgradeMetricsProvider::~UpgradeMetricsProvider() {}
+
+void UpgradeMetricsProvider::ProvideCurrentSessionData(
+    metrics::ChromeUserMetricsExtension* uma_proto) {
+  UpgradeDetector* upgrade_detector = UpgradeDetector::GetInstance();
+  UMA_HISTOGRAM_ENUMERATION(
+      "UpgradeDetector.NotificationStage",
+      upgrade_detector->upgrade_notification_stage(),
+      UpgradeDetector::kUpgradeNotificationAnnoyanceLevelCount);
+}
diff --git a/chrome/browser/metrics/upgrade_metrics_provider.h b/chrome/browser/metrics/upgrade_metrics_provider.h
new file mode 100644
index 0000000..42f85c8
--- /dev/null
+++ b/chrome/browser/metrics/upgrade_metrics_provider.h
@@ -0,0 +1,27 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_METRICS_UPGRADE_METRICS_PROVIDER_H_
+#define CHROME_BROWSER_METRICS_UPGRADE_METRICS_PROVIDER_H_
+
+#include "base/macros.h"
+#include "components/metrics/metrics_provider.h"
+
+// UpgradeMetricsProvider groups various constants and functions used for
+// reporting extension IDs with UMA reports (after hashing the extension IDs
+// for privacy).
+class UpgradeMetricsProvider : public metrics::MetricsProvider {
+ public:
+  UpgradeMetricsProvider();
+  ~UpgradeMetricsProvider() override;
+
+  // metrics::MetricsProvider:
+  void ProvideCurrentSessionData(
+      metrics::ChromeUserMetricsExtension* uma_proto) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UpgradeMetricsProvider);
+};
+
+#endif  // CHROME_BROWSER_METRICS_UPGRADE_METRICS_PROVIDER_H_
diff --git a/chrome/browser/metrics/upgrade_metrics_provider_unittest.cc b/chrome/browser/metrics/upgrade_metrics_provider_unittest.cc
new file mode 100644
index 0000000..d5667f1b
--- /dev/null
+++ b/chrome/browser/metrics/upgrade_metrics_provider_unittest.cc
@@ -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.
+
+#include "chrome/browser/metrics/upgrade_metrics_provider.h"
+
+#include "base/test/histogram_tester.h"
+#include "base/test/scoped_task_environment.h"
+#include "chrome/browser/upgrade_detector.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class UpgradeMetricsProviderTest : public testing::Test {
+ public:
+  UpgradeMetricsProviderTest() {}
+
+  void TestHistogramLevel(
+      UpgradeDetector::UpgradeNotificationAnnoyanceLevel level) {
+    UpgradeDetector::GetInstance()->set_upgrade_notification_stage(level);
+    base::HistogramTester histogram_tester;
+    metrics_provider_.ProvideCurrentSessionData(nullptr);
+    histogram_tester.ExpectUniqueSample("UpgradeDetector.NotificationStage",
+                                        level, 1);
+  }
+
+ private:
+  UpgradeMetricsProvider metrics_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpgradeMetricsProviderTest);
+};
+
+TEST_F(UpgradeMetricsProviderTest, HistogramCheck) {
+  base::test::ScopedTaskEnvironment task_environment;
+  TestHistogramLevel(UpgradeDetector::UPGRADE_ANNOYANCE_NONE);
+  TestHistogramLevel(UpgradeDetector::UPGRADE_ANNOYANCE_LOW);
+  TestHistogramLevel(UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED);
+  TestHistogramLevel(UpgradeDetector::UPGRADE_ANNOYANCE_HIGH);
+  TestHistogramLevel(UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL);
+}
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 942ab119..bda612c5 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -68,6 +68,7 @@
 #include "net/base/url_util.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "third_party/re2/src/re2/re2.h"
+#include "url/url_constants.h"
 
 #if defined(SAFE_BROWSING_DB_LOCAL)
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
@@ -210,6 +211,14 @@
     // this is effectively their master password.
     is_enabled =
         entry->GetURL().host_piece() != chrome::kChromeUIChromeSigninHost;
+
+    // Per https://crbug.com/756587, exclude existing saved passwords for
+    // about: documents.  Note that this only checks main frames, but this is
+    // sufficient for credentials manager API which is only enabled for main
+    // frames.  Autofill for about: subframes is already disabled by
+    // restricting OnPasswordFormsParsed/OnPasswordFormsRendered.
+    if (entry->GetURL().SchemeIs(url::kAboutScheme))
+      is_enabled = false;
   }
 
   // The password manager is disabled while VR (virtual reality) is being used,
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index c7504e2..dd42e8cb 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -46,6 +46,7 @@
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/url_constants.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/constants.h"
@@ -442,6 +443,16 @@
   EXPECT_FALSE(client->IsSavingAndFillingEnabledForCurrentPage());
 }
 
+// Check that password manager is disabled on about:blank pages.
+// See https://crbug.com/756587.
+TEST_F(ChromePasswordManagerClientTest, SavingAndFillingDisbledForAboutBlank) {
+  GURL kUrl(url::kAboutBlankURL);
+  NavigateAndCommit(kUrl);
+  EXPECT_EQ(kUrl, GetClient()->GetLastCommittedEntryURL());
+  EXPECT_FALSE(GetClient()->IsSavingAndFillingEnabledForCurrentPage());
+  EXPECT_FALSE(GetClient()->IsFillingEnabledForCurrentPage());
+}
+
 TEST_F(ChromePasswordManagerClientTest, GetLastCommittedEntryURL_Empty) {
   EXPECT_EQ(GURL::EmptyGURL(), GetClient()->GetLastCommittedEntryURL());
 }
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 7fd8aab..af19e75 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -20,6 +20,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/metrics/subprocess_metrics_provider.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_manager_test_base.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
@@ -48,6 +49,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_switches.h"
@@ -185,6 +187,43 @@
   EXPECT_FALSE(BubbleObserver(web_contents).IsSavePromptShownAutomatically());
 }
 
+// Generate HTML for a simple password form with the specified action URL.
+std::string GeneratePasswordFormForAction(const GURL& action_url) {
+  return "<form method='POST' action='" + action_url.spec() + "'"
+         "      onsubmit='return true;' id='testform'>"
+         "  <input type='password' id='password_field'>"
+         "</form>";
+}
+
+// Inject an about:blank frame with a password form that uses the specified
+// action URL into |web_contents|.
+void InjectBlankFrameWithPasswordForm(content::WebContents* web_contents,
+                                      const GURL& action_url) {
+  std::string form_html = GeneratePasswordFormForAction(action_url);
+  std::string inject_blank_frame_with_password_form =
+      "var frame = document.createElement('iframe');"
+      "frame.id = 'iframe';"
+      "document.body.appendChild(frame);"
+      "frame.contentDocument.body.innerHTML = \"" + form_html + "\"";
+  ASSERT_TRUE(content::ExecuteScript(web_contents,
+                                     inject_blank_frame_with_password_form));
+}
+
+// Fills in a fake password and submits the form in |frame|, waiting for the
+// submit navigation to finish.  |action_url| is the form action URL to wait
+// for.
+void SubmitInjectedPasswordForm(content::WebContents* web_contents,
+                                content::RenderFrameHost* frame,
+                                const GURL& action_url) {
+  std::string submit_form =
+      "document.getElementById('password_field').value = 'pa55w0rd';"
+      "document.getElementById('testform').submit();";
+  NavigationObserver observer(web_contents);
+  observer.SetPathToWaitFor(action_url.path());
+  ASSERT_TRUE(content::ExecuteScript(frame, submit_form));
+  observer.Wait();
+}
+
 }  // namespace
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ObservingAutofillClient);
@@ -3407,4 +3446,185 @@
   RunDialog();
 }
 
+// Verify that password manager ignores passwords on forms injected into
+// about:blank frames.  See https://crbug.com/756587.
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
+                       AboutBlankFramesAreIgnored) {
+  // Start from a page without a password form.
+  NavigateToFile("/password/other.html");
+
+  // Add a blank iframe and then inject a password form into it.
+  std::unique_ptr<BubbleObserver> prompt_observer(
+      new BubbleObserver(WebContents()));
+  GURL submit_url(embedded_test_server()->GetURL("/password/done.html"));
+  InjectBlankFrameWithPasswordForm(WebContents(), submit_url);
+  content::RenderFrameHost* frame =
+      ChildFrameAt(WebContents()->GetMainFrame(), 0);
+  EXPECT_EQ(GURL(url::kAboutBlankURL), frame->GetLastCommittedURL());
+  EXPECT_TRUE(frame->IsRenderFrameLive());
+  EXPECT_FALSE(prompt_observer->IsSavePromptAvailable());
+
+  // Fill in the password and submit the form.  This shouldn't bring up a save
+  // password prompt and shouldn't result in a renderer kill.
+  base::HistogramTester histogram_tester;
+  SubmitInjectedPasswordForm(WebContents(), frame, submit_url);
+  EXPECT_TRUE(frame->IsRenderFrameLive());
+  EXPECT_EQ(submit_url, frame->GetLastCommittedURL());
+  EXPECT_FALSE(prompt_observer->IsSavePromptAvailable());
+
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.AboutBlankPasswordSubmission",
+      false /* is_main_frame */, 1);
+}
+
+// Verify that password manager ignores passwords on forms injected into
+// about:blank popups.  See https://crbug.com/756587.
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
+                       AboutBlankPopupsAreIgnored) {
+  // Start from a page without a password form.
+  NavigateToFile("/password/other.html");
+
+  // Open an about:blank popup and inject the password form into it.
+  content::WindowedNotificationObserver popup_observer(
+      chrome::NOTIFICATION_TAB_ADDED,
+      content::NotificationService::AllSources());
+  GURL submit_url(embedded_test_server()->GetURL("/password/done.html"));
+  std::string form_html = GeneratePasswordFormForAction(submit_url);
+  std::string open_blank_popup_with_password_form =
+      "var w = window.open('about:blank');"
+      "w.document.body.innerHTML = \"" + form_html + "\";";
+  ASSERT_TRUE(content::ExecuteScript(WebContents(),
+                                     open_blank_popup_with_password_form));
+  popup_observer.Wait();
+  ASSERT_EQ(2, browser()->tab_strip_model()->count());
+  content::WebContents* newtab =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Submit the password form and check that there was no renderer kill and no
+  // prompt to save passwords.
+  base::HistogramTester histogram_tester;
+  std::unique_ptr<BubbleObserver> prompt_observer(new BubbleObserver(newtab));
+  SubmitInjectedPasswordForm(newtab, newtab->GetMainFrame(), submit_url);
+  EXPECT_FALSE(prompt_observer->IsSavePromptAvailable());
+  EXPECT_TRUE(newtab->GetMainFrame()->IsRenderFrameLive());
+  EXPECT_EQ(submit_url, newtab->GetMainFrame()->GetLastCommittedURL());
+
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.AboutBlankPasswordSubmission",
+      true /* is_main_frame */, 1);
+}
+
+// Verify that previously saved passwords for about:blank frames are not used
+// for autofill.  See https://crbug.com/756587.
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
+                       ExistingAboutBlankPasswordsAreNotUsed) {
+  scoped_refptr<password_manager::TestPasswordStore> password_store =
+      static_cast<password_manager::TestPasswordStore*>(
+          PasswordStoreFactory::GetForProfile(
+              browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS).get());
+  autofill::PasswordForm signin_form;
+  signin_form.origin = GURL(url::kAboutBlankURL);
+  signin_form.signon_realm = "about:";
+  GURL submit_url(embedded_test_server()->GetURL("/password/done.html"));
+  signin_form.action = submit_url;
+  signin_form.password_value = base::ASCIIToUTF16("pa55w0rd");
+  password_store->AddLogin(signin_form);
+
+  // Start from a page without a password form.
+  NavigateToFile("/password/other.html");
+
+  // Inject an about:blank frame with password form.
+  InjectBlankFrameWithPasswordForm(WebContents(), submit_url);
+  content::RenderFrameHost* frame =
+      ChildFrameAt(WebContents()->GetMainFrame(), 0);
+  EXPECT_EQ(GURL(url::kAboutBlankURL), frame->GetLastCommittedURL());
+
+  // Simulate user interaction in the iframe which normally triggers
+  // autofill. Click in the middle of the frame to avoid the border.
+  EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
+      RenderFrameHost(),
+      "var iframeRect = "
+      "    document.getElementById('iframe').getBoundingClientRect();"));
+  int x;
+  EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractInt(
+      RenderFrameHost(),
+      "window.domAutomationController.send("
+      "    parseInt((iframeRect.left + iframeRect.right) / 2));",
+      &x));
+  int y;
+  EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractInt(
+      RenderFrameHost(),
+      "window.domAutomationController.send("
+      "    parseInt((iframeRect.top + iframeRect.bottom) / 2));",
+      &y));
+  content::SimulateMouseClickAt(
+      WebContents(), 0, blink::WebMouseEvent::Button::kLeft, gfx::Point(x, y));
+
+  // Verify password is not autofilled.  Blink has a timer for 0.3 seconds
+  // before it updates the browser with the new dynamic form, so wait long
+  // enough for this timer to fire before checking the password.  Note that we
+  // can't wait for any other events here, because when the test passes, there
+  // should be no password manager IPCs sent from the renderer to browser.
+  std::string empty_password;
+  EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      frame,
+      "setTimeout(function() {"
+      "    domAutomationController.send("
+      "        document.getElementById('password_field').value);"
+      "}, 1000);",
+      &empty_password));
+  EXPECT_EQ("", empty_password);
+
+  EXPECT_TRUE(frame->IsRenderFrameLive());
+}
+
+class SitePerProcessPasswordManagerBrowserTest
+    : public PasswordManagerBrowserTestBase {
+ public:
+  SitePerProcessPasswordManagerBrowserTest() {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    content::IsolateAllSitesForTesting(command_line);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SitePerProcessPasswordManagerBrowserTest);
+};
+
+// Verify that there is no renderer kill when filling out a password on a
+// subframe with a data: URL.
+IN_PROC_BROWSER_TEST_F(SitePerProcessPasswordManagerBrowserTest,
+                       NoRendererKillWithDataURLFrames) {
+  // Start from a page without a password form.
+  NavigateToFile("/password/other.html");
+
+  // Add a iframe with a data URL that has a password form.
+  std::unique_ptr<BubbleObserver> prompt_observer(
+      new BubbleObserver(WebContents()));
+  GURL submit_url(embedded_test_server()->GetURL("/password/done.html"));
+  std::string form_html = GeneratePasswordFormForAction(submit_url);
+  std::string inject_data_frame_with_password_form =
+      "var frame = document.createElement('iframe');\n"
+      "frame.src = \"data:text/html," + form_html + "\";\n"
+      "document.body.appendChild(frame);\n";
+  ASSERT_TRUE(content::ExecuteScript(WebContents(),
+                                     inject_data_frame_with_password_form));
+  content::WaitForLoadStop(WebContents());
+  content::RenderFrameHost* frame =
+      ChildFrameAt(WebContents()->GetMainFrame(), 0);
+  EXPECT_TRUE(frame->GetLastCommittedURL().SchemeIs(url::kDataScheme));
+  EXPECT_TRUE(frame->IsRenderFrameLive());
+  EXPECT_FALSE(prompt_observer->IsSavePromptAvailable());
+
+  // Fill in the password and submit the form.  This shouldn't bring up a save
+  // password prompt and shouldn't result in a renderer kill.
+  SubmitInjectedPasswordForm(WebContents(), frame, submit_url);
+  EXPECT_TRUE(frame->IsRenderFrameLive());
+  EXPECT_EQ(submit_url, frame->GetLastCommittedURL());
+  EXPECT_FALSE(prompt_observer->IsSavePromptAvailable());
+}
+
 }  // namespace password_manager
diff --git a/chrome/browser/policy/chrome_browser_policy_connector.cc b/chrome/browser/policy/chrome_browser_policy_connector.cc
index c89ff21d..22af802e 100644
--- a/chrome/browser/policy/chrome_browser_policy_connector.cc
+++ b/chrome/browser/policy/chrome_browser_policy_connector.cc
@@ -11,10 +11,10 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
-#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/path_service.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
 #include "build/build_config.h"
 #include "chrome/browser/policy/configuration_policy_handler_list_factory.h"
 #include "chrome/browser/policy/device_management_service_configuration.h"
@@ -27,7 +27,6 @@
 #include "components/policy/core/common/policy_service.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_constants.h"
-#include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_switches.h"
 #include "net/url_request/url_request_context_getter.h"
 
@@ -43,8 +42,6 @@
 #include "components/policy/core/browser/android/android_combined_policy_provider.h"
 #endif
 
-using content::BrowserThread;
-
 namespace policy {
 
 namespace {
@@ -78,10 +75,6 @@
 void ChromeBrowserPolicyConnector::Init(
     PrefService* local_state,
     scoped_refptr<net::URLRequestContextGetter> request_context) {
-  // Initialization of some of the providers requires the FILE thread; make
-  // sure that threading is ready at this point.
-  DCHECK(BrowserThread::IsThreadInitialized(BrowserThread::FILE));
-
   std::unique_ptr<DeviceManagementService::Configuration> configuration(
       new DeviceManagementServiceConfiguration(
           BrowserPolicyConnector::GetDeviceManagementUrl()));
@@ -97,13 +90,15 @@
 ChromeBrowserPolicyConnector::CreatePlatformProvider() {
 #if defined(OS_WIN)
   std::unique_ptr<AsyncPolicyLoader> loader(PolicyLoaderWin::Create(
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
+      base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BACKGROUND}),
       kRegistryChromePolicyKey));
   return base::MakeUnique<AsyncPolicyProvider>(GetSchemaRegistry(),
                                                std::move(loader));
 #elif defined(OS_MACOSX)
   std::unique_ptr<AsyncPolicyLoader> loader(new PolicyLoaderMac(
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
+      base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BACKGROUND}),
       GetManagedPolicyPath(), new MacPreferences()));
   return base::MakeUnique<AsyncPolicyProvider>(GetSchemaRegistry(),
                                                std::move(loader));
@@ -111,7 +106,8 @@
   base::FilePath config_dir_path;
   if (PathService::Get(chrome::DIR_POLICY_FILES, &config_dir_path)) {
     std::unique_ptr<AsyncPolicyLoader> loader(new ConfigDirPolicyLoader(
-        BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
+        base::CreateSequencedTaskRunnerWithTraits(
+            {base::MayBlock(), base::TaskPriority::BACKGROUND}),
         config_dir_path, POLICY_SCOPE_MACHINE));
     return base::MakeUnique<AsyncPolicyProvider>(GetSchemaRegistry(),
                                                  std::move(loader));
diff --git a/chrome/browser/profiling_host/memlog_browsertest.cc b/chrome/browser/profiling_host/memlog_browsertest.cc
index 1f30d93..73e09eef 100644
--- a/chrome/browser/profiling_host/memlog_browsertest.cc
+++ b/chrome/browser/profiling_host/memlog_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/allocator/features.h"
+#include "base/allocator/partition_allocator/partition_alloc.h"
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "base/threading/thread_restrictions.h"
@@ -39,7 +40,9 @@
 
 void ValidateDump(base::Value* dump_json,
                   int expected_alloc_size,
-                  int expected_alloc_count) {
+                  int expected_alloc_count,
+                  const char* allocator_name,
+                  const char* type_name) {
   // Verify allocation is found.
   // See chrome/profiling/json_exporter.cc for file format info.
   base::Value* events = dump_json->FindKey("traceEvents");
@@ -59,38 +62,63 @@
   base::Value* heaps_v2 = dumps->FindPath({"args", "dumps", "heaps_v2"});
   ASSERT_TRUE(heaps_v2);
 
-  base::Value* sizes = heaps_v2->FindPath({"allocators", "malloc", "sizes"});
+  base::Value* sizes =
+      heaps_v2->FindPath({"allocators", allocator_name, "sizes"});
   ASSERT_TRUE(sizes);
   const base::Value::ListStorage& sizes_list = sizes->GetList();
   EXPECT_FALSE(sizes_list.empty());
 
-  base::Value* counts = heaps_v2->FindPath({"allocators", "malloc", "counts"});
+  base::Value* counts =
+      heaps_v2->FindPath({"allocators", allocator_name, "counts"});
   ASSERT_TRUE(counts);
   const base::Value::ListStorage& counts_list = counts->GetList();
   EXPECT_EQ(sizes_list.size(), counts_list.size());
 
-  // If given an expected allocation to look for, search the sizes and counts
-  // list for them.
-  if (expected_alloc_size) {
-    bool found_browser_alloc = false;
-    for (size_t i = 0; i < sizes_list.size(); i++) {
-      if (counts_list[i].GetInt() == expected_alloc_count &&
-          sizes_list[i].GetInt() != expected_alloc_size) {
-        LOG(WARNING) << "Allocation candidate (size:" << sizes_list[i].GetInt()
-                     << " count:" << counts_list[i].GetInt() << ")";
-      }
-      if (sizes_list[i].GetInt() == expected_alloc_size &&
-          counts_list[i].GetInt() == expected_alloc_count) {
-        found_browser_alloc = true;
+  base::Value* types =
+      heaps_v2->FindPath({"allocators", allocator_name, "types"});
+  ASSERT_TRUE(types);
+  const base::Value::ListStorage& types_list = types->GetList();
+  EXPECT_FALSE(types_list.empty());
+  EXPECT_EQ(sizes_list.size(), types_list.size());
+
+  bool found_browser_alloc = false;
+  size_t browser_alloc_index = 0;
+  for (size_t i = 0; i < sizes_list.size(); i++) {
+    if (counts_list[i].GetInt() == expected_alloc_count &&
+        sizes_list[i].GetInt() != expected_alloc_size) {
+      LOG(WARNING) << "Allocation candidate (size:" << sizes_list[i].GetInt()
+                   << " count:" << counts_list[i].GetInt() << ")";
+    }
+    if (sizes_list[i].GetInt() == expected_alloc_size &&
+        counts_list[i].GetInt() == expected_alloc_count) {
+      browser_alloc_index = i;
+      found_browser_alloc = true;
+      break;
+    }
+  }
+
+  ASSERT_TRUE(found_browser_alloc)
+      << "Failed to find an allocation of the "
+         "appropriate size. Did the send buffer "
+         "not flush? (size: "
+      << expected_alloc_size << " count:" << expected_alloc_count << ")";
+
+  // Find the type, if an expectation was passed in.
+  if (type_name) {
+    bool found = false;
+    int type = types_list[browser_alloc_index].GetInt();
+    base::Value* strings = heaps_v2->FindPath({"maps", "strings"});
+    for (base::Value& dict : strings->GetList()) {
+      // Each dict has the format {"id":1,"string":"kPartitionAllocTypeName"}
+      int id = dict.FindPath({"id"})->GetInt();
+      if (id == type) {
+        found = true;
+        std::string name = dict.FindPath({"string"})->GetString();
+        EXPECT_STREQ(name.c_str(), type_name);
         break;
       }
     }
-
-    ASSERT_TRUE(found_browser_alloc)
-        << "Failed to find an allocation of the "
-           "appropriate size. Did the send buffer "
-           "not flush? (size: "
-        << expected_alloc_size << " count:" << expected_alloc_count << ")";
+    EXPECT_TRUE(found) << "Failed to find type name string.";
   }
 }
 
@@ -178,6 +206,14 @@
   constexpr int kBrowserAllocSize = 103 * 1024;
   constexpr int kBrowserAllocCount = 2048;
 
+  // Test fixed-size partition alloc. The size must be aligned to system pointer
+  // size.
+  constexpr int kPartitionAllocSize = 8 * 23;
+  constexpr int kPartitionAllocCount = 107;
+  static const char* kPartitionAllocTypeName = "kPartitionAllocTypeName";
+  base::PartitionAllocatorGeneric partition_allocator;
+  partition_allocator.init();
+
   // Assuming an average stack size of 20 frames, each alloc packet is ~160
   // bytes in size, and there are 400 packets to fill up a channel. There are 17
   // channels, so ~6800 allocations should flush all channels. But this assumes
@@ -187,11 +223,17 @@
   constexpr int kFlushCount = 68000;
 
   std::vector<char*> leaks;
-  leaks.reserve(kBrowserAllocCount + kFlushCount);
+  leaks.reserve(2 * kBrowserAllocCount + +kPartitionAllocSize + kFlushCount);
   for (int i = 0; i < kBrowserAllocCount; ++i) {
     leaks.push_back(new char[kBrowserAllocSize]);
   }
 
+  for (int i = 0; i < kPartitionAllocCount; ++i) {
+    leaks.push_back(static_cast<char*>(
+        PartitionAllocGeneric(partition_allocator.root(), kPartitionAllocSize,
+                              kPartitionAllocTypeName)));
+  }
+
   size_t total_variadic_allocations = 0;
   for (int i = 0; i < kBrowserAllocCount; ++i) {
     leaks.push_back(new char[i + 1]);  // Variadic allocation.
@@ -221,11 +263,15 @@
     std::unique_ptr<base::Value> dump_json =
         ReadDumpFile(browser_dumpfile_path);
     ASSERT_TRUE(dump_json);
-    ASSERT_NO_FATAL_FAILURE(ValidateDump(dump_json.get(),
-                                         kBrowserAllocSize * kBrowserAllocCount,
-                                         kBrowserAllocCount));
+    ASSERT_NO_FATAL_FAILURE(
+        ValidateDump(dump_json.get(), kBrowserAllocSize * kBrowserAllocCount,
+                     kBrowserAllocCount, "malloc", nullptr));
+    ASSERT_NO_FATAL_FAILURE(
+        ValidateDump(dump_json.get(), total_variadic_allocations,
+                     kBrowserAllocCount, "malloc", nullptr));
     ASSERT_NO_FATAL_FAILURE(ValidateDump(
-        dump_json.get(), total_variadic_allocations, kBrowserAllocCount));
+        dump_json.get(), kPartitionAllocSize * kPartitionAllocCount,
+        kPartitionAllocCount, "partition_alloc", kPartitionAllocTypeName));
   }
 
   {
diff --git a/chrome/browser/resources/chromeos/arc_support/background.js b/chrome/browser/resources/chromeos/arc_support/background.js
index c5c02c6..90084ef 100644
--- a/chrome/browser/resources/chromeos/arc_support/background.js
+++ b/chrome/browser/resources/chromeos/arc_support/background.js
@@ -194,7 +194,7 @@
 
   /** Called when "settings" link is clicked. */
   onSettingsLinkClicked(event) {
-    chrome.browser.openTab({'url': 'chrome://settings'}, function() {});
+    sendNativeMessage('onOpenSettingsPageClicked');
     event.stopPropagation();
   }
 }
diff --git a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
index 112bfc88..4ce626b 100644
--- a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
+++ b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak.js
@@ -312,7 +312,7 @@
       var isLast = (i == nodes.length - 1);
 
       var options = {
-        rate: this.rate_,
+        rate: this.speechRate_,
         'enqueue': true,
         onEvent:
             (function(node, isLast, event) {
@@ -371,9 +371,10 @@
                                 this.voiceNameFromPrefs_ = prefs['voice'];
                               }
                               if (prefs['rate']) {
-                                this.rate_ = parseFloat(prefs['rate']);
+                                this.speechRate_ = parseFloat(prefs['rate']);
                               } else {
-                                chrome.storage.sync.set({'rate': this.rate_});
+                                chrome.storage.sync.set(
+                                    {'rate': this.speechRate_});
                               }
                             }).bind(this));
                       }).bind(this);
diff --git a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak_options.js b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak_options.js
index 1010891..393819f 100644
--- a/chrome/browser/resources/chromeos/select_to_speak/select_to_speak_options.js
+++ b/chrome/browser/resources/chromeos/select_to_speak/select_to_speak_options.js
@@ -17,9 +17,9 @@
   init_: function() {
     this.addTranslatedMessagesToDom_();
     this.populateVoiceList_('voice');
-    window.speechSynthesis.onvoiceschanged = function() {
+    window.speechSynthesis.onvoiceschanged = (function() {
       this.populateVoiceList_('voice');
-    };
+    }.bind(this));
     this.syncSelectControlToPref_('voice', 'voice');
     this.syncSelectControlToPref_('rate', 'rate');
   },
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index 38f14c9..c52bd8c 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -1177,10 +1177,8 @@
   // Checkbox should be showing.
   EXPECT_EQ(VISIBLE, GetVisibility("extended-reporting-opt-in"));
 
-  // TODO(crbug.com/666172): Security indicator should be showing.
-  // Call |ExpectSecurityIndicatorDowngrade(tab, 0u);| here once the bug is
-  // fixed.
-
+  // Security indicator should be showing.
+  ExpectSecurityIndicatorDowngrade(tab, 0u);
   // Check navigation entry state.
   ASSERT_TRUE(controller.GetVisibleEntry());
   EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index 1de01f1..24f28b4 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -92,13 +92,6 @@
 
 namespace {
 
-// Events for UMA. Do not reorder or change!
-enum SSLCertificateDecisionsDidRevoke {
-  USER_CERT_DECISIONS_NOT_REVOKED = 0,
-  USER_CERT_DECISIONS_REVOKED,
-  END_OF_SSL_CERTIFICATE_DECISIONS_DID_REVOKE_ENUM
-};
-
 // The list of content settings types to display on the Page Info UI. THE
 // ORDER OF THESE ITEMS IS IMPORTANT and comes from https://crbug.com/610358. To
 // propose changing it, email security-dev@chromium.org.
@@ -337,13 +330,23 @@
   RecordPageInfoAction(PAGE_INFO_OPENED);
 }
 
-PageInfo::~PageInfo() {}
+PageInfo::~PageInfo() {
+  // Check if Re-enable warnings button was visible, if so, log on UMA whether
+  // it was clicked or not.
+  SSLCertificateDecisionsDidRevoke user_decision =
+      did_revoke_user_ssl_decisions_ ? USER_CERT_DECISIONS_REVOKED
+                                     : USER_CERT_DECISIONS_NOT_REVOKED;
+  if (show_ssl_decision_revoke_button_) {
+    UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.did_user_revoke_decisions2",
+                              user_decision,
+                              END_OF_SSL_CERTIFICATE_DECISIONS_DID_REVOKE_ENUM);
+  }
+}
 
 void PageInfo::RecordPageInfoAction(PageInfoAction action) {
   UMA_HISTOGRAM_ENUMERATION("WebsiteSettings.Action", action, PAGE_INFO_COUNT);
 
   std::string histogram_name;
-
   if (site_url_.SchemeIsCryptographic()) {
     if (security_level_ == security_state::SECURE ||
         security_level_ == security_state::EV_SECURE) {
@@ -425,7 +428,6 @@
   ChooserContextBase* context = ui_info.get_context(profile_);
   const GURL origin = site_url_.GetOrigin();
   context->RevokeObjectPermission(origin, origin, object);
-
   show_info_bar_ = true;
 
   // Refresh the UI to reflect the changed settings.
@@ -446,14 +448,6 @@
     if (infobar_service)
       PageInfoInfoBarDelegate::Create(infobar_service);
   }
-
-  SSLCertificateDecisionsDidRevoke user_decision =
-      did_revoke_user_ssl_decisions_ ? USER_CERT_DECISIONS_REVOKED
-                                     : USER_CERT_DECISIONS_NOT_REVOKED;
-
-  UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.did_user_revoke_decisions",
-                            user_decision,
-                            END_OF_SSL_CERTIFICATE_DECISIONS_DID_REVOKE_ENUM);
 #endif
 }
 
diff --git a/chrome/browser/ui/page_info/page_info.h b/chrome/browser/ui/page_info/page_info.h
index b0aedb40..7f922548 100644
--- a/chrome/browser/ui/page_info/page_info.h
+++ b/chrome/browser/ui/page_info/page_info.h
@@ -92,6 +92,14 @@
     SITE_IDENTITY_STATUS_PASSWORD_REUSE,
   };
 
+  // Events for UMA. Do not reorder or change! Exposed in header so enum is
+  // accessible from test.
+  enum SSLCertificateDecisionsDidRevoke {
+    USER_CERT_DECISIONS_NOT_REVOKED = 0,
+    USER_CERT_DECISIONS_REVOKED = 1,
+    END_OF_SSL_CERTIFICATE_DECISIONS_DID_REVOKE_ENUM
+  };
+
   // UMA statistics for PageInfo. Do not reorder or remove existing
   // fields. A Java counterpart will be generated for this enum.
   // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.page_info
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index f1c3d28..8d0411f 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -29,6 +29,7 @@
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/infobars/core/infobar.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
+#include "content/public/browser/ssl_host_state_delegate.h"
 #include "content/public/browser/ssl_status.h"
 #include "content/public/common/content_switches.h"
 #include "device/base/mock_device_client.h"
@@ -803,6 +804,71 @@
 }
 #endif
 
+// Tests that metrics for the "Re-Enable Warnings" button on PageInfo are being
+// logged correctly.
+TEST_F(PageInfoTest, ReEnableWarningsMetrics) {
+  struct TestCase {
+    const std::string url;
+    const bool button_visible;
+    const bool button_clicked;
+  };
+
+  const TestCase kTestCases[] = {
+      {"https://example.test", false, false},
+      {"https://example.test", true, false},
+      {"https://example.test", true, true},
+  };
+  const char kGenericHistogram[] =
+      "interstitial.ssl.did_user_revoke_decisions2";
+  for (const auto& test : kTestCases) {
+    base::HistogramTester histograms;
+    ResetMockUI();
+    SetURL(test.url);
+    if (test.button_visible) {
+      // In the case where the button should be visible, add an exception to
+      // the profile settings for the site (since the exception is what
+      // will make the button visible).
+      HostContentSettingsMap* content_settings =
+          HostContentSettingsMapFactory::GetForProfile(profile());
+      std::unique_ptr<base::DictionaryValue> dict =
+          std::unique_ptr<base::DictionaryValue>(new base::DictionaryValue());
+      dict->SetKey(
+          "testkey",
+          base::Value(content::SSLHostStateDelegate::CertJudgment::ALLOWED));
+      content_settings->SetWebsiteSettingDefaultScope(
+          url(), GURL(), CONTENT_SETTINGS_TYPE_SSL_CERT_DECISIONS,
+          std::string(), std::move(dict));
+      page_info();
+      if (test.button_clicked) {
+        page_info()->OnRevokeSSLErrorBypassButtonPressed();
+        ClearPageInfo();
+        histograms.ExpectTotalCount(kGenericHistogram, 1);
+        histograms.ExpectBucketCount(
+            kGenericHistogram,
+            PageInfo::SSLCertificateDecisionsDidRevoke::
+                USER_CERT_DECISIONS_REVOKED,
+            1);
+      } else {  // Case where button is visible but not clicked.
+        ClearPageInfo();
+        histograms.ExpectTotalCount(kGenericHistogram, 1);
+        histograms.ExpectBucketCount(
+            kGenericHistogram,
+            PageInfo::SSLCertificateDecisionsDidRevoke::
+                USER_CERT_DECISIONS_NOT_REVOKED,
+            1);
+      }
+    } else {
+      page_info();
+      ClearPageInfo();
+      // Button is not visible, so check histogram is empty after opening and
+      // closing page info.
+      histograms.ExpectTotalCount(kGenericHistogram, 0);
+    }
+  }
+  // Test class expects PageInfo to exist during Teardown.
+  page_info();
+}
+
 // Tests that metrics are recorded on a PageInfo for pages with
 // various security levels.
 TEST_F(PageInfoTest, SecurityLevelMetrics) {
diff --git a/chrome/browser/ui/views/frame/top_container_view.cc b/chrome/browser/ui/views/frame/top_container_view.cc
index 686c73a..188132c 100644
--- a/chrome/browser/ui/views/frame/top_container_view.cc
+++ b/chrome/browser/ui/views/frame/top_container_view.cc
@@ -28,8 +28,16 @@
     // invalidation info, as we're painting something outside of the normal
     // parent-child relationship, so invalidations are no longer in the correct
     // space to compare.
+    ui::PaintContext context(paint_info.context(),
+                             ui::PaintContext::CLONE_WITHOUT_INVALIDATION);
+
+    // Since TopContainerView is not an ancestor of BrowserFrameView, it is not
+    // responsible for its painting. To call paint on BrowserFrameView, we need
+    // to generate a new PaintInfo that shares a DisplayItemList with
+    // TopContainerView.
     browser_view_->frame()->GetFrameView()->Paint(
-        views::PaintInfo::ClonePaintInfo(paint_info));
+        views::PaintInfo::CreateRootPaintInfo(
+            context, browser_view_->frame()->GetFrameView()->size()));
   }
   View::PaintChildren(paint_info);
 }
diff --git a/chrome/browser/upgrade_detector.h b/chrome/browser/upgrade_detector.h
index 0e73ebc..f814d3c 100644
--- a/chrome/browser/upgrade_detector.h
+++ b/chrome/browser/upgrade_detector.h
@@ -29,6 +29,8 @@
 class UpgradeDetector {
  public:
   // The Homeland Security Upgrade Advisory System.
+  // These values are logged in a histogram and shouldn't be renumbered or
+  // removed.
   enum UpgradeNotificationAnnoyanceLevel {
     UPGRADE_ANNOYANCE_NONE = 0,  // What? Me worry?
     UPGRADE_ANNOYANCE_LOW,       // Green.
@@ -36,8 +38,13 @@
     UPGRADE_ANNOYANCE_HIGH,      // Red.
     UPGRADE_ANNOYANCE_SEVERE,    // Orange.
     UPGRADE_ANNOYANCE_CRITICAL,  // Red exclamation mark.
+    UPGRADE_ANNOYANCE_LAST = UPGRADE_ANNOYANCE_CRITICAL  // The last value
   };
 
+  // The number of UpgradeNotificationAnnoyanceLevel enum values.
+  static constexpr int kUpgradeNotificationAnnoyanceLevelCount =
+      UPGRADE_ANNOYANCE_LAST + 1;
+
   // Returns the singleton implementation instance.
   static UpgradeDetector* GetInstance();
 
@@ -166,6 +173,7 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(AppMenuModelTest, Basics);
   FRIEND_TEST_ALL_PREFIXES(SystemTrayClientTest, UpdateTrayIcon);
+  friend class UpgradeMetricsProviderTest;
 
   // Initiates an Idle check. See IdleCallback below.
   void CheckIdle();
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index f2f35c7c..8ec1de0 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -774,7 +774,7 @@
   },
   "webNavigation": {
     "dependencies": ["permission:webNavigation"],
-    "contexts": ["blessed_extension", "extension_service_worker"]
+    "contexts": ["blessed_extension"]
   },
   "webrtcAudioPrivate": {
     "dependencies": ["permission:webrtcAudioPrivate"],
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 5725e3c..312f7fdd 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -612,16 +612,13 @@
         "../browser/ui/views/menu_view_drag_and_drop_test.cc",
         "../browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc",
         "../browser/ui/views/status_icons/status_tray_state_changer_interactive_uitest_win.cc",
-        "//ui/views/corewm/desktop_capture_controller_unittest.cc",
-        "//ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc",
-        "//ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc",
-        "//ui/views/widget/widget_interactive_uitest.cc",
         "base/view_event_test_base.cc",
         "base/view_event_test_base.h",
       ]
       deps += [
         "//ui/views",
         "//ui/views:test_support",
+        "//ui/views:views_interactive_ui_tests",
         "//ui/views/controls/webview:test_support",
       ]
       if (!is_mac || mac_views_browser) {
@@ -670,16 +667,6 @@
           }
         }
       }
-      if (!use_x11) {
-        sources -= [
-          "//ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc",
-          "//ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc",
-        ]
-      }
-      if (use_aura) {
-        sources +=
-            [ "//ui/views/widget/native_widget_aura_interactive_uitest.cc" ]
-      }
       if (use_aura || is_mac) {
         deps += [ "//ui/touch_selection" ]
       }
@@ -800,10 +787,6 @@
       libs = [ "oleacc.lib" ]
     }
 
-    if (toolkit_views && is_win) {
-      sources += [ "//ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc" ]
-    }
-
     if (is_mac) {
       data_deps += [
         "//chrome",
@@ -865,10 +848,6 @@
         "../browser/notifications/platform_notification_service_interactive_uitest.cc",
       ]
     }
-
-    if (!use_aura || is_chromeos) {
-      sources -= [ "//ui/views/corewm/desktop_capture_controller_unittest.cc" ]
-    }
   }
 
   if (enable_app_list && use_ash) {
@@ -4878,6 +4857,7 @@
     sources += [
       "../browser/media/webrtc/native_desktop_media_list_unittest.cc",
       "../browser/metrics/desktop_session_duration/desktop_session_duration_tracker_unittest.cc",
+      "../browser/metrics/upgrade_metrics_provider_unittest.cc",
       "../browser/signin/force_signin_verifier_unittest.cc",
       "../browser/signin/signin_global_error_unittest.cc",
       "../browser/signin/signin_util_unittest.cc",
diff --git a/chrome/test/base/chrome_unit_test_suite.cc b/chrome/test/base/chrome_unit_test_suite.cc
index e02999a..00db1bb 100644
--- a/chrome/test/base/chrome_unit_test_suite.cc
+++ b/chrome/test/base/chrome_unit_test_suite.cc
@@ -4,8 +4,9 @@
 
 #include "chrome/test/base/chrome_unit_test_suite.h"
 
+#include <memory>
+
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
 #include "base/path_service.h"
 #include "base/process/process_handle.h"
 #include "build/build_config.h"
@@ -68,20 +69,6 @@
     content_client_.reset();
     content::SetContentClient(NULL);
 
-    // AsyncPolicyProvider is a lazily created KeyedService that may need to be
-    // shut down here. However, AsyncPolicyProvider::Shutdown() will want to
-    // post tasks to delete its policy loaders. This goes through
-    // BrowserThreadTaskRunner::PostNonNestableDelayedTask(), which can invoke
-    // LazyInstance<BrowserThreadGlobals>::Get() and try to create it for the
-    // first time. It might be created during the test, but it might not (see
-    // comments in TestingBrowserProcess::browser_policy_connector()). Since
-    // creating BrowserThreadGlobals requires creating a SequencedWorkerPool,
-    // and that needs a MessageLoop, make sure there is one here so that tests
-    // don't get obscure errors. Tests can also invoke TestingBrowserProcess::
-    // DeleteInstance() themselves (after ensuring any TestingProfile instances
-    // are deleted). But they shouldn't have to worry about that.
-    DCHECK(!base::MessageLoop::current());
-    base::MessageLoopForUI message_loop;
     TestingBrowserProcess::DeleteInstance();
   }
 
diff --git a/chrome/test/data/extensions/api_test/automation/sites/tree_change_indirect.html b/chrome/test/data/extensions/api_test/automation/sites/tree_change_indirect.html
new file mode 100644
index 0000000..59c6bb5
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/sites/tree_change_indirect.html
@@ -0,0 +1,40 @@
+<!--
+ * Copyright 2017 The Chromium Authors. All rights reserved.  Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+<!--
+ Test to ensure that labels and descriptions that come from elsewhere in the
+ tree are updated when the related content changes.
+-->
+<html>
+<head>
+  <title>Automation Tests - Tree change Indirect</title>
+</head>
+<body>
+<div>
+  <input type="checkbox" id="input">
+  <label id="input-name" for="input">cats</label>
+</div>
+
+<div id="main" role="main" aria-labelledby="main-name">
+</div>
+<p id="main-name">apples</p>
+
+<div id="group" role="group" aria-describedby="group-desc">
+  Group
+</div>
+<p id="group-desc">butter</p>
+
+<button id="change">Change</button>
+
+<script>
+  document.getElementById('change').addEventListener('click', function() {
+    document.getElementById('input-name').innerHTML = 'dogs';
+    document.getElementById('main-name').innerHTML = 'oranges';
+    document.getElementById('group-desc').innerHTML = 'margarine';
+  });
+</script>
+
+</body>
+</html>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change_indirect.html b/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change_indirect.html
new file mode 100644
index 0000000..c775412
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change_indirect.html
@@ -0,0 +1,7 @@
+<!--
+ * 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.
+-->
+<script src="common.js"></script>
+<script src="tree_change_indirect.js"></script>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change_indirect.js b/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change_indirect.js
new file mode 100644
index 0000000..27ab2f2
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/tests/tabs/tree_change_indirect.js
@@ -0,0 +1,49 @@
+// 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.
+
+// Test to ensure that labels and descriptions that come from elsewhere in the
+// tree are updated when the related content changes.
+
+// TODO(aleventhal) why are neither of these working?
+//function findById(id) {
+  //return rootNode.domQuerySelector("#" + id);
+  //return rootNode.find({ htmlAttributes: { id }});
+//}
+
+var allTests = [
+  function testUpdateRelatedNamesAndDescriptions() {
+    var cats = rootNode.find({ role: "checkBox"});
+    var apples = rootNode.find({ role: "main" } );
+    var butter = rootNode.find({ role: "group" } );
+    // TODO(aleventhal) why are we getting the wrong objects for these?
+    // var cats = findById("input");
+    // var apples = findById("main");
+    // var butter = findById("group");
+    assertEq("cats", cats.name);
+    assertEq("apples", apples.name);
+    assertEq("butter", butter.description);
+
+    chrome.automation.addTreeChangeObserver("allTreeChanges", function(change) {
+      if (change.type == "textChanged") {
+        // TODO(aleventhal) Why is timeout necessary to avoid uncaught exception
+        // "Error in event handler for automationInternal.onTreeChange:
+        // ReferenceError: treeChange is not defined" ?
+        setTimeout(function() {
+          var dogs = rootNode.find({ role: "checkBox"});
+          var oranges = rootNode.find({ role: "main" } );
+          var margarine = rootNode.find({ role: "group"} );
+          assertEq("dogs", dogs.name);
+          assertEq("oranges", oranges.name);
+          assertEq("margarine", margarine.description);
+          chrome.test.succeed();
+        }, 0);
+      }
+    });
+
+    var button = rootNode.find({ attributes: { name: "Change" }});
+    button.doDefault();
+  },
+];
+
+setUpAndRunTests(allTests, "tree_change_indirect.html");
diff --git a/chrome/test/data/extensions/api_test/service_worker/filtered_events/a.html b/chrome/test/data/extensions/api_test/service_worker/filtered_events/a.html
deleted file mode 100644
index 34202f6..0000000
--- a/chrome/test/data/extensions/api_test/service_worker/filtered_events/a.html
+++ /dev/null
@@ -1,7 +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.
--->
-<!DOCTYPE html>
-<script src="a.js"></script>
diff --git a/chrome/test/data/extensions/api_test/service_worker/filtered_events/a.js b/chrome/test/data/extensions/api_test/service_worker/filtered_events/a.js
deleted file mode 100644
index 0800553..0000000
--- a/chrome/test/data/extensions/api_test/service_worker/filtered_events/a.js
+++ /dev/null
@@ -1,7 +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.
-
-onload = function() {
-  document.location = 'b.html';
-}
diff --git a/chrome/test/data/extensions/api_test/service_worker/filtered_events/b.html b/chrome/test/data/extensions/api_test/service_worker/filtered_events/b.html
deleted file mode 100644
index b469b6d..0000000
--- a/chrome/test/data/extensions/api_test/service_worker/filtered_events/b.html
+++ /dev/null
@@ -1,7 +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.
--->
-<!DOCTYPE html>
-<html></html>
diff --git a/chrome/test/data/extensions/api_test/service_worker/filtered_events/manifest.json b/chrome/test/data/extensions/api_test/service_worker/filtered_events/manifest.json
deleted file mode 100644
index 63fb4075..0000000
--- a/chrome/test/data/extensions/api_test/service_worker/filtered_events/manifest.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "name": "Extension service worker - filtered events",
-  "version": "1.0",
-  "manifest_version": 2,
-  "description": "Extension service worker - filtered events test with webNavigation",
-  "background": {
-    "scripts": ["test_filtered.js"]
-  },
-  "permissions": ["webNavigation", "tabs"]
-}
diff --git a/chrome/test/data/extensions/api_test/service_worker/filtered_events/sw.js b/chrome/test/data/extensions/api_test/service_worker/filtered_events/sw.js
deleted file mode 100644
index 77b2327d..0000000
--- a/chrome/test/data/extensions/api_test/service_worker/filtered_events/sw.js
+++ /dev/null
@@ -1,32 +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.
-
-self.onmessage = function(e) {
-  chrome.test.assertEq('testServiceWorkerFilteredEvents', e.data);
-  runTest();
-};
-
-function runTest() {
-  var getURL = chrome.extension.getURL;
-  chrome.tabs.create({url: 'about:blank'}, function(tab) {
-    var tabId = tab.id;
-    var aVisited = false;
-    chrome.webNavigation.onCommitted.addListener(function(details) {
-      chrome.test.fail();
-    }, {url: [{pathSuffix: 'never-navigated.html'}]});
-    chrome.webNavigation.onCommitted.addListener(function(details) {
-      chrome.test.log('chrome.webNavigation.onCommitted - a.html');
-      chrome.test.assertEq(getURL('a.html'), details.url);
-      aVisited = true;
-    }, {url: [{pathSuffix: 'a.html'}]});
-    chrome.webNavigation.onCommitted.addListener(function(details) {
-      chrome.test.log('chrome.webNavigation.onCommitted - b.html');
-      chrome.test.assertEq(getURL('b.html'), details.url);
-      chrome.test.assertTrue(aVisited);
-      chrome.test.succeed();
-    }, {url: [{pathSuffix: 'b.html'}]});
-
-    chrome.tabs.update(tabId, {url: getURL('a.html')});
-  });
-}
diff --git a/chrome/test/data/extensions/api_test/service_worker/filtered_events/test_filtered.js b/chrome/test/data/extensions/api_test/service_worker/filtered_events/test_filtered.js
deleted file mode 100644
index f8726ff3..0000000
--- a/chrome/test/data/extensions/api_test/service_worker/filtered_events/test_filtered.js
+++ /dev/null
@@ -1,24 +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.
-
-var workerPromise = new Promise(function(resolve, reject) {
-  navigator.serviceWorker.register('sw.js').then(function() {
-    return navigator.serviceWorker.ready;
-  }).then(function(registration) {
-    var sw = registration.active;
-    sw.postMessage('testServiceWorkerFilteredEvents');
-  }).catch(function(err) {
-    reject(err);
-  });
-});
-
-
-function testServiceWorkerFilteredEvents() {
-  // The worker calls chrome.test.succeed() if the test passes.
-  workerPromise.catch(function(err) {
-    chrome.test.fail();
-  });
-};
-
-chrome.test.runTests([testServiceWorkerFilteredEvents]);
diff --git a/components/autofill/content/common/autofill_types_struct_traits_unittest.cc b/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
index b5d10c08..9427236 100644
--- a/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
+++ b/components/autofill/content/common/autofill_types_struct_traits_unittest.cc
@@ -95,9 +95,7 @@
       PasswordForm::GenerationUploadStatus::POSITIVE_SIGNAL_SENT;
   form->display_name = base::ASCIIToUTF16("test display name");
   form->icon_url = GURL("https://foo.com/icon.png");
-  form->federation_origin =
-      url::Origin::UnsafelyCreateOriginWithoutNormalization(
-          "http", "www.google.com", 80, "");
+  form->federation_origin = url::Origin(GURL("http://wwww.google.com"));
   form->skip_zero_click = false;
   form->layout = PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP;
   form->was_parsed_using_autofill_predictions = false;
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 952c6d5..39b17a40 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -1118,9 +1118,13 @@
       render_frame()->GetRenderView()->ElementBoundsInWindow(element));
 }
 
-bool PasswordAutofillAgent::OriginCanAccessPasswordManager(
-    const blink::WebSecurityOrigin& origin) {
-  return origin.CanAccessPasswordManager();
+bool PasswordAutofillAgent::FrameCanAccessPasswordManager() {
+  // about:blank or about:srcdoc frames should not be allowed to use password
+  // manager.  See https://crbug.com/756587.
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+  if (frame->GetDocument().Url().ProtocolIs(url::kAboutScheme))
+    return false;
+  return frame->GetSecurityOrigin().CanAccessPasswordManager();
 }
 
 void PasswordAutofillAgent::OnDynamicFormsSeen() {
@@ -1137,6 +1141,8 @@
   if (!provisionally_saved_form_.IsPasswordValid())
     return;
 
+  DCHECK(FrameCanAccessPasswordManager());
+
   // Prompt to save only if the form is now gone, either invisible or
   // removed from the DOM.
   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
@@ -1196,10 +1202,9 @@
     logger->LogURL(Logger::STRING_SECURITY_ORIGIN,
                    GURL(origin.ToString().Utf8()));
   }
-  if (!OriginCanAccessPasswordManager(origin)) {
-    if (logger) {
+  if (!FrameCanAccessPasswordManager()) {
+    if (logger)
       logger->LogMessage(Logger::STRING_SECURITY_ORIGIN_FAILURE);
-    }
     return;
   }
 
@@ -1343,6 +1348,7 @@
   // for examples of sites that perform login using this technique.
   if (render_frame()->GetWebFrame()->Parent() &&
       provisionally_saved_form_.IsPasswordValid()) {
+    DCHECK(FrameCanAccessPasswordManager());
     provisionally_saved_form_.SetSubmissionIndicatorEvent(
         PasswordForm::SubmissionIndicatorEvent::FRAME_DETACHED);
     GetPasswordManagerDriver()->InPageNavigation(
@@ -1408,11 +1414,24 @@
           PasswordForm::SubmissionIndicatorEvent::HTML_FORM_SUBMISSION;
     }
 
-    // Some observers depend on sending this information now instead of when
-    // the frame starts loading. If there are redirects that cause a new
-    // RenderView to be instantiated (such as redirects to the WebStore)
-    // we will never get to finish the load.
-    GetPasswordManagerDriver()->PasswordFormSubmitted(*submitted_form);
+    if (FrameCanAccessPasswordManager()) {
+      // Some observers depend on sending this information now instead of when
+      // the frame starts loading. If there are redirects that cause a new
+      // RenderView to be instantiated (such as redirects to the WebStore)
+      // we will never get to finish the load.
+      GetPasswordManagerDriver()->PasswordFormSubmitted(*submitted_form);
+    } else {
+      if (logger)
+        logger->LogMessage(Logger::STRING_SECURITY_ORIGIN_FAILURE);
+
+      // Record how often users submit passwords on about:blank frames.
+      if (form.GetDocument().Url().ProtocolIs(url::kAboutScheme)) {
+        bool is_main_frame = !form.GetDocument().GetFrame()->Parent();
+        UMA_HISTOGRAM_BOOLEAN("PasswordManager.AboutBlankPasswordSubmission",
+                              is_main_frame);
+      }
+    }
+
     if (form_element_observer_) {
       form_element_observer_->Disconnect();
       form_element_observer_ = nullptr;
@@ -1437,13 +1456,23 @@
     logger->LogMessage(Logger::STRING_DID_START_PROVISIONAL_LOAD_METHOD);
   }
 
-  const blink::WebLocalFrame* navigated_frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* navigated_frame = render_frame()->GetWebFrame();
   if (navigated_frame->Parent()) {
     if (logger)
       logger->LogMessage(Logger::STRING_FRAME_NOT_MAIN_FRAME);
     return;
   }
 
+  // This is a new navigation, so require a new user gesture before filling in
+  // passwords.
+  gatekeeper_.Reset();
+
+  if (!FrameCanAccessPasswordManager()) {
+    if (logger)
+      logger->LogMessage(Logger::STRING_SECURITY_ORIGIN_FAILURE);
+    return;
+  }
+
   // Bug fix for crbug.com/368690. isProcessingUserGesture() is false when
   // the user is performing actions outside the page (e.g. typed url,
   // history navigation). We don't want to trigger saving in these cases.
@@ -1522,10 +1551,6 @@
         logger->LogMessage(Logger::STRING_PASSWORD_FORM_NOT_FOUND_ON_PAGE);
     }
   }
-
-  // This is a new navigation, so require a new user gesture before filling in
-  // passwords.
-  gatekeeper_.Reset();
 }
 
 // mojom::PasswordAutofillAgent:
@@ -1819,6 +1844,10 @@
     return;
 
   DCHECK(password_form && (!form.IsNull() || !input.IsNull()));
+
+  if (!FrameCanAccessPasswordManager())
+    return;
+
   provisionally_saved_form_.Set(std::move(password_form), form, input);
 
   if (base::FeatureList::IsEnabled(
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index bc5db5db..f34bd69a 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -28,7 +28,6 @@
 namespace blink {
 class WebFormElementObserver;
 class WebInputElement;
-class WebSecurityOrigin;
 }
 
 namespace autofill {
@@ -149,9 +148,10 @@
 
   bool logging_state_active() const { return logging_state_active_; }
 
- protected:
-  virtual bool OriginCanAccessPasswordManager(
-      const blink::WebSecurityOrigin& origin);
+  // Determine whether the current frame is allowed to access the password
+  // manager. For example, frames with about:blank documents or documents with
+  // unique origins aren't allowed access.
+  virtual bool FrameCanAccessPasswordManager();
 
  private:
   class FormElementObserverCallback;
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index a4997ea..22f11c6 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -311,13 +311,9 @@
 }
 
 bool PasswordGenerationAgent::ShouldAnalyzeDocument() {
-  // Make sure that this security origin is allowed to use password manager.
-  // Generating a password that can't be saved is a bad idea.
-  if (!render_frame() || !render_frame()
-                              ->GetWebFrame()
-                              ->GetDocument()
-                              .GetSecurityOrigin()
-                              .CanAccessPasswordManager()) {
+  // Make sure that this frame is allowed to use password manager. Generating a
+  // password that can't be saved is a bad idea.
+  if (!render_frame() || !password_agent_->FrameCanAccessPasswordManager()) {
     LogMessage(Logger::STRING_GENERATION_RENDERER_NO_PASSWORD_MANAGER_ACCESS);
     return false;
   }
diff --git a/components/autofill/content/renderer/test_password_autofill_agent.cc b/components/autofill/content/renderer/test_password_autofill_agent.cc
index 9fc1a4f..560bbbc 100644
--- a/components/autofill/content/renderer/test_password_autofill_agent.cc
+++ b/components/autofill/content/renderer/test_password_autofill_agent.cc
@@ -13,8 +13,7 @@
 
 TestPasswordAutofillAgent::~TestPasswordAutofillAgent() {}
 
-bool TestPasswordAutofillAgent::OriginCanAccessPasswordManager(
-    const blink::WebSecurityOrigin& origin) {
+bool TestPasswordAutofillAgent::FrameCanAccessPasswordManager() {
   return true;
 }
 
diff --git a/components/autofill/content/renderer/test_password_autofill_agent.h b/components/autofill/content/renderer/test_password_autofill_agent.h
index 4e037de8..2f4add1 100644
--- a/components/autofill/content/renderer/test_password_autofill_agent.h
+++ b/components/autofill/content/renderer/test_password_autofill_agent.h
@@ -19,8 +19,7 @@
   // Always returns true. This allows browser tests with "data: " URL scheme to
   // work with the password manager.
   // PasswordAutofillAgent:
-  bool OriginCanAccessPasswordManager(
-      const blink::WebSecurityOrigin& origin) override;
+  bool FrameCanAccessPasswordManager() override;
 };
 
 }  // namespace autofill
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index a61f93d..9a90958 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -332,6 +332,14 @@
 bool ContentPasswordManagerDriver::CheckChildProcessSecurityPolicy(
     const GURL& url,
     BadMessageReason reason) {
+  // Renderer-side logic should prevent any password manager usage for
+  // about:blank frames as well as data URLs.  If that's not the case, kill the
+  // renderer, as it might be exploited.
+  if (url.SchemeIs(url::kAboutScheme) || url.SchemeIs(url::kDataScheme)) {
+    bad_message::ReceivedBadMessage(render_frame_host_->GetProcess(), reason);
+    return false;
+  }
+
   content::ChildProcessSecurityPolicy* policy =
       content::ChildProcessSecurityPolicy::GetInstance();
   if (!policy->CanAccessDataForOrigin(render_frame_host_->GetProcess()->GetID(),
diff --git a/components/safe_browsing/base_ui_manager.cc b/components/safe_browsing/base_ui_manager.cc
index 084dd71..699720a 100644
--- a/components/safe_browsing/base_ui_manager.cc
+++ b/components/safe_browsing/base_ui_manager.cc
@@ -41,31 +41,46 @@
       *threat_type = found->second;
     return true;
   }
-  void RemovePending(const GURL& url) { pending_.erase(url); }
+  void RemovePending(const GURL& url) {
+    DCHECK(pending_.end() != pending_.find(url));
+    if (--pending_[url].second < 1)
+      pending_.erase(url);
+  }
   void Remove(const GURL& url) { map_.erase(url); }
   void Insert(const GURL url, SBThreatType threat_type) {
     if (Contains(url, nullptr))
       return;
     map_[url] = threat_type;
-    RemovePending(url);
+    RemoveAllPending(url);
   }
   bool ContainsPending(const GURL& url, SBThreatType* threat_type) {
     auto found = pending_.find(url);
     if (found == pending_.end())
       return false;
     if (threat_type)
-      *threat_type = found->second;
+      *threat_type = found->second.first;
     return true;
   }
   void InsertPending(const GURL url, SBThreatType threat_type) {
-    if (ContainsPending(url, nullptr))
+    if (pending_.find(url) != pending_.end()) {
+      pending_[url].first = threat_type;
+      pending_[url].second++;
       return;
-    pending_[url] = threat_type;
+    }
+    pending_[url] = {threat_type, 1};
   }
 
+ protected:
+  // Method to remove all the instances of a website in the pending list
+  // disregarding the count. Used when adding a site to the permanent list.
+  void RemoveAllPending(const GURL& url) { pending_.erase(url); }
+
  private:
   std::map<GURL, SBThreatType> map_;
-  std::map<GURL, SBThreatType> pending_;
+  // Keep a count of how many times a site has been added to the pending list
+  // in order to solve a problem where upon reloading an interstitial, a site
+  // would be re-added to and removed from the whitelist in the wrong order.
+  std::map<GURL, std::pair<SBThreatType, int>> pending_;
   DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet);
 };
 
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index d39ef58..383197a 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -302,23 +302,7 @@
 }
 
 STDMETHODIMP BrowserAccessibilityComWin::get_nSelections(LONG* n_selections) {
-  WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTIONS);
-  AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes);
-  if (!owner())
-    return E_FAIL;
-
-  if (!n_selections)
-    return E_INVALIDARG;
-
-  *n_selections = 0;
-  int selection_start, selection_end;
-  GetSelectionOffsets(&selection_start, &selection_end);
-  if (selection_start >= 0 && selection_end >= 0 &&
-      selection_start != selection_end) {
-    *n_selections = 1;
-  }
-
-  return S_OK;
+  return AXPlatformNodeWin::get_nSelections(n_selections);
 }
 
 STDMETHODIMP BrowserAccessibilityComWin::get_selection(LONG selection_index,
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index 2eeb5ea..df09e58b 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -222,16 +222,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
-                       AccessibilityEventsAriaDisabledChanged) {
-  RunEventTest(FILE_PATH_LITERAL("aria-disabled-changed.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
-                       AccessibilityEventsAriaRequiredChanged) {
-  RunEventTest(FILE_PATH_LITERAL("aria-required-changed.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
                        AccessibilityEventsCheckedStateChanged) {
   RunEventTest(FILE_PATH_LITERAL("checked-state-changed.html"));
 }
@@ -252,21 +242,16 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
+                       AccessibilityEventsDescriptionChangeIndirect) {
+  RunEventTest(FILE_PATH_LITERAL("description-change-indirect.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
                        AccessibilityEventsExpandedChange) {
   RunEventTest(FILE_PATH_LITERAL("expanded-change.html"));
 }
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
-                       AccessibilityEventsFormDisabledChanged) {
-  RunEventTest(FILE_PATH_LITERAL("form-disabled-changed.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
-                       AccessibilityEventsFormRequiredChanged) {
-  RunEventTest(FILE_PATH_LITERAL("form-required-changed.html"));
-}
-
-IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
                        AccessibilityEventsInnerHtmlChange) {
   RunEventTest(FILE_PATH_LITERAL("inner-html-change.html"));
 }
@@ -350,6 +335,11 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
+                       AccessibilityEventsNameChangeIndirect) {
+  RunEventTest(FILE_PATH_LITERAL("name-change-indirect.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
                        AccessibilityEventsRemoveChild) {
   RunEventTest(FILE_PATH_LITERAL("remove-child.html"));
 }
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index c33c5be..fd2e3cd1 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -421,6 +421,11 @@
   RunAriaTest(FILE_PATH_LITERAL("aria-describedby.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityAriaDescribedByUpdates) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-describedby-updates.html"));
+}
+
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaDetails) {
   RunAriaTest(FILE_PATH_LITERAL("aria-details.html"));
 }
@@ -534,6 +539,11 @@
   RunAriaTest(FILE_PATH_LITERAL("aria-labelledby-heading.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityAriaLabelledByUpdates) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-labelledby-updates.html"));
+}
+
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaLevel) {
   RunAriaTest(FILE_PATH_LITERAL("aria-level.html"));
 }
@@ -1381,6 +1391,10 @@
   RunHtmlTest(FILE_PATH_LITERAL("label.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityLabelUpdates) {
+  RunHtmlTest(FILE_PATH_LITERAL("label-updates.html"));
+}
+
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityLandmark) {
   RunHtmlTest(FILE_PATH_LITERAL("landmark.html"));
 }
diff --git a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
index 82aa362..efb31eea 100644
--- a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "components/filesystem/public/interfaces/file_system.mojom.h"
@@ -313,8 +314,7 @@
   // Now open many new wrappers (for different origins) to trigger clean up.
   for (int i = 1; i <= 100; ++i) {
     context()->OpenLocalStorage(
-        url::Origin::UnsafelyCreateOriginWithoutNormalization(
-            "http", "example.com", i, ""),
+        url::Origin(GURL(base::StringPrintf("http://example.com:%d", i))),
         MakeRequest(&wrapper));
     wrapper.reset();
   }
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 400e0bbc..e338eb6 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -1049,8 +1049,11 @@
     return;
   }
   std::vector<ServiceWorkerVersionInfo> live_versions = GetAllLiveVersionInfo();
-  base::RepeatingClosure barrier =
-      base::BarrierClosure(live_versions.size(), std::move(callback));
+  base::RepeatingClosure barrier = base::BarrierClosure(
+      live_versions.size(),
+      base::BindOnce(
+          base::IgnoreResult(&base::SingleThreadTaskRunner::PostTask),
+          std::move(task_runner_for_callback), FROM_HERE, std::move(callback)));
   for (const ServiceWorkerVersionInfo& info : live_versions) {
     ServiceWorkerVersion* version = GetLiveVersion(info.version_id);
     DCHECK(version);
diff --git a/content/renderer/input/widget_input_handler_manager.cc b/content/renderer/input/widget_input_handler_manager.cc
index 6d427339..b3a8f956 100644
--- a/content/renderer/input/widget_input_handler_manager.cc
+++ b/content/renderer/input/widget_input_handler_manager.cc
@@ -19,6 +19,7 @@
 #include "third_party/WebKit/public/platform/WebKeyboardEvent.h"
 #include "third_party/WebKit/public/platform/scheduler/renderer/renderer_scheduler.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "ui/events/base_event_utils.h"
 
 namespace content {
 namespace {
@@ -245,6 +246,15 @@
   if (!event || !event->web_event) {
     return;
   }
+
+  // If TimeTicks is not consistent across processes we cannot use the event's
+  // platform timestamp in this process. Instead use the time that the event is
+  // received as the event's timestamp.
+  if (!base::TimeTicks::IsConsistentAcrossProcesses()) {
+    event->web_event->SetTimeStampSeconds(
+        ui::EventTimeStampToSeconds(base::TimeTicks::Now()));
+  }
+
   if (compositor_task_runner_) {
     CHECK(!main_thread_task_runner_->BelongsToCurrentThread());
     input_handler_proxy_->HandleInputEventWithLatencyInfo(
diff --git a/content/test/data/accessibility/aria/aria-describedby-updates-expected-blink.txt b/content/test/data/accessibility/aria/aria-describedby-updates-expected-blink.txt
new file mode 100644
index 0000000..3c68834
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-describedby-updates-expected-blink.txt
@@ -0,0 +1,2 @@
+rootWebArea
+++main description='oranges'
diff --git a/content/test/data/accessibility/aria/aria-describedby-updates.html b/content/test/data/accessibility/aria/aria-describedby-updates.html
new file mode 100644
index 0000000..e90961e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-describedby-updates.html
@@ -0,0 +1,23 @@
+<!--
+@WAIT-FOR:oranges
+@BLINK-ALLOW:description=*
+@WIN-ALLOW:description=*
+@MAC-ALLOW:AXDescription*
+@BLINK-DENY:descriptionFrom*
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <div role="main" aria-describedby="title">
+    <p id="title" aria-hidden="true">apples</p>
+  </div>
+</div>
+
+<script>
+  setTimeout(() => {
+    const label = document.getElementById('title');
+    label.innerHTML = 'oranges';
+  }, 100);
+</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/aria/aria-labelledby-updates-expected-blink.txt b/content/test/data/accessibility/aria/aria-labelledby-updates-expected-blink.txt
new file mode 100644
index 0000000..fed8f49
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-labelledby-updates-expected-blink.txt
@@ -0,0 +1,2 @@
+rootWebArea
+++main name='oranges'
diff --git a/content/test/data/accessibility/aria/aria-labelledby-updates.html b/content/test/data/accessibility/aria/aria-labelledby-updates.html
new file mode 100644
index 0000000..31c85595b
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-labelledby-updates.html
@@ -0,0 +1,20 @@
+<!--
+@WAIT-FOR:oranges
+@BLINK-DENY:nameFrom*
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <div role="main" aria-labelledby="title">
+  </div>
+  <p id="title" aria-hidden="true"><span><span>apples</span></span></p>
+</div>
+
+<script>
+  setTimeout(() => {
+    const label = document.getElementById('title');
+    label.firstElementChild.firstElementChild.innerText = 'oranges';
+  }, 100);
+</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/event/description-change-indirect-expected-win.txt b/content/test/data/accessibility/event/description-change-indirect-expected-win.txt
new file mode 100644
index 0000000..d41a504
--- /dev/null
+++ b/content/test/data/accessibility/event/description-change-indirect-expected-win.txt
@@ -0,0 +1,7 @@
+EVENT_OBJECT_HIDE on role=ROLE_SYSTEM_STATICTEXT name="apples"
+EVENT_OBJECT_NAMECHANGE on role=H1 name="oranges"
+IA2_EVENT_TEXT_REMOVED on role=H1 name="oranges" old_text={'appl' start=0 end=4}
+IA2_EVENT_TEXT_INSERTED on role=H1 name="oranges" new_text={'orang' start=0 end=5}
+EVENT_OBJECT_SHOW on role=ROLE_SYSTEM_STATICTEXT name="oranges"
+EVENT_OBJECT_DESCRIPTIONCHANGE on role=ROLE_SYSTEM_GROUPING
+EVENT_OBJECT_REORDER on role=H1 name="oranges"
diff --git a/content/test/data/accessibility/event/description-change-indirect.html b/content/test/data/accessibility/event/description-change-indirect.html
new file mode 100644
index 0000000..f5888b5
--- /dev/null
+++ b/content/test/data/accessibility/event/description-change-indirect.html
@@ -0,0 +1,22 @@
+<!--
+@BLINK-ALLOW:description=*
+@WIN-ALLOW:description=*
+@MAC-ALLOW:AXDescription*
+@MAC-ALLOW:AXDescription*
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <div role="main" aria-describedby="title">
+  </div>
+  <h1 id="title">apples</h1>
+</div>
+
+<script>
+  function go() {
+    const label = document.getElementById('title');
+    label.innerHTML = 'oranges';
+  }
+</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/event/name-change-indirect-expected-win.txt b/content/test/data/accessibility/event/name-change-indirect-expected-win.txt
new file mode 100644
index 0000000..30bb23749
--- /dev/null
+++ b/content/test/data/accessibility/event/name-change-indirect-expected-win.txt
@@ -0,0 +1,7 @@
+EVENT_OBJECT_HIDE on role=ROLE_SYSTEM_STATICTEXT name="apples"
+EVENT_OBJECT_NAMECHANGE on role=H1 name="oranges"
+IA2_EVENT_TEXT_REMOVED on role=H1 name="oranges" old_text={'appl' start=0 end=4}
+IA2_EVENT_TEXT_INSERTED on role=H1 name="oranges" new_text={'orang' start=0 end=5}
+EVENT_OBJECT_SHOW on role=ROLE_SYSTEM_STATICTEXT name="oranges"
+EVENT_OBJECT_NAMECHANGE on role=ROLE_SYSTEM_GROUPING name="oranges"
+EVENT_OBJECT_REORDER on role=H1 name="oranges"
diff --git a/content/test/data/accessibility/event/name-change-indirect.html b/content/test/data/accessibility/event/name-change-indirect.html
new file mode 100644
index 0000000..7064872
--- /dev/null
+++ b/content/test/data/accessibility/event/name-change-indirect.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<body>
+  <div role="main" aria-labelledby="title">
+    <h1 id="title">apples</h1>
+  </div>
+</div>
+
+<script>
+  function go() {
+    const label = document.getElementById('title');
+    label.innerHTML = 'oranges';
+  }
+</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/html/label-updates-expected-blink.txt b/content/test/data/accessibility/html/label-updates-expected-blink.txt
new file mode 100644
index 0000000..3dd86474
--- /dev/null
+++ b/content/test/data/accessibility/html/label-updates-expected-blink.txt
@@ -0,0 +1,3 @@
+rootWebArea
+++genericContainer
+++++textField name='oranges'
diff --git a/content/test/data/accessibility/html/label-updates.html b/content/test/data/accessibility/html/label-updates.html
new file mode 100644
index 0000000..112e05b
--- /dev/null
+++ b/content/test/data/accessibility/html/label-updates.html
@@ -0,0 +1,19 @@
+<!--
+@WAIT-FOR:oranges
+@BLINK-DENY:nameFrom*
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <input id="input">
+  <label id="label" aria-hidden="true" for="input"><span><span>apples</span></span></label>
+</div>
+
+<script>
+  setTimeout(() => {
+    const label = document.getElementById('label');
+    label.firstElementChild.firstElementChild.innerText = 'oranges';
+  }, 100);
+</script>
+</body>
+</html>
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 1476b42..9084dd6 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -318,9 +318,9 @@
         ['mac', 'nvidia', 'intel'], bug=630800)
 
     # Mac Retina NVIDIA
-    self.Flaky('deqp/functional/gles3/shaderindexing/mat_01.html',
+    self.Fail('deqp/functional/gles3/shaderindexing/mat_01.html',
         ['mac', ('nvidia', 0xfe9)], bug=728271)
-    self.Flaky('deqp/functional/gles3/shaderindexing/tmp.html',
+    self.Fail('deqp/functional/gles3/shaderindexing/tmp.html',
         ['mac', ('nvidia', 0xfe9)], bug=728271)
     self.Fail('deqp/functional/gles3/fbomultisample*',
         ['mac', ('nvidia', 0xfe9)], bug=641209)
diff --git a/device/usb/mojo/device_impl.cc b/device/usb/mojo/device_impl.cc
index e561d80..76a0154 100644
--- a/device/usb/mojo/device_impl.cc
+++ b/device/usb/mojo/device_impl.cc
@@ -300,8 +300,7 @@
                                    uint32_t timeout,
                                    ControlTransferInCallback callback) {
   if (!device_handle_) {
-    std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR,
-                            base::nullopt);
+    std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, {});
     return;
   }
 
@@ -312,8 +311,7 @@
         params->request, params->value, params->index, buffer, length, timeout,
         base::BindOnce(&OnTransferIn, std::move(callback)));
   } else {
-    std::move(callback).Run(mojom::UsbTransferStatus::PERMISSION_DENIED,
-                            base::nullopt);
+    std::move(callback).Run(mojom::UsbTransferStatus::PERMISSION_DENIED, {});
   }
 }
 
@@ -343,8 +341,7 @@
                                    uint32_t timeout,
                                    GenericTransferInCallback callback) {
   if (!device_handle_) {
-    std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR,
-                            base::nullopt);
+    std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, {});
     return;
   }
 
@@ -379,9 +376,8 @@
     IsochronousTransferInCallback callback) {
   if (!device_handle_) {
     std::move(callback).Run(
-        base::nullopt,
-        BuildIsochronousPacketArray(packet_lengths,
-                                    mojom::UsbTransferStatus::TRANSFER_ERROR));
+        {}, BuildIsochronousPacketArray(
+                packet_lengths, mojom::UsbTransferStatus::TRANSFER_ERROR));
     return;
   }
 
diff --git a/device/usb/mojo/device_impl_unittest.cc b/device/usb/mojo/device_impl_unittest.cc
index dd4607c0..5e70a2e 100644
--- a/device/usb/mojo/device_impl_unittest.cc
+++ b/device/usb/mojo/device_impl_unittest.cc
@@ -78,17 +78,15 @@
   continuation.Run();
 }
 
-void ExpectTransferInAndThen(
-    mojom::UsbTransferStatus expected_status,
-    const std::vector<uint8_t>& expected_bytes,
-    const base::Closure& continuation,
-    mojom::UsbTransferStatus actual_status,
-    const base::Optional<std::vector<uint8_t>>& actual_bytes) {
+void ExpectTransferInAndThen(mojom::UsbTransferStatus expected_status,
+                             const std::vector<uint8_t>& expected_bytes,
+                             const base::Closure& continuation,
+                             mojom::UsbTransferStatus actual_status,
+                             const std::vector<uint8_t>& actual_bytes) {
   EXPECT_EQ(expected_status, actual_status);
-  ASSERT_TRUE(actual_bytes);
-  ASSERT_EQ(expected_bytes.size(), actual_bytes->size());
-  for (size_t i = 0; i < actual_bytes->size(); ++i) {
-    EXPECT_EQ(expected_bytes[i], (*actual_bytes)[i])
+  ASSERT_EQ(expected_bytes.size(), actual_bytes.size());
+  for (size_t i = 0; i < actual_bytes.size(); ++i) {
+    EXPECT_EQ(expected_bytes[i], actual_bytes[i])
         << "Contents differ at index: " << i;
   }
   continuation.Run();
@@ -112,7 +110,7 @@
     const std::vector<uint8_t>& expected_bytes,
     const std::vector<uint32_t>& expected_packets,
     const base::Closure& continuation,
-    const base::Optional<std::vector<uint8_t>>& actual_bytes,
+    const std::vector<uint8_t>& actual_bytes,
     std::vector<UsbIsochronousPacketPtr> actual_packets) {
   ASSERT_EQ(expected_packets.size(), actual_packets.size());
   for (size_t i = 0; i < expected_packets.size(); ++i) {
@@ -121,10 +119,9 @@
     EXPECT_EQ(mojom::UsbTransferStatus::COMPLETED, actual_packets[i]->status)
         << "Packet at index " << i << " not completed.";
   }
-  ASSERT_TRUE(actual_bytes);
-  ASSERT_EQ(expected_bytes.size(), actual_bytes->size());
-  for (size_t i = 0; i < actual_bytes->size(); ++i) {
-    EXPECT_EQ(expected_bytes[i], (*actual_bytes)[i])
+  ASSERT_EQ(expected_bytes.size(), actual_bytes.size());
+  for (size_t i = 0; i < actual_bytes.size(); ++i) {
+    EXPECT_EQ(expected_bytes[i], actual_bytes[i])
         << "Contents differ at index: " << i;
   }
   continuation.Run();
diff --git a/device/usb/public/interfaces/device.mojom b/device/usb/public/interfaces/device.mojom
index 11ded46..bca50ce72 100644
--- a/device/usb/public/interfaces/device.mojom
+++ b/device/usb/public/interfaces/device.mojom
@@ -180,7 +180,7 @@
   ControlTransferIn(UsbControlTransferParams params,
                     uint32 length,
                     uint32 timeout)
-      => (UsbTransferStatus status, array<uint8>? data);
+      => (UsbTransferStatus status, array<uint8> data);
 
   // Initiates an inbound control transfer request. |params| determine the
   // details of the request. Transfers to recipients other than DEVICE require a
@@ -210,7 +210,7 @@
   // indicates no timeout: the request will remain pending indefinitely until
   // completed or otherwise terminated.
   GenericTransferIn(uint8 endpoint_number, uint32 length, uint32 timeout)
-      => (UsbTransferStatus status, array<uint8>? data);
+      => (UsbTransferStatus status, array<uint8> data);
 
   // Initiates an outbound generic transfer request on a specific endpoint. The
   // interface to which |endpoint_number| belongs must be claimed, and the
@@ -247,7 +247,7 @@
   IsochronousTransferIn(uint8 endpoint_number,
                         array<uint32> packet_lengths,
                         uint32 timeout)
-      => (array<uint8>? data, array<UsbIsochronousPacket> packets);
+      => (array<uint8> data, array<UsbIsochronousPacket> packets);
 
   // Initiates an outbound isochronous transfer request on a specific endpoint.
   // The interface to which |endpoint_number| belongs must be claimed, and the
diff --git a/extensions/browser/event_listener_map.cc b/extensions/browser/event_listener_map.cc
index 84114fb..4a63801 100644
--- a/extensions/browser/event_listener_map.cc
+++ b/extensions/browser/event_listener_map.cc
@@ -90,11 +90,7 @@
 }
 
 void EventListener::MakeLazy() {
-  // A lazy listener neither has a process attached to it nor it has a worker
-  // thread id (if the listener was for a service worker), so reset these values
-  // below to reflect that.
-  if (is_for_service_worker_)
-    worker_thread_id_ = kMainThreadId;
+  DCHECK_EQ(worker_thread_id_, kMainThreadId);
   process_ = nullptr;
 }
 
diff --git a/extensions/browser/event_listener_map.h b/extensions/browser/event_listener_map.h
index 71bc9a6..83f18a7 100644
--- a/extensions/browser/event_listener_map.h
+++ b/extensions/browser/event_listener_map.h
@@ -121,7 +121,7 @@
   // is_for_service_worker_ = true) and the worker is in running state, then
   // this is the worker's thread id in the worker |process_|. For lazy service
   // worker events, this will be kMainThreadId.
-  int worker_thread_id_;
+  const int worker_thread_id_;
 
   std::unique_ptr<base::DictionaryValue> filter_;
   EventFilter::MatcherID matcher_id_;  // -1 if unset.
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc
index 6b8032b..d421ba8 100644
--- a/extensions/browser/event_router.cc
+++ b/extensions/browser/event_router.cc
@@ -324,52 +324,36 @@
                               RegisteredEventType::kServiceWorker);
 }
 
-void EventRouter::AddFilteredEventListener(
-    const std::string& event_name,
-    content::RenderProcessHost* process,
-    const std::string& extension_id,
-    base::Optional<ServiceWorkerIdentifier> sw_identifier,
-    const base::DictionaryValue& filter,
-    bool add_lazy_listener) {
-  const bool is_for_service_worker = sw_identifier.has_value();
-  listeners_.AddListener(
-      is_for_service_worker
-          ? EventListener::ForExtensionServiceWorker(
-                event_name, extension_id, process, sw_identifier->scope,
-                sw_identifier->thread_id, filter.CreateDeepCopy())
-          : EventListener::ForExtension(event_name, extension_id, process,
-                                        filter.CreateDeepCopy()));
+// TODO(lazyboy): Support filters for extension SW events.
+void EventRouter::AddFilteredEventListener(const std::string& event_name,
+                                           content::RenderProcessHost* process,
+                                           const std::string& extension_id,
+                                           const base::DictionaryValue& filter,
+                                           bool add_lazy_listener) {
+  listeners_.AddListener(EventListener::ForExtension(
+      event_name, extension_id, process,
+      std::unique_ptr<DictionaryValue>(filter.DeepCopy())));
 
   if (!add_lazy_listener)
     return;
 
-  bool added = listeners_.AddListener(
-      is_for_service_worker
-          ? EventListener::ForExtensionServiceWorker(
-                event_name, extension_id, nullptr, sw_identifier->scope,
-                kMainThreadId,  // Lazy, without worker thread id.
-                filter.CreateDeepCopy())
-          : EventListener::ForExtension(event_name, extension_id, nullptr,
-                                        filter.CreateDeepCopy()));
+  bool added = listeners_.AddListener(EventListener::ForExtension(
+      event_name, extension_id, nullptr,
+      std::unique_ptr<DictionaryValue>(filter.DeepCopy())));
   if (added)
     AddFilterToEvent(event_name, extension_id, &filter);
 }
 
+// TODO(lazyboy): Support filters for extension SW events.
 void EventRouter::RemoveFilteredEventListener(
     const std::string& event_name,
     content::RenderProcessHost* process,
     const std::string& extension_id,
-    base::Optional<ServiceWorkerIdentifier> sw_identifier,
     const base::DictionaryValue& filter,
     bool remove_lazy_listener) {
-  const bool is_for_service_worker = sw_identifier.has_value();
-  std::unique_ptr<EventListener> listener =
-      is_for_service_worker
-          ? EventListener::ForExtensionServiceWorker(
-                event_name, extension_id, process, sw_identifier->scope,
-                sw_identifier->thread_id, filter.CreateDeepCopy())
-          : EventListener::ForExtension(event_name, extension_id, process,
-                                        filter.CreateDeepCopy());
+  std::unique_ptr<EventListener> listener = EventListener::ForExtension(
+      event_name, extension_id, process,
+      std::unique_ptr<DictionaryValue>(filter.DeepCopy()));
 
   listeners_.RemoveListener(listener.get());
 
@@ -787,6 +771,7 @@
   std::set<std::string> registered_events =
       GetRegisteredEvents(extension->id(), RegisteredEventType::kLazy);
   listeners_.LoadUnfilteredLazyListeners(extension->id(), registered_events);
+  // TODO(lazyboy): Load extension SW filtered events when they are available.
   const DictionaryValue* filtered_events = GetFilteredEvents(extension->id());
   if (filtered_events)
     listeners_.LoadFilteredLazyListeners(extension->id(), *filtered_events);
diff --git a/extensions/browser/event_router.h b/extensions/browser/event_router.h
index 617954dc..5d7347c 100644
--- a/extensions/browser/event_router.h
+++ b/extensions/browser/event_router.h
@@ -30,7 +30,6 @@
 #include "url/gurl.h"
 
 class GURL;
-struct ServiceWorkerIdentifier;
 
 namespace content {
 class BrowserContext;
@@ -173,23 +172,19 @@
                                             const GURL& service_worker_scope);
 
   // If |add_lazy_listener| is true also add the lazy version of this listener.
-  void AddFilteredEventListener(
-      const std::string& event_name,
-      content::RenderProcessHost* process,
-      const std::string& extension_id,
-      base::Optional<ServiceWorkerIdentifier> sw_identifier,
-      const base::DictionaryValue& filter,
-      bool add_lazy_listener);
+  void AddFilteredEventListener(const std::string& event_name,
+                                content::RenderProcessHost* process,
+                                const std::string& extension_id,
+                                const base::DictionaryValue& filter,
+                                bool add_lazy_listener);
 
   // If |remove_lazy_listener| is true also remove the lazy version of this
   // listener.
-  void RemoveFilteredEventListener(
-      const std::string& event_name,
-      content::RenderProcessHost* process,
-      const std::string& extension_id,
-      base::Optional<ServiceWorkerIdentifier> sw_identifier,
-      const base::DictionaryValue& filter,
-      bool remove_lazy_listener);
+  void RemoveFilteredEventListener(const std::string& event_name,
+                                   content::RenderProcessHost* process,
+                                   const std::string& extension_id,
+                                   const base::DictionaryValue& filter,
+                                   bool remove_lazy_listener);
 
   // Returns true if there is at least one listener for the given event.
   bool HasEventListener(const std::string& event_name) const;
diff --git a/extensions/browser/event_router_unittest.cc b/extensions/browser/event_router_unittest.cc
index 800b87e..01122e68 100644
--- a/extensions/browser/event_router_unittest.cc
+++ b/extensions/browser/event_router_unittest.cc
@@ -20,7 +20,6 @@
 #include "extensions/browser/event_listener_map.h"
 #include "extensions/browser/extensions_test.h"
 #include "extensions/common/extension_builder.h"
-#include "extensions/common/extension_messages.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::DictionaryValue;
@@ -371,8 +370,7 @@
     std::unique_ptr<base::DictionaryValue> filter =
         CreateHostSuffixFilter(kHostSuffixes[i]);
     event_router()->AddFilteredEventListener(kEventName, render_process_host(),
-                                             kExtensionId, base::nullopt,
-                                             *filter, true);
+                                             kExtensionId, *filter, true);
     filters.push_back(std::move(filter));
   }
 
@@ -394,24 +392,21 @@
 
   // Remove the second filter.
   event_router()->RemoveFilteredEventListener(kEventName, render_process_host(),
-                                              kExtensionId, base::nullopt,
-                                              *filters[1], true);
+                                              kExtensionId, *filters[1], true);
   ASSERT_TRUE(ContainsFilter(kExtensionId, kEventName, *filters[0]));
   ASSERT_FALSE(ContainsFilter(kExtensionId, kEventName, *filters[1]));
   ASSERT_TRUE(ContainsFilter(kExtensionId, kEventName, *filters[2]));
 
   // Remove the first filter.
   event_router()->RemoveFilteredEventListener(kEventName, render_process_host(),
-                                              kExtensionId, base::nullopt,
-                                              *filters[0], true);
+                                              kExtensionId, *filters[0], true);
   ASSERT_FALSE(ContainsFilter(kExtensionId, kEventName, *filters[0]));
   ASSERT_FALSE(ContainsFilter(kExtensionId, kEventName, *filters[1]));
   ASSERT_TRUE(ContainsFilter(kExtensionId, kEventName, *filters[2]));
 
   // Remove the third filter.
   event_router()->RemoveFilteredEventListener(kEventName, render_process_host(),
-                                              kExtensionId, base::nullopt,
-                                              *filters[2], true);
+                                              kExtensionId, *filters[2], true);
   ASSERT_FALSE(ContainsFilter(kExtensionId, kEventName, *filters[0]));
   ASSERT_FALSE(ContainsFilter(kExtensionId, kEventName, *filters[1]));
   ASSERT_FALSE(ContainsFilter(kExtensionId, kEventName, *filters[2]));
diff --git a/extensions/browser/extension_message_filter.cc b/extensions/browser/extension_message_filter.cc
index 678d6067..a445136 100644
--- a/extensions/browser/extension_message_filter.cc
+++ b/extensions/browser/extension_message_filter.cc
@@ -273,7 +273,6 @@
 void ExtensionMessageFilter::OnExtensionAddFilteredListener(
     const std::string& extension_id,
     const std::string& event_name,
-    base::Optional<ServiceWorkerIdentifier> sw_identifier,
     const base::DictionaryValue& filter,
     bool lazy) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -285,13 +284,12 @@
     return;
 
   GetEventRouter()->AddFilteredEventListener(event_name, process, extension_id,
-                                             sw_identifier, filter, lazy);
+                                             filter, lazy);
 }
 
 void ExtensionMessageFilter::OnExtensionRemoveFilteredListener(
     const std::string& extension_id,
     const std::string& event_name,
-    base::Optional<ServiceWorkerIdentifier> sw_identifier,
     const base::DictionaryValue& filter,
     bool lazy) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -302,8 +300,8 @@
   if (!process)
     return;
 
-  GetEventRouter()->RemoveFilteredEventListener(
-      event_name, process, extension_id, sw_identifier, filter, lazy);
+  GetEventRouter()->RemoveFilteredEventListener(event_name, process,
+                                                extension_id, filter, lazy);
 }
 
 void ExtensionMessageFilter::OnExtensionShouldSuspendAck(
diff --git a/extensions/browser/extension_message_filter.h b/extensions/browser/extension_message_filter.h
index 33be9ca..003966b 100644
--- a/extensions/browser/extension_message_filter.h
+++ b/extensions/browser/extension_message_filter.h
@@ -17,7 +17,6 @@
 class GURL;
 struct ExtensionMsg_ExternalConnectionInfo;
 struct ExtensionMsg_TabTargetConnectionInfo;
-struct ServiceWorkerIdentifier;
 
 namespace content {
 class BrowserContext;
@@ -77,18 +76,14 @@
       const std::string& extension_id,
       const std::string& event_name,
       const GURL& worker_scope_url);
-  void OnExtensionAddFilteredListener(
-      const std::string& extension_id,
-      const std::string& event_name,
-      base::Optional<ServiceWorkerIdentifier> sw_identifier,
-      const base::DictionaryValue& filter,
-      bool lazy);
-  void OnExtensionRemoveFilteredListener(
-      const std::string& extension_id,
-      const std::string& event_name,
-      base::Optional<ServiceWorkerIdentifier> sw_identifier,
-      const base::DictionaryValue& filter,
-      bool lazy);
+  void OnExtensionAddFilteredListener(const std::string& extension_id,
+                                      const std::string& event_name,
+                                      const base::DictionaryValue& filter,
+                                      bool lazy);
+  void OnExtensionRemoveFilteredListener(const std::string& extension_id,
+                                         const std::string& event_name,
+                                         const base::DictionaryValue& filter,
+                                         bool lazy);
   void OnExtensionShouldSuspendAck(const std::string& extension_id,
                                    int sequence_id);
   void OnExtensionSuspendAck(const std::string& extension_id);
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 820e086..9d33880 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -16,6 +16,8 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
+#include "base/time/clock.h"
+#include "base/time/default_clock.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/crx_file/id_util.h"
@@ -252,20 +254,6 @@
 }  // namespace
 
 //
-// TimeProvider
-//
-
-ExtensionPrefs::TimeProvider::TimeProvider() {
-}
-
-ExtensionPrefs::TimeProvider::~TimeProvider() {
-}
-
-base::Time ExtensionPrefs::TimeProvider::GetCurrentTime() const {
-  return base::Time::Now();
-}
-
-//
 // ScopedDictionaryUpdate
 //
 ExtensionPrefs::ScopedDictionaryUpdate::ScopedDictionaryUpdate(
@@ -335,9 +323,10 @@
     ExtensionPrefValueMap* extension_pref_value_map,
     bool extensions_disabled,
     const std::vector<ExtensionPrefsObserver*>& early_observers) {
-  return ExtensionPrefs::Create(
-      browser_context, prefs, root_dir, extension_pref_value_map,
-      extensions_disabled, early_observers, std::make_unique<TimeProvider>());
+  return ExtensionPrefs::Create(browser_context, prefs, root_dir,
+                                extension_pref_value_map, extensions_disabled,
+                                early_observers,
+                                std::make_unique<base::DefaultClock>());
 }
 
 // static
@@ -348,9 +337,9 @@
     ExtensionPrefValueMap* extension_pref_value_map,
     bool extensions_disabled,
     const std::vector<ExtensionPrefsObserver*>& early_observers,
-    std::unique_ptr<TimeProvider> time_provider) {
+    std::unique_ptr<base::Clock> clock) {
   return new ExtensionPrefs(browser_context, pref_service, root_dir,
-                            extension_pref_value_map, std::move(time_provider),
+                            extension_pref_value_map, std::move(clock),
                             extensions_disabled, early_observers);
 }
 
@@ -1081,7 +1070,7 @@
     const std::string& install_parameter) {
   ScopedExtensionPrefUpdate update(prefs_, extension->id());
   auto extension_dict = update.Get();
-  const base::Time install_time = time_provider_->GetCurrentTime();
+  const base::Time install_time = clock_->Now();
   PopulateExtensionInfoPrefs(extension, install_time, initial_state,
                              install_flags, install_parameter,
                              extension_dict.get());
@@ -1295,8 +1284,8 @@
     const std::string& install_parameter) {
   ScopedDictionaryUpdate update(this, extension->id(), kDelayedInstallInfo);
   auto extension_dict = update.Create();
-  PopulateExtensionInfoPrefs(extension, time_provider_->GetCurrentTime(),
-                             initial_state, install_flags, install_parameter,
+  PopulateExtensionInfoPrefs(extension, clock_->Now(), initial_state,
+                             install_flags, install_parameter,
                              extension_dict.get());
 
   // Add transient data that is needed by FinishDelayedInstallInfo(), but
@@ -1345,7 +1334,7 @@
   }
   pending_install_dict->Remove(kDelayedInstallReason, NULL);
 
-  const base::Time install_time = time_provider_->GetCurrentTime();
+  const base::Time install_time = clock_->Now();
   pending_install_dict->SetString(
       kPrefInstallTime, base::Int64ToString(install_time.ToInternalValue()));
 
@@ -1711,14 +1700,14 @@
     PrefService* prefs,
     const base::FilePath& root_dir,
     ExtensionPrefValueMap* extension_pref_value_map,
-    std::unique_ptr<TimeProvider> time_provider,
+    std::unique_ptr<base::Clock> clock,
     bool extensions_disabled,
     const std::vector<ExtensionPrefsObserver*>& early_observers)
     : browser_context_(browser_context),
       prefs_(prefs),
       install_directory_(root_dir),
       extension_pref_value_map_(extension_pref_value_map),
-      time_provider_(std::move(time_provider)),
+      clock_(std::move(clock)),
       extensions_disabled_(extensions_disabled) {
   MakePathsRelative();
 
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index 9e2977a8..1849bd0 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -31,6 +31,10 @@
 class ExtensionPrefValueMap;
 class PrefService;
 
+namespace base {
+class Clock;
+}
+
 namespace content {
 class BrowserContext;
 }
@@ -82,21 +86,6 @@
     DELAY_REASON_WAIT_FOR_OS_UPDATE = 4,
   };
 
-  // Creates base::Time classes. The default implementation is just to return
-  // the current time, but tests can inject alternative implementations.
-  class TimeProvider {
-   public:
-    TimeProvider();
-
-    virtual ~TimeProvider();
-
-    // By default, returns the current time (base::Time::Now()).
-    virtual base::Time GetCurrentTime() const;
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(TimeProvider);
-  };
-
   // Wrappers around a prefs::ScopedDictionaryPrefUpdate, which allow us to
   // access the entry of a particular key for an extension. Use these if you
   // need a mutable record of a dictionary or list in the current settings.
@@ -169,7 +158,7 @@
       ExtensionPrefValueMap* extension_pref_value_map,
       bool extensions_disabled,
       const std::vector<ExtensionPrefsObserver*>& early_observers,
-      std::unique_ptr<TimeProvider> time_provider);
+      std::unique_ptr<base::Clock> clock);
 
   ~ExtensionPrefs() override;
 
@@ -581,7 +570,7 @@
                  PrefService* prefs,
                  const base::FilePath& root_dir,
                  ExtensionPrefValueMap* extension_pref_value_map,
-                 std::unique_ptr<TimeProvider> time_provider,
+                 std::unique_ptr<base::Clock> clock,
                  bool extensions_disabled,
                  const std::vector<ExtensionPrefsObserver*>& early_observers);
 
@@ -708,7 +697,7 @@
   // Weak pointer, owned by BrowserContext.
   ExtensionPrefValueMap* extension_pref_value_map_;
 
-  std::unique_ptr<TimeProvider> time_provider_;
+  std::unique_ptr<base::Clock> clock_;
 
   bool extensions_disabled_;
 
diff --git a/extensions/browser/guest_view/mime_handler_view/OWNERS b/extensions/browser/guest_view/mime_handler_view/OWNERS
deleted file mode 100644
index 17da63d..0000000
--- a/extensions/browser/guest_view/mime_handler_view/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-raymes@chromium.org
-
-# COMPONENT: Platform>Apps>BrowserTag
diff --git a/extensions/common/constants.cc b/extensions/common/constants.cc
index 779ec3f..ef80d21 100644
--- a/extensions/common/constants.cc
+++ b/extensions/common/constants.cc
@@ -4,8 +4,6 @@
 
 #include "extensions/common/constants.h"
 
-#include "ipc/ipc_message.h"
-
 namespace extensions {
 
 const char kExtensionScheme[] = "chrome-extension";
@@ -88,7 +86,6 @@
     arraysize(kWebstoreSignaturesPublicKey);
 
 const int kMainThreadId = 0;
-const int kIgnoreRoutingId = MSG_ROUTING_NONE;
 
 const char kMimeTypeJpeg[] = "image/jpeg";
 const char kMimeTypePng[] = "image/png";
diff --git a/extensions/common/constants.h b/extensions/common/constants.h
index a583e37..b96eb342 100644
--- a/extensions/common/constants.h
+++ b/extensions/common/constants.h
@@ -122,10 +122,6 @@
 // from a non-service worker context
 extern const int kMainThreadId;
 
-// A routing ID that is used in some contexts to specify that those contexts
-// do not care about a specific routing id.
-extern const int kIgnoreRoutingId;
-
 // Enumeration of possible app launch sources.
 // This should be kept in sync with LaunchSource in
 // extensions/common/api/app_runtime.idl, and GetLaunchSourceEnum() in
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 0591f07..8093a5fb 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -276,13 +276,6 @@
   IPC_STRUCT_TRAITS_MEMBER(window_exposed_by_default)
 IPC_STRUCT_TRAITS_END()
 
-// Identifier containing info about a service worker, used in event listener
-// IPCs.
-IPC_STRUCT_BEGIN(ServiceWorkerIdentifier)
-  IPC_STRUCT_MEMBER(GURL, scope)
-  IPC_STRUCT_MEMBER(int, thread_id)
-IPC_STRUCT_END()
-
 // Singly-included section for custom IPC traits.
 #ifndef EXTENSIONS_COMMON_EXTENSION_MESSAGES_H_
 #define EXTENSIONS_COMMON_EXTENSION_MESSAGES_H_
@@ -731,27 +724,19 @@
 
 // Notify the browser that the given extension added a listener to instances of
 // the named event that satisfy the filter.
-// If |sw_identifier| is specified, it implies that the listener is for a
-// service worker, and the param is used to identify the worker.
-IPC_MESSAGE_CONTROL5(
-    ExtensionHostMsg_AddFilteredListener,
-    std::string /* extension_id */,
-    std::string /* name */,
-    base::Optional<ServiceWorkerIdentifier> /* sw_identifier */,
-    base::DictionaryValue /* filter */,
-    bool /* lazy */)
+IPC_MESSAGE_CONTROL4(ExtensionHostMsg_AddFilteredListener,
+                     std::string /* extension_id */,
+                     std::string /* name */,
+                     base::DictionaryValue /* filter */,
+                     bool /* lazy */)
 
 // Notify the browser that the given extension is no longer interested in
 // instances of the named event that satisfy the filter.
-// If |sw_identifier| is specified, it implies that the listener is for a
-// service worker, and the param is used to identify the worker.
-IPC_MESSAGE_CONTROL5(
-    ExtensionHostMsg_RemoveFilteredListener,
-    std::string /* extension_id */,
-    std::string /* name */,
-    base::Optional<ServiceWorkerIdentifier> /* sw_identifier */,
-    base::DictionaryValue /* filter */,
-    bool /* lazy */)
+IPC_MESSAGE_CONTROL4(ExtensionHostMsg_RemoveFilteredListener,
+                     std::string /* extension_id */,
+                     std::string /* name */,
+                     base::DictionaryValue /* filter */,
+                     bool /* lazy */)
 
 // Notify the browser that an event has finished being dispatched.
 IPC_MESSAGE_ROUTED1(ExtensionHostMsg_EventAck, int /* message_id */)
diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc
index 3e27e64..2fdd788d 100644
--- a/extensions/renderer/event_bindings.cc
+++ b/extensions/renderer/event_bindings.cc
@@ -32,17 +32,6 @@
 
 namespace {
 
-// Returns the routing id to use for matching filtered events.
-// Used for routing events to the correct RenderFrame. This doesn't apply to
-// Extension Service Worker events as there is no RenderFrame to target an event
-// to. This function returns extensions::kIgnoreRoutingId in that case,
-// essentially ignoring routing id for worker events.
-int GetRoutingIDForFilteredEvents(ScriptContext* script_context) {
-  return script_context->context_type() == Feature::SERVICE_WORKER_CONTEXT
-             ? kIgnoreRoutingId
-             : script_context->GetRenderFrame()->GetRoutingID();
-}
-
 // Returns a v8::Array containing the ids of the listeners that match the given
 // |event_filter_dict| in the given |script_context|.
 v8::Local<v8::Array> GetMatchingListeners(ScriptContext* script_context,
@@ -56,7 +45,7 @@
   // have a routingId in their filter.
   std::set<EventFilter::MatcherID> matched_event_filters =
       event_filter.MatchEvent(event_name, info,
-                              GetRoutingIDForFilteredEvents(script_context));
+                              script_context->GetRenderFrame()->GetRoutingID());
   v8::Local<v8::Array> array(
       v8::Array::New(isolate, matched_event_filters.size()));
   int i = 0;
@@ -239,8 +228,8 @@
   EventFilter& event_filter = bookkeeper->event_filter();
   int id = event_filter.AddEventMatcher(
       event_name,
-      std::make_unique<EventMatcher>(std::move(filter),
-                                     GetRoutingIDForFilteredEvents(context())));
+      std::make_unique<EventMatcher>(
+          std::move(filter), context()->GetRenderFrame()->GetRoutingID()));
   if (id == -1) {
     args.GetReturnValue().Set(static_cast<int32_t>(-1));
     return;
diff --git a/extensions/renderer/ipc_message_sender.cc b/extensions/renderer/ipc_message_sender.cc
index c56539d..c105653 100644
--- a/extensions/renderer/ipc_message_sender.cc
+++ b/extensions/renderer/ipc_message_sender.cc
@@ -94,7 +94,7 @@
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     render_thread_->Send(new ExtensionHostMsg_AddFilteredListener(
-        context->GetExtensionID(), event_name, base::nullopt, filter, is_lazy));
+        context->GetExtensionID(), event_name, filter, is_lazy));
   }
 
   void SendRemoveFilteredEventListenerIPC(ScriptContext* context,
@@ -105,8 +105,7 @@
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
     render_thread_->Send(new ExtensionHostMsg_RemoveFilteredListener(
-        context->GetExtensionID(), event_name, base::nullopt, filter,
-        remove_lazy_listener));
+        context->GetExtensionID(), event_name, filter, remove_lazy_listener));
   }
 
  private:
@@ -205,11 +204,7 @@
                                        bool is_lazy) override {
     DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
-    ServiceWorkerIdentifier sw_identifier;
-    sw_identifier.scope = context->service_worker_scope();
-    sw_identifier.thread_id = content::WorkerThread::GetCurrentId(),
-    dispatcher_->Send(new ExtensionHostMsg_AddFilteredListener(
-        context->GetExtensionID(), event_name, sw_identifier, filter, is_lazy));
+    NOTIMPLEMENTED();
   }
 
   void SendRemoveFilteredEventListenerIPC(ScriptContext* context,
@@ -218,12 +213,7 @@
                                           bool remove_lazy_listener) override {
     DCHECK_EQ(Feature::SERVICE_WORKER_CONTEXT, context->context_type());
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
-    ServiceWorkerIdentifier sw_identifier;
-    sw_identifier.scope = context->service_worker_scope();
-    sw_identifier.thread_id = content::WorkerThread::GetCurrentId(),
-    dispatcher_->Send(new ExtensionHostMsg_RemoveFilteredListener(
-        context->GetExtensionID(), event_name, sw_identifier, filter,
-        remove_lazy_listener));
+    NOTIMPLEMENTED();
   }
 
  private:
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 291e4b2..f5de3f4 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -222,7 +222,7 @@
         config.codec() == kCodecMP3 || config.codec() == kCodecAAC ||
         config.codec() == kCodecVorbis || config.codec() == kCodecFLAC;
 
-    stream_.reset(new SourceBufferStream(config, media_log));
+    stream_.reset(new StreamType(config, media_log));
     return true;
   }
 
@@ -237,7 +237,7 @@
 
   if (!stream_) {
     DCHECK_EQ(state_, UNINITIALIZED);
-    stream_.reset(new SourceBufferStream(config, media_log));
+    stream_.reset(new StreamType(config, media_log));
     return true;
   }
 
@@ -250,7 +250,7 @@
   base::AutoLock auto_lock(lock_);
   DCHECK(!stream_);
   DCHECK_EQ(state_, UNINITIALIZED);
-  stream_.reset(new SourceBufferStream(config, media_log));
+  stream_.reset(new StreamType(config, media_log));
 }
 
 void ChunkDemuxerStream::MarkEndOfStream() {
@@ -378,7 +378,7 @@
       return;
     case RETURNING_DATA_FOR_READS:
       switch (stream_->GetNextBuffer(&buffer)) {
-        case SourceBufferStream::kSuccess:
+        case StreamType::kSuccess:
           status = DemuxerStream::kOk;
           DVLOG(2) << __func__ << ": returning kOk, type " << type_ << ", dts "
                    << buffer->GetDecodeTimestamp().InSecondsF() << ", pts "
@@ -386,18 +386,18 @@
                    << buffer->duration().InSecondsF() << ", key "
                    << buffer->is_key_frame();
           break;
-        case SourceBufferStream::kNeedBuffer:
+        case StreamType::kNeedBuffer:
           // Return early without calling |read_cb_| since we don't have
           // any data to return yet.
           DVLOG(2) << __func__ << ": returning kNeedBuffer, type " << type_;
           return;
-        case SourceBufferStream::kEndOfStream:
+        case StreamType::kEndOfStream:
           status = DemuxerStream::kOk;
           buffer = StreamParserBuffer::CreateEOSBuffer();
           DVLOG(2) << __func__ << ": returning kOk with EOS buffer, type "
                    << type_;
           break;
-        case SourceBufferStream::kConfigChange:
+        case StreamType::kConfigChange:
           status = kConfigChanged;
           buffer = NULL;
           DVLOG(2) << __func__ << ": returning kConfigChange, type " << type_;
diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h
index 59027df..6dc4ca1 100644
--- a/media/filters/chunk_demuxer.h
+++ b/media/filters/chunk_demuxer.h
@@ -25,6 +25,7 @@
 #include "media/base/ranges.h"
 #include "media/base/stream_parser.h"
 #include "media/filters/source_buffer_parse_warnings.h"
+#include "media/filters/source_buffer_range_by_dts.h"
 #include "media/filters/source_buffer_state.h"
 #include "media/filters/source_buffer_stream.h"
 
@@ -34,6 +35,10 @@
  public:
   using BufferQueue = base::circular_deque<scoped_refptr<StreamParserBuffer>>;
 
+  // TODO(wolenetz): Vary the buffering API used by the stream. See
+  // https://crbug.com/718641.
+  using StreamType = SourceBufferStream<SourceBufferRangeByDts>;
+
   ChunkDemuxerStream(Type type, MediaTrack::Id media_track_id);
   ~ChunkDemuxerStream() override;
 
@@ -151,7 +156,7 @@
 
   Liveness liveness_;
 
-  std::unique_ptr<SourceBufferStream> stream_;
+  std::unique_ptr<StreamType> stream_;
 
   const MediaTrack::Id media_track_id_;
 
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
index f71528c..ec8ed4f 100644
--- a/media/filters/source_buffer_stream.cc
+++ b/media/filters/source_buffer_stream.cc
@@ -16,7 +16,6 @@
 #include "media/base/media_switches.h"
 #include "media/base/timestamp_constants.h"
 #include "media/filters/source_buffer_platform.h"
-#include "media/filters/source_buffer_range.h"
 #include "media/filters/source_buffer_range_by_dts.h"
 
 namespace media {
@@ -47,7 +46,9 @@
 
 // Helper method that returns true if |ranges| is sorted in increasing order,
 // false otherwise.
-bool IsRangeListSorted(const SourceBufferStream::RangeList& ranges) {
+template <typename RangeClass>
+bool IsRangeListSorted(
+    const typename SourceBufferStream<RangeClass>::RangeList& ranges) {
   DecodeTimestamp prev = kNoDecodeTimestamp();
   for (const auto& range_ptr : ranges) {
     if (prev != kNoDecodeTimestamp() && prev >= range_ptr->GetStartTimestamp())
@@ -79,7 +80,8 @@
 }
 
 // Helper method for logging, converts a range into a readable string.
-std::string RangeToString(const SourceBufferRangeByDts& range) {
+template <typename RangeClass>
+std::string RangeToString(const RangeClass& range) {
   if (range.size_in_bytes() == 0) {
     return "[]";
   }
@@ -91,7 +93,9 @@
 }
 
 // Helper method for logging, converts a set of ranges into a readable string.
-std::string RangesToString(const SourceBufferStream::RangeList& ranges) {
+template <typename RangeClass>
+std::string RangesToString(
+    const typename SourceBufferStream<RangeClass>::RangeList& ranges) {
   if (ranges.empty())
     return "<EMPTY>";
 
@@ -104,8 +108,9 @@
   return ss.str();
 }
 
+template <typename RangeClass>
 std::string BufferQueueToLogString(
-    const SourceBufferStream::BufferQueue& buffers) {
+    const typename SourceBufferStream<RangeClass>::BufferQueue& buffers) {
   std::stringstream result;
   if (buffers.front()->GetDecodeTimestamp().InMicroseconds() ==
       buffers.front()->timestamp().InMicroseconds() &&
@@ -124,12 +129,14 @@
   return result.str();
 }
 
-SourceBufferRange::GapPolicy TypeToGapPolicy(SourceBufferStream::Type type) {
+template <typename RangeClass>
+SourceBufferRange::GapPolicy TypeToGapPolicy(
+    typename SourceBufferStream<RangeClass>::Type type) {
   switch (type) {
-    case SourceBufferStream::kAudio:
-    case SourceBufferStream::kVideo:
+    case SourceBufferStream<RangeClass>::kAudio:
+    case SourceBufferStream<RangeClass>::kVideo:
       return SourceBufferRange::NO_GAPS_ALLOWED;
-    case SourceBufferStream::kText:
+    case SourceBufferStream<RangeClass>::kText:
       return SourceBufferRange::ALLOW_GAPS;
   }
 
@@ -139,8 +146,10 @@
 
 }  // namespace
 
-SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config,
-                                       MediaLog* media_log)
+template <typename RangeClass>
+SourceBufferStream<RangeClass>::SourceBufferStream(
+    const AudioDecoderConfig& audio_config,
+    MediaLog* media_log)
     : media_log_(media_log),
       seek_buffer_timestamp_(kNoTimestamp),
       coded_frame_group_start_time_(kNoDecodeTimestamp()),
@@ -152,8 +161,10 @@
   audio_configs_.push_back(audio_config);
 }
 
-SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config,
-                                       MediaLog* media_log)
+template <typename RangeClass>
+SourceBufferStream<RangeClass>::SourceBufferStream(
+    const VideoDecoderConfig& video_config,
+    MediaLog* media_log)
     : media_log_(media_log),
       seek_buffer_timestamp_(kNoTimestamp),
       coded_frame_group_start_time_(kNoDecodeTimestamp()),
@@ -165,8 +176,10 @@
   video_configs_.push_back(video_config);
 }
 
-SourceBufferStream::SourceBufferStream(const TextTrackConfig& text_config,
-                                       MediaLog* media_log)
+template <typename RangeClass>
+SourceBufferStream<RangeClass>::SourceBufferStream(
+    const TextTrackConfig& text_config,
+    MediaLog* media_log)
     : media_log_(media_log),
       text_track_config_(text_config),
       seek_buffer_timestamp_(kNoTimestamp),
@@ -176,9 +189,11 @@
       max_interbuffer_distance_(kNoTimestamp),
       memory_limit_(kSourceBufferAudioMemoryLimit) {}
 
-SourceBufferStream::~SourceBufferStream() {}
+template <typename RangeClass>
+SourceBufferStream<RangeClass>::~SourceBufferStream() {}
 
-void SourceBufferStream::OnStartOfCodedFrameGroup(
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::OnStartOfCodedFrameGroup(
     DecodeTimestamp coded_frame_group_start_time) {
   DVLOG(1) << __func__ << " " << GetStreamTypeName() << " ("
            << coded_frame_group_start_time.InSecondsF() << ")";
@@ -186,7 +201,7 @@
   coded_frame_group_start_time_ = coded_frame_group_start_time;
   new_coded_frame_group_ = true;
 
-  RangeList::iterator last_range = range_for_next_append_;
+  typename RangeList::iterator last_range = range_for_next_append_;
   range_for_next_append_ = FindExistingRangeFor(coded_frame_group_start_time);
 
   // Only reset |last_appended_buffer_timestamp_| if this new coded frame group
@@ -206,7 +221,8 @@
   }
 }
 
-bool SourceBufferStream::Append(const BufferQueue& buffers) {
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::Append(const BufferQueue& buffers) {
   TRACE_EVENT2("media", "SourceBufferStream::Append",
                "stream type", GetStreamTypeName(),
                "buffers to append", buffers.size());
@@ -218,7 +234,7 @@
   DCHECK(!end_of_stream_);
 
   DVLOG(1) << __func__ << " " << GetStreamTypeName() << ": buffers "
-           << BufferQueueToLogString(buffers);
+           << BufferQueueToLogString<RangeClass>(buffers);
 
   // New coded frame groups emitted by the coded frame processor must begin with
   // a keyframe. TODO(wolenetz): Change this to [DCHECK + MEDIA_LOG(ERROR...) +
@@ -297,7 +313,7 @@
                     " keyframe that has been removed, and contain no keyframes."
                     " Skipping further processing.";
         DVLOG(1) << __func__ << " " << GetStreamTypeName()
-                 << ": done. ranges_=" << RangesToString(ranges_);
+                 << ": done. ranges_=" << RangesToString<RangeClass>(ranges_);
         return true;
       } else if (itr != buffers.begin()) {
         // Copy the first key frame and everything after it into
@@ -310,12 +326,11 @@
           buffers_for_new_range->front()->GetDecodeTimestamp();
     }
 
-    range_for_next_append_ =
-        AddToRanges(base::MakeUnique<SourceBufferRangeByDts>(
-            TypeToGapPolicy(GetType()), *buffers_for_new_range,
-            new_range_start_time,
-            base::Bind(&SourceBufferStream::GetMaxInterbufferDistance,
-                       base::Unretained(this))));
+    range_for_next_append_ = AddToRanges(base::MakeUnique<RangeClass>(
+        TypeToGapPolicy<RangeClass>(GetType()), *buffers_for_new_range,
+        new_range_start_time,
+        base::Bind(&SourceBufferStream<RangeClass>::GetMaxInterbufferDistance,
+                   base::Unretained(this))));
     last_appended_buffer_timestamp_ =
         buffers_for_new_range->back()->GetDecodeTimestamp();
     last_appended_buffer_duration_ = buffers_for_new_range->back()->duration();
@@ -367,14 +382,16 @@
   SetSelectedRangeIfNeeded(next_buffer_timestamp);
 
   DVLOG(1) << __func__ << " " << GetStreamTypeName()
-           << ": done. ranges_=" << RangesToString(ranges_);
-  DCHECK(IsRangeListSorted(ranges_));
+           << ": done. ranges_=" << RangesToString<RangeClass>(ranges_);
+  DCHECK(IsRangeListSorted<RangeClass>(ranges_));
   DCHECK(OnlySelectedRangeIsSeeked());
   return true;
 }
 
-void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end,
-                                base::TimeDelta duration) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::Remove(base::TimeDelta start,
+                                            base::TimeDelta end,
+                                            base::TimeDelta duration) {
   DVLOG(1) << __func__ << " " << GetStreamTypeName() << " ("
            << start.InSecondsF() << ", " << end.InSecondsF() << ", "
            << duration.InSecondsF() << ")";
@@ -412,7 +429,9 @@
   }
 }
 
-DecodeTimestamp SourceBufferStream::PotentialNextAppendTimestamp() const {
+template <typename RangeClass>
+DecodeTimestamp SourceBufferStream<RangeClass>::PotentialNextAppendTimestamp()
+    const {
   // The next potential append will either be just at or after
   // |last_appended_buffer_timestamp_| (if known), or if unknown and we are
   // still at the beginning of a new coded frame group, then will be into the
@@ -430,7 +449,8 @@
   return kNoDecodeTimestamp();
 }
 
-void SourceBufferStream::UpdateLastAppendStateForRemove(
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::UpdateLastAppendStateForRemove(
     DecodeTimestamp remove_start,
     DecodeTimestamp remove_end,
     bool exclude_start) {
@@ -474,15 +494,17 @@
   }
 }
 
-void SourceBufferStream::RemoveInternal(DecodeTimestamp start,
-                                        DecodeTimestamp end,
-                                        bool exclude_start,
-                                        BufferQueue* deleted_buffers) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::RemoveInternal(
+    DecodeTimestamp start,
+    DecodeTimestamp end,
+    bool exclude_start,
+    BufferQueue* deleted_buffers) {
   DVLOG(2) << __func__ << " " << GetStreamTypeName() << " ("
            << start.InSecondsF() << ", " << end.InSecondsF() << ", "
            << exclude_start << ")";
   DVLOG(3) << __func__ << " " << GetStreamTypeName()
-           << ": before remove ranges_=" << RangesToString(ranges_);
+           << ": before remove ranges_=" << RangesToString<RangeClass>(ranges_);
 
   DCHECK(start >= DecodeTimestamp());
   DCHECK(start < end) << "start " << start.InSecondsF()
@@ -492,15 +514,15 @@
   // Doing this upfront simplifies decisions about range_for_next_append_ below.
   UpdateLastAppendStateForRemove(start, end, exclude_start);
 
-  RangeList::iterator itr = ranges_.begin();
+  typename RangeList::iterator itr = ranges_.begin();
   while (itr != ranges_.end()) {
-    SourceBufferRangeByDts* range = itr->get();
+    RangeClass* range = itr->get();
     if (range->GetStartTimestamp() >= end)
       break;
 
     // Split off any remaining GOPs starting at or after |end| and add it to
     // |ranges_|.
-    std::unique_ptr<SourceBufferRangeByDts> new_range = range->SplitRange(end);
+    std::unique_ptr<RangeClass> new_range = range->SplitRange(end);
     if (new_range) {
       itr = ranges_.insert(++itr, std::move(new_range));
 
@@ -569,13 +591,14 @@
   }
 
   DVLOG(3) << __func__ << " " << GetStreamTypeName()
-           << ": after remove ranges_=" << RangesToString(ranges_);
+           << ": after remove ranges_=" << RangesToString<RangeClass>(ranges_);
 
-  DCHECK(IsRangeListSorted(ranges_));
+  DCHECK(IsRangeListSorted<RangeClass>(ranges_));
   DCHECK(OnlySelectedRangeIsSeeked());
 }
 
-void SourceBufferStream::ResetSeekState() {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::ResetSeekState() {
   SetSelectedRange(NULL);
   track_buffer_.clear();
   config_change_pending_ = false;
@@ -585,13 +608,15 @@
   pending_buffers_complete_ = false;
 }
 
-void SourceBufferStream::ResetLastAppendedState() {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::ResetLastAppendedState() {
   last_appended_buffer_timestamp_ = kNoDecodeTimestamp();
   last_appended_buffer_duration_ = kNoTimestamp;
   last_appended_buffer_is_keyframe_ = false;
 }
 
-bool SourceBufferStream::ShouldSeekToStartOfBuffered(
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::ShouldSeekToStartOfBuffered(
     base::TimeDelta seek_timestamp) const {
   if (ranges_.empty())
     return false;
@@ -601,7 +626,9 @@
           beginning_of_buffered < kSeekToStartFudgeRoom());
 }
 
-bool SourceBufferStream::IsMonotonicallyIncreasing(const BufferQueue& buffers) {
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::IsMonotonicallyIncreasing(
+    const BufferQueue& buffers) {
   DCHECK(!buffers.empty());
   DecodeTimestamp prev_timestamp = last_appended_buffer_timestamp_;
   bool prev_is_keyframe = last_appended_buffer_is_keyframe_;
@@ -640,8 +667,9 @@
   return true;
 }
 
-bool SourceBufferStream::OnlySelectedRangeIsSeeked() const {
-  for (RangeList::const_iterator itr = ranges_.begin();
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::OnlySelectedRangeIsSeeked() const {
+  for (typename RangeList::const_iterator itr = ranges_.begin();
        itr != ranges_.end(); ++itr) {
     if ((*itr)->HasNextBufferPosition() && itr->get() != selected_range_)
       return false;
@@ -649,7 +677,8 @@
   return !selected_range_ || selected_range_->HasNextBufferPosition();
 }
 
-void SourceBufferStream::UpdateMaxInterbufferDistance(
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::UpdateMaxInterbufferDistance(
     const BufferQueue& buffers) {
   DCHECK(!buffers.empty());
   DecodeTimestamp prev_timestamp = last_appended_buffer_timestamp_;
@@ -678,14 +707,16 @@
   }
 }
 
-void SourceBufferStream::SetConfigIds(const BufferQueue& buffers) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::SetConfigIds(const BufferQueue& buffers) {
   for (BufferQueue::const_iterator itr = buffers.begin();
        itr != buffers.end(); ++itr) {
     (*itr)->SetConfigId(append_config_index_);
   }
 }
 
-void SourceBufferStream::OnMemoryPressure(
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::OnMemoryPressure(
     DecodeTimestamp media_time,
     base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level,
     bool force_instant_gc) {
@@ -696,8 +727,10 @@
     GarbageCollectIfNeeded(media_time, 0);
 }
 
-bool SourceBufferStream::GarbageCollectIfNeeded(DecodeTimestamp media_time,
-                                                size_t newDataSize) {
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::GarbageCollectIfNeeded(
+    DecodeTimestamp media_time,
+    size_t newDataSize) {
   DCHECK(media_time != kNoDecodeTimestamp());
   // Garbage collection should only happen before/during appending new data,
   // which should not happen in end-of-stream state. Unless we also allow GC to
@@ -746,7 +779,7 @@
 
   DVLOG(2) << __func__ << " " << GetStreamTypeName()
            << ": Before GC media_time=" << media_time.InSecondsF()
-           << " ranges_=" << RangesToString(ranges_)
+           << " ranges_=" << RangesToString<RangeClass>(ranges_)
            << " seek_pending_=" << seek_pending_
            << " ranges_size=" << ranges_size << " newDataSize=" << newDataSize
            << " memory_limit_=" << memory_limit_
@@ -787,7 +820,7 @@
     size_t between = FreeBuffersAfterLastAppended(bytes_to_free, media_time);
     DVLOG(3) << __func__ << " FreeBuffersAfterLastAppended "
              << " released " << between << " bytes"
-             << " ranges_=" << RangesToString(ranges_);
+             << " ranges_=" << RangesToString<RangeClass>(ranges_);
     bytes_freed += between;
 
     // Some players start appending data at the new seek target position before
@@ -815,7 +848,8 @@
     // All data earlier than the seek target |media_time| can be removed safely
     size_t front = FreeBuffers(bytes_to_free - bytes_freed, media_time, false);
     DVLOG(3) << __func__ << " Removed " << front
-             << " bytes from the front. ranges_=" << RangesToString(ranges_);
+             << " bytes from the front. ranges_="
+             << RangesToString<RangeClass>(ranges_);
     bytes_freed += front;
 
     // If removing data earlier than |media_time| didn't free up enough space,
@@ -823,7 +857,8 @@
     if (bytes_freed < bytes_to_free) {
       size_t back = FreeBuffers(bytes_to_free - bytes_freed, media_time, true);
       DVLOG(3) << __func__ << " Removed " << back
-               << " bytes from the back. ranges_=" << RangesToString(ranges_);
+               << " bytes from the back. ranges_="
+               << RangesToString<RangeClass>(ranges_);
       bytes_freed += back;
     }
 
@@ -833,7 +868,8 @@
       size_t front2 = FreeBuffers(bytes_to_free - bytes_freed,
                                   ranges_.back()->GetEndTimestamp(), false);
       DVLOG(3) << __func__ << " Removed " << front2
-               << " bytes from the front. ranges_=" << RangesToString(ranges_);
+               << " bytes from the front. ranges_="
+               << RangesToString<RangeClass>(ranges_);
       bytes_freed += front2;
     }
     DCHECK(bytes_freed >= bytes_to_free);
@@ -844,7 +880,8 @@
   if (bytes_freed < bytes_to_free) {
     size_t front = FreeBuffers(bytes_to_free - bytes_freed, media_time, false);
     DVLOG(3) << __func__ << " Removed " << front
-             << " bytes from the front. ranges_=" << RangesToString(ranges_);
+             << " bytes from the front. ranges_="
+             << RangesToString<RangeClass>(ranges_);
     bytes_freed += front;
   }
 
@@ -853,7 +890,8 @@
   if (bytes_freed < bytes_to_free) {
     size_t back = FreeBuffers(bytes_to_free - bytes_freed, media_time, true);
     DVLOG(3) << __func__ << " Removed " << back
-             << " bytes from the back. ranges_=" << RangesToString(ranges_);
+             << " bytes from the back. ranges_="
+             << RangesToString<RangeClass>(ranges_);
     bytes_freed += back;
   }
 
@@ -861,13 +899,15 @@
            << ": After GC bytes_to_free=" << bytes_to_free
            << " bytes_freed=" << bytes_freed
            << " bytes_over_hard_memory_limit=" << bytes_over_hard_memory_limit
-           << " ranges_=" << RangesToString(ranges_);
+           << " ranges_=" << RangesToString<RangeClass>(ranges_);
 
   return bytes_freed >= bytes_over_hard_memory_limit;
 }
 
-size_t SourceBufferStream::FreeBuffersAfterLastAppended(
-    size_t total_bytes_to_free, DecodeTimestamp media_time) {
+template <typename RangeClass>
+size_t SourceBufferStream<RangeClass>::FreeBuffersAfterLastAppended(
+    size_t total_bytes_to_free,
+    DecodeTimestamp media_time) {
   DVLOG(4) << __func__ << " last_appended_buffer_timestamp_="
            << last_appended_buffer_timestamp_.InSecondsF()
            << " media_time=" << media_time.InSecondsF();
@@ -900,9 +940,12 @@
   return bytes_freed;
 }
 
-size_t SourceBufferStream::GetRemovalRange(
-    DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp,
-    size_t total_bytes_to_free, DecodeTimestamp* removal_end_timestamp) {
+template <typename RangeClass>
+size_t SourceBufferStream<RangeClass>::GetRemovalRange(
+    DecodeTimestamp start_timestamp,
+    DecodeTimestamp end_timestamp,
+    size_t total_bytes_to_free,
+    DecodeTimestamp* removal_end_timestamp) {
   DCHECK(start_timestamp >= DecodeTimestamp()) << start_timestamp.InSecondsF();
   DCHECK(start_timestamp < end_timestamp)
       << "start " << start_timestamp.InSecondsF()
@@ -910,9 +953,9 @@
 
   size_t bytes_freed = 0;
 
-  for (RangeList::iterator itr = ranges_.begin();
+  for (typename RangeList::iterator itr = ranges_.begin();
        itr != ranges_.end() && bytes_freed < total_bytes_to_free; ++itr) {
-    SourceBufferRangeByDts* range = itr->get();
+    RangeClass* range = itr->get();
     if (range->GetStartTimestamp() >= end_timestamp)
       break;
     if (range->GetEndTimestamp() < start_timestamp)
@@ -926,9 +969,10 @@
   return bytes_freed;
 }
 
-size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free,
-                                       DecodeTimestamp media_time,
-                                       bool reverse_direction) {
+template <typename RangeClass>
+size_t SourceBufferStream<RangeClass>::FreeBuffers(size_t total_bytes_to_free,
+                                                   DecodeTimestamp media_time,
+                                                   bool reverse_direction) {
   TRACE_EVENT2("media", "SourceBufferStream::FreeBuffers",
                "total bytes to free", total_bytes_to_free,
                "reverse direction", reverse_direction);
@@ -938,10 +982,10 @@
 
   // This range will save the last GOP appended to |range_for_next_append_|
   // if the buffers surrounding it get deleted during garbage collection.
-  std::unique_ptr<SourceBufferRangeByDts> new_range_for_append;
+  std::unique_ptr<RangeClass> new_range_for_append;
 
   while (!ranges_.empty() && bytes_freed < total_bytes_to_free) {
-    SourceBufferRangeByDts* current_range = NULL;
+    RangeClass* current_range = NULL;
     BufferQueue buffers;
     size_t bytes_deleted = 0;
 
@@ -986,9 +1030,9 @@
       DCHECK(!new_range_for_append);
 
       // Create a new range containing these buffers.
-      new_range_for_append = base::MakeUnique<SourceBufferRangeByDts>(
-          TypeToGapPolicy(GetType()), buffers, kNoDecodeTimestamp(),
-          base::Bind(&SourceBufferStream::GetMaxInterbufferDistance,
+      new_range_for_append = base::MakeUnique<RangeClass>(
+          TypeToGapPolicy<RangeClass>(GetType()), buffers, kNoDecodeTimestamp(),
+          base::Bind(&SourceBufferStream<RangeClass>::GetMaxInterbufferDistance,
                      base::Unretained(this)));
       range_for_next_append_ = ranges_.end();
     } else {
@@ -1022,7 +1066,7 @@
     // Merging is necessary if there were no buffers (or very few buffers)
     // deleted after creating that added range.
     if (range_for_next_append_ != ranges_.begin()) {
-      RangeList::iterator range_before_next = range_for_next_append_;
+      typename RangeList::iterator range_before_next = range_for_next_append_;
       --range_before_next;
       MergeWithAdjacentRangeIfNecessary(range_before_next);
     }
@@ -1031,7 +1075,9 @@
   return bytes_freed;
 }
 
-void SourceBufferStream::TrimSpliceOverlap(const BufferQueue& new_buffers) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::TrimSpliceOverlap(
+    const BufferQueue& new_buffers) {
   DCHECK(!new_buffers.empty());
   DCHECK_EQ(kAudio, GetType());
 
@@ -1039,7 +1085,7 @@
   const base::TimeDelta splice_timestamp = new_buffers.front()->timestamp();
   const DecodeTimestamp splice_dts =
       DecodeTimestamp::FromPresentationTime(splice_timestamp);
-  RangeList::iterator range_itr = FindExistingRangeFor(splice_dts);
+  typename RangeList::iterator range_itr = FindExistingRangeFor(splice_dts);
   if (range_itr == ranges_.end()) {
     DVLOG(3) << __func__ << " No splice trimming. No range overlap at time "
              << splice_timestamp.InMicroseconds();
@@ -1139,8 +1185,10 @@
   DVLOG(1) << __func__ << log_string.str();
 }
 
-void SourceBufferStream::PrepareRangesForNextAppend(
-    const BufferQueue& new_buffers, BufferQueue* deleted_buffers) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::PrepareRangesForNextAppend(
+    const BufferQueue& new_buffers,
+    BufferQueue* deleted_buffers) {
   DCHECK(deleted_buffers);
 
   if (GetType() == kAudio)
@@ -1199,14 +1247,18 @@
   RemoveInternal(next_timestamp, end, exclude_start, deleted_buffers);
 }
 
-bool SourceBufferStream::AreAdjacentInSequence(
-    DecodeTimestamp first_timestamp, DecodeTimestamp second_timestamp) const {
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::AreAdjacentInSequence(
+    DecodeTimestamp first_timestamp,
+    DecodeTimestamp second_timestamp) const {
   return first_timestamp < second_timestamp &&
       second_timestamp <=
       first_timestamp + ComputeFudgeRoom(GetMaxInterbufferDistance());
 }
 
-void SourceBufferStream::PruneTrackBuffer(const DecodeTimestamp timestamp) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::PruneTrackBuffer(
+    const DecodeTimestamp timestamp) {
   // If we don't have the next timestamp, we don't have anything to delete.
   if (timestamp == kNoDecodeTimestamp())
     return;
@@ -1220,13 +1272,13 @@
            << ". New track buffer size:" << track_buffer_.size();
 }
 
-void SourceBufferStream::MergeWithAdjacentRangeIfNecessary(
-    const RangeList::iterator& range_with_new_buffers_itr) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::MergeWithAdjacentRangeIfNecessary(
+    const typename RangeList::iterator& range_with_new_buffers_itr) {
   DCHECK(range_with_new_buffers_itr != ranges_.end());
 
-  SourceBufferRangeByDts* range_with_new_buffers =
-      range_with_new_buffers_itr->get();
-  RangeList::iterator next_range_itr = range_with_new_buffers_itr;
+  RangeClass* range_with_new_buffers = range_with_new_buffers_itr->get();
+  typename RangeList::iterator next_range_itr = range_with_new_buffers_itr;
   ++next_range_itr;
 
   if (next_range_itr == ranges_.end() ||
@@ -1251,7 +1303,8 @@
   DeleteAndRemoveRange(&next_range_itr);
 }
 
-void SourceBufferStream::Seek(base::TimeDelta timestamp) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::Seek(base::TimeDelta timestamp) {
   DCHECK(timestamp >= base::TimeDelta());
   DVLOG(1) << __func__ << " " << GetStreamTypeName() << " ("
            << timestamp.InSecondsF() << ")";
@@ -1269,7 +1322,7 @@
 
   DecodeTimestamp seek_dts = DecodeTimestamp::FromPresentationTime(timestamp);
 
-  RangeList::iterator itr = ranges_.end();
+  typename RangeList::iterator itr = ranges_.end();
   for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) {
     if ((*itr)->CanSeekTo(seek_dts))
       break;
@@ -1294,11 +1347,13 @@
   seek_pending_ = false;
 }
 
-bool SourceBufferStream::IsSeekPending() const {
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::IsSeekPending() const {
   return seek_pending_ && !IsEndOfStreamReached();
 }
 
-void SourceBufferStream::OnSetDuration(base::TimeDelta duration) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::OnSetDuration(base::TimeDelta duration) {
   DVLOG(1) << __func__ << " " << GetStreamTypeName() << " ("
            << duration.InSecondsF() << ")";
   DCHECK(!end_of_stream_);
@@ -1321,7 +1376,9 @@
   }
 }
 
-SourceBufferStream::Status SourceBufferStream::GetNextBuffer(
+template <typename RangeClass>
+typename SourceBufferStream<RangeClass>::Status
+SourceBufferStream<RangeClass>::GetNextBuffer(
     scoped_refptr<StreamParserBuffer>* out_buffer) {
   DVLOG(2) << __func__ << " " << GetStreamTypeName();
   if (!pending_buffer_.get()) {
@@ -1343,7 +1400,9 @@
   return status;
 }
 
-SourceBufferStream::Status SourceBufferStream::HandleNextBufferWithPreroll(
+template <typename RangeClass>
+typename SourceBufferStream<RangeClass>::Status
+SourceBufferStream<RangeClass>::HandleNextBufferWithPreroll(
     scoped_refptr<StreamParserBuffer>* out_buffer) {
   // Any config change should have already been handled.
   DCHECK_EQ(current_config_index_, pending_buffer_->GetConfigId());
@@ -1361,7 +1420,9 @@
   return SourceBufferStream::kSuccess;
 }
 
-SourceBufferStream::Status SourceBufferStream::GetNextBufferInternal(
+template <typename RangeClass>
+typename SourceBufferStream<RangeClass>::Status
+SourceBufferStream<RangeClass>::GetNextBufferInternal(
     scoped_refptr<StreamParserBuffer>* out_buffer) {
   CHECK(!config_change_pending_);
 
@@ -1417,7 +1478,8 @@
   return kSuccess;
 }
 
-void SourceBufferStream::WarnIfTrackBufferExhaustionSkipsForward(
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::WarnIfTrackBufferExhaustionSkipsForward(
     const scoped_refptr<StreamParserBuffer>& next_buffer) {
   if (!just_exhausted_track_buffer_)
     return;
@@ -1440,7 +1502,8 @@
   }
 }
 
-DecodeTimestamp SourceBufferStream::GetNextBufferTimestamp() {
+template <typename RangeClass>
+DecodeTimestamp SourceBufferStream<RangeClass>::GetNextBufferTimestamp() {
   if (!track_buffer_.empty())
     return track_buffer_.front()->GetDecodeTimestamp();
 
@@ -1451,19 +1514,24 @@
   return selected_range_->GetNextTimestamp();
 }
 
-SourceBufferStream::RangeList::iterator
-SourceBufferStream::FindExistingRangeFor(DecodeTimestamp start_timestamp) {
-  for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr) {
+template <typename RangeClass>
+typename SourceBufferStream<RangeClass>::RangeList::iterator
+SourceBufferStream<RangeClass>::FindExistingRangeFor(
+    DecodeTimestamp start_timestamp) {
+  for (typename RangeList::iterator itr = ranges_.begin(); itr != ranges_.end();
+       ++itr) {
     if ((*itr)->BelongsToRange(start_timestamp))
       return itr;
   }
   return ranges_.end();
 }
 
-SourceBufferStream::RangeList::iterator SourceBufferStream::AddToRanges(
-    std::unique_ptr<SourceBufferRangeByDts> new_range) {
+template <typename RangeClass>
+typename SourceBufferStream<RangeClass>::RangeList::iterator
+SourceBufferStream<RangeClass>::AddToRanges(
+    std::unique_ptr<RangeClass> new_range) {
   DecodeTimestamp start_timestamp = new_range->GetStartTimestamp();
-  RangeList::iterator itr = ranges_.end();
+  typename RangeList::iterator itr = ranges_.end();
   for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) {
     if ((*itr)->GetStartTimestamp() > start_timestamp)
       break;
@@ -1471,10 +1539,11 @@
   return ranges_.insert(itr, std::move(new_range));
 }
 
-SourceBufferStream::RangeList::iterator
-SourceBufferStream::GetSelectedRangeItr() {
+template <typename RangeClass>
+typename SourceBufferStream<RangeClass>::RangeList::iterator
+SourceBufferStream<RangeClass>::GetSelectedRangeItr() {
   DCHECK(selected_range_);
-  RangeList::iterator itr = ranges_.end();
+  typename RangeList::iterator itr = ranges_.end();
   for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) {
     if (itr->get() == selected_range_)
       break;
@@ -1483,15 +1552,17 @@
   return itr;
 }
 
-void SourceBufferStream::SeekAndSetSelectedRange(
-    SourceBufferRangeByDts* range,
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::SeekAndSetSelectedRange(
+    RangeClass* range,
     DecodeTimestamp seek_timestamp) {
   if (range)
     range->Seek(seek_timestamp);
   SetSelectedRange(range);
 }
 
-void SourceBufferStream::SetSelectedRange(SourceBufferRangeByDts* range) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::SetSelectedRange(RangeClass* range) {
   DVLOG(1) << __func__ << " " << GetStreamTypeName() << ": " << selected_range_
            << " " << (selected_range_ ? RangeToString(*selected_range_) : "")
            << " -> " << range << " " << (range ? RangeToString(*range) : "");
@@ -1501,9 +1572,11 @@
   selected_range_ = range;
 }
 
-Ranges<base::TimeDelta> SourceBufferStream::GetBufferedTime() const {
+template <typename RangeClass>
+Ranges<base::TimeDelta> SourceBufferStream<RangeClass>::GetBufferedTime()
+    const {
   Ranges<base::TimeDelta> ranges;
-  for (RangeList::const_iterator itr = ranges_.begin();
+  for (typename RangeList::const_iterator itr = ranges_.begin();
        itr != ranges_.end(); ++itr) {
     ranges.Add((*itr)->GetStartTimestamp().ToPresentationTime(),
                (*itr)->GetBufferedEndTimestamp().ToPresentationTime());
@@ -1511,7 +1584,9 @@
   return ranges;
 }
 
-base::TimeDelta SourceBufferStream::GetHighestPresentationTimestamp() const {
+template <typename RangeClass>
+base::TimeDelta
+SourceBufferStream<RangeClass>::GetHighestPresentationTimestamp() const {
   if (ranges_.empty())
     return base::TimeDelta();
 
@@ -1520,31 +1595,36 @@
   return ranges_.back()->GetEndTimestamp().ToPresentationTime();
 }
 
-base::TimeDelta SourceBufferStream::GetBufferedDuration() const {
+template <typename RangeClass>
+base::TimeDelta SourceBufferStream<RangeClass>::GetBufferedDuration() const {
   if (ranges_.empty())
     return base::TimeDelta();
 
   return ranges_.back()->GetBufferedEndTimestamp().ToPresentationTime();
 }
 
-size_t SourceBufferStream::GetBufferedSize() const {
+template <typename RangeClass>
+size_t SourceBufferStream<RangeClass>::GetBufferedSize() const {
   size_t ranges_size = 0;
   for (const auto& range_ptr : ranges_)
     ranges_size += range_ptr->size_in_bytes();
   return ranges_size;
 }
 
-void SourceBufferStream::MarkEndOfStream() {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::MarkEndOfStream() {
   DCHECK(!end_of_stream_);
   end_of_stream_ = true;
 }
 
-void SourceBufferStream::UnmarkEndOfStream() {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::UnmarkEndOfStream() {
   DCHECK(end_of_stream_);
   end_of_stream_ = false;
 }
 
-bool SourceBufferStream::IsEndOfStreamReached() const {
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::IsEndOfStreamReached() const {
   if (!end_of_stream_ || !track_buffer_.empty())
     return false;
 
@@ -1563,7 +1643,9 @@
   return selected_range_ == ranges_.back().get();
 }
 
-const AudioDecoderConfig& SourceBufferStream::GetCurrentAudioDecoderConfig() {
+template <typename RangeClass>
+const AudioDecoderConfig&
+SourceBufferStream<RangeClass>::GetCurrentAudioDecoderConfig() {
   if (config_change_pending_)
     CompleteConfigChange();
   // Trying to track down crash. http://crbug.com/715761
@@ -1572,7 +1654,9 @@
   return audio_configs_[current_config_index_];
 }
 
-const VideoDecoderConfig& SourceBufferStream::GetCurrentVideoDecoderConfig() {
+template <typename RangeClass>
+const VideoDecoderConfig&
+SourceBufferStream<RangeClass>::GetCurrentVideoDecoderConfig() {
   if (config_change_pending_)
     CompleteConfigChange();
   // Trying to track down crash. http://crbug.com/715761
@@ -1581,17 +1665,23 @@
   return video_configs_[current_config_index_];
 }
 
-const TextTrackConfig& SourceBufferStream::GetCurrentTextTrackConfig() {
+template <typename RangeClass>
+const TextTrackConfig&
+SourceBufferStream<RangeClass>::GetCurrentTextTrackConfig() {
   return text_track_config_;
 }
 
-base::TimeDelta SourceBufferStream::GetMaxInterbufferDistance() const {
+template <typename RangeClass>
+base::TimeDelta SourceBufferStream<RangeClass>::GetMaxInterbufferDistance()
+    const {
   if (max_interbuffer_distance_ == kNoTimestamp)
     return base::TimeDelta::FromMilliseconds(kDefaultBufferDurationInMs);
   return max_interbuffer_distance_;
 }
 
-bool SourceBufferStream::UpdateAudioConfig(const AudioDecoderConfig& config) {
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::UpdateAudioConfig(
+    const AudioDecoderConfig& config) {
   DCHECK(!audio_configs_.empty());
   DCHECK(video_configs_.empty());
   DVLOG(3) << "UpdateAudioConfig.";
@@ -1617,7 +1707,9 @@
   return true;
 }
 
-bool SourceBufferStream::UpdateVideoConfig(const VideoDecoderConfig& config) {
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::UpdateVideoConfig(
+    const VideoDecoderConfig& config) {
   DCHECK(!video_configs_.empty());
   DCHECK(audio_configs_.empty());
   DVLOG(3) << "UpdateVideoConfig.";
@@ -1643,7 +1735,8 @@
   return true;
 }
 
-void SourceBufferStream::CompleteConfigChange() {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::CompleteConfigChange() {
   config_change_pending_ = false;
 
   if (!track_buffer_.empty()) {
@@ -1655,7 +1748,8 @@
     current_config_index_ = selected_range_->GetNextConfigId();
 }
 
-void SourceBufferStream::SetSelectedRangeIfNeeded(
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::SetSelectedRangeIfNeeded(
     const DecodeTimestamp timestamp) {
   DVLOG(2) << __func__ << " " << GetStreamTypeName() << "("
            << timestamp.InSecondsF() << ")";
@@ -1700,12 +1794,14 @@
                           seek_timestamp);
 }
 
-DecodeTimestamp SourceBufferStream::FindNewSelectedRangeSeekTimestamp(
+template <typename RangeClass>
+DecodeTimestamp
+SourceBufferStream<RangeClass>::FindNewSelectedRangeSeekTimestamp(
     const DecodeTimestamp start_timestamp) {
   DCHECK(start_timestamp != kNoDecodeTimestamp());
   DCHECK(start_timestamp >= DecodeTimestamp());
 
-  RangeList::iterator itr = ranges_.begin();
+  typename RangeList::iterator itr = ranges_.begin();
 
   // When checking a range to see if it has or begins soon enough after
   // |start_timestamp|, use the fudge room to determine "soon enough".
@@ -1737,11 +1833,12 @@
   return kNoDecodeTimestamp();
 }
 
-DecodeTimestamp SourceBufferStream::FindKeyframeAfterTimestamp(
+template <typename RangeClass>
+DecodeTimestamp SourceBufferStream<RangeClass>::FindKeyframeAfterTimestamp(
     const DecodeTimestamp timestamp) {
   DCHECK(timestamp != kNoDecodeTimestamp());
 
-  RangeList::iterator itr = FindExistingRangeFor(timestamp);
+  typename RangeList::iterator itr = FindExistingRangeFor(timestamp);
 
   if (itr == ranges_.end())
     return kNoDecodeTimestamp();
@@ -1751,7 +1848,8 @@
   return (*itr)->NextKeyframeTimestamp(timestamp);
 }
 
-std::string SourceBufferStream::GetStreamTypeName() const {
+template <typename RangeClass>
+std::string SourceBufferStream<RangeClass>::GetStreamTypeName() const {
   switch (GetType()) {
     case kAudio:
       return "AUDIO";
@@ -1764,7 +1862,9 @@
   return "";
 }
 
-SourceBufferStream::Type SourceBufferStream::GetType() const {
+template <typename RangeClass>
+typename SourceBufferStream<RangeClass>::Type
+SourceBufferStream<RangeClass>::GetType() const {
   if (!audio_configs_.empty())
     return kAudio;
   if (!video_configs_.empty())
@@ -1773,7 +1873,9 @@
   return kText;
 }
 
-void SourceBufferStream::DeleteAndRemoveRange(RangeList::iterator* itr) {
+template <typename RangeClass>
+void SourceBufferStream<RangeClass>::DeleteAndRemoveRange(
+    typename RangeList::iterator* itr) {
   DVLOG(1) << __func__;
 
   DCHECK(*itr != ranges_.end());
@@ -1791,7 +1893,8 @@
   *itr = ranges_.erase(*itr);
 }
 
-bool SourceBufferStream::SetPendingBuffer(
+template <typename RangeClass>
+bool SourceBufferStream<RangeClass>::SetPendingBuffer(
     scoped_refptr<StreamParserBuffer>* out_buffer) {
   DCHECK(out_buffer->get());
   DCHECK(!pending_buffer_.get());
@@ -1806,4 +1909,6 @@
   return true;
 }
 
+template class SourceBufferStream<SourceBufferRangeByDts>;
+
 }  // namespace media
diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h
index 62577b0..d323473 100644
--- a/media/filters/source_buffer_stream.h
+++ b/media/filters/source_buffer_stream.h
@@ -15,6 +15,7 @@
 #include <list>
 #include <memory>
 #include <string>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -28,21 +29,23 @@
 #include "media/base/stream_parser_buffer.h"
 #include "media/base/text_track_config.h"
 #include "media/base/video_decoder_config.h"
+#include "media/filters/source_buffer_range.h"
 
 namespace media {
 
-class SourceBufferRangeByDts;
-
 // See file-level comment for complete description.
+// Template parameter determines which kind of buffering behavior is used. See
+// https://crbug.com/718641 and media::MseBufferByPts feature.
+template <typename RangeClass>
 class MEDIA_EXPORT SourceBufferStream {
  public:
-  typedef StreamParser::BufferQueue BufferQueue;
-  // TODO(wolenetz): Define both a ByDts and ByPts RangeList and use the type
-  // conditioned on which kind of SourceBufferStream. Maybe take a template
-  // parameter to SourceBufferStream class (e.g. SourceBufferRangeBy{Dts,Pts})
-  // to differentiate the behavior while being type-safe and efficient. See
-  // https://crbug.com/718641.
-  typedef std::list<std::unique_ptr<SourceBufferRangeByDts>> RangeList;
+  static_assert(
+      std::is_base_of<SourceBufferRange, RangeClass>::value &&
+          !std::is_abstract<RangeClass>::value,
+      "RangeClass must be a concrete class having SourceBufferRange as base");
+
+  using BufferQueue = StreamParser::BufferQueue;
+  using RangeList = std::list<std::unique_ptr<RangeClass>>;
 
   // Status returned by GetNextBuffer().
   // kSuccess: Indicates that the next buffer was returned.
@@ -215,7 +218,7 @@
   // Checks to see if |range_with_new_buffers_itr| can be merged with the range
   // next to it, and merges them if so.
   void MergeWithAdjacentRangeIfNecessary(
-      const RangeList::iterator& range_with_new_buffers_itr);
+      const typename RangeList::iterator& range_with_new_buffers_itr);
 
   // Returns true if |second_timestamp| is the timestamp of the next buffer in
   // sequence after |first_timestamp|, false otherwise.
@@ -230,25 +233,26 @@
   // Finds the range that should contain a coded frame group that begins with
   // |start_timestamp| and returns the iterator pointing to it. Returns
   // |ranges_.end()| if there's no such existing range.
-  RangeList::iterator FindExistingRangeFor(DecodeTimestamp start_timestamp);
+  typename RangeList::iterator FindExistingRangeFor(
+      DecodeTimestamp start_timestamp);
 
   // Inserts |new_range| into |ranges_| preserving sorted order. Returns an
   // iterator in |ranges_| that points to |new_range|. |new_range| becomes owned
   // by |ranges_|.
-  RangeList::iterator AddToRanges(
-      std::unique_ptr<SourceBufferRangeByDts> new_range);
+  typename RangeList::iterator AddToRanges(
+      std::unique_ptr<RangeClass> new_range);
 
   // Returns an iterator that points to the place in |ranges_| where
   // |selected_range_| lives.
-  RangeList::iterator GetSelectedRangeItr();
+  typename RangeList::iterator GetSelectedRangeItr();
 
   // Sets the |selected_range_| to |range| and resets the next buffer position
   // for the previous |selected_range_|.
-  void SetSelectedRange(SourceBufferRangeByDts* range);
+  void SetSelectedRange(RangeClass* range);
 
   // Seeks |range| to |seek_timestamp| and then calls SetSelectedRange() with
   // |range|.
-  void SeekAndSetSelectedRange(SourceBufferRangeByDts* range,
+  void SeekAndSetSelectedRange(RangeClass* range,
                                DecodeTimestamp seek_timestamp);
 
   // Resets this stream back to an unseeked state.
@@ -318,7 +322,7 @@
   // If |*itr| points to |selected_range_|, then |selected_range_| is set to
   // NULL. After the range is removed, |*itr| is to the range after the one that
   // was removed or to |ranges_.end()| if the last range was removed.
-  void DeleteAndRemoveRange(RangeList::iterator* itr);
+  void DeleteAndRemoveRange(typename RangeList::iterator* itr);
 
   // Helper function used when updating |range_for_next_append_|.
   // Returns a guess of what the next append timestamp will be based on
@@ -413,7 +417,7 @@
   // Pointer to the seeked-to Range. This is the range from which
   // GetNextBuffer() calls are fulfilled after the |track_buffer_| has been
   // emptied.
-  SourceBufferRangeByDts* selected_range_ = nullptr;
+  RangeClass* selected_range_ = nullptr;
 
   // Queue of the next buffers to be returned from calls to GetNextBuffer(). If
   // |track_buffer_| is empty, return buffers from |selected_range_|.
@@ -428,7 +432,7 @@
 
   // Points to the range containing the current coded frame group being
   // appended.
-  RangeList::iterator range_for_next_append_;
+  typename RangeList::iterator range_for_next_append_;
 
   // True when the next call to Append() begins a new coded frame group.
   // TODO(wolenetz): Simplify by passing this flag into Append().
@@ -473,7 +477,7 @@
   int num_garbage_collect_algorithm_logs_ = 0;
   int num_strange_same_timestamps_logs_ = 0;
 
-  DISALLOW_COPY_AND_ASSIGN(SourceBufferStream);
+  DISALLOW_COPY_AND_ASSIGN(SourceBufferStream<RangeClass>);
 };
 
 }  // namespace media
diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc
index 58423a6..1910a86 100644
--- a/media/filters/source_buffer_stream_unittest.cc
+++ b/media/filters/source_buffer_stream_unittest.cc
@@ -76,8 +76,19 @@
 
 // Test parameter determines if media::kMseBufferByPts feature should be forced
 // on or off for the test.
+// TODO(wolenetz): Add ByPts support by switching from TEST_P to TYPED_TEST with
+// template parameter to match the various SourceBufferStream<RangeClass>
+// type(s) used in production code. See https://crbug.com/718641. In that test
+// style, fixture template parameter determines which kind of buffering API
+// would be unit tested. Note that SBS and SBR internally ignore
+// media::kMseBufferByPts feature setting; the subclass of SBR controls the
+// behavior type, fixed at construction time.  For now, this "using" begins the
+// transition:
+using RangeApi = SourceBufferRangeByDts;
 class SourceBufferStreamTest : public testing::TestWithParam<BufferingApi> {
  protected:
+  using StreamType = SourceBufferStream<RangeApi>;
+
   SourceBufferStreamTest() {
     buffering_api_ = GetParam();
     switch (buffering_api_) {
@@ -90,7 +101,7 @@
     }
     video_config_ = TestVideoConfig::Normal();
     SetStreamInfo(kDefaultFramesPerSecond, kDefaultKeyframesPerSecond);
-    stream_.reset(new SourceBufferStream(video_config_, &media_log_));
+    stream_.reset(new StreamType(video_config_, &media_log_));
   }
 
   void SetMemoryLimit(size_t buffers_of_data) {
@@ -106,7 +117,7 @@
   void SetTextStream() {
     video_config_ = TestVideoConfig::Invalid();
     TextTrackConfig config(kTextSubtitles, "", "", "");
-    stream_.reset(new SourceBufferStream(config, &media_log_));
+    stream_.reset(new StreamType(config, &media_log_));
     SetStreamInfo(2, 2);
   }
 
@@ -115,7 +126,7 @@
     audio_config_.Initialize(kCodecVorbis, kSampleFormatPlanarF32,
                              CHANNEL_LAYOUT_STEREO, 1000, EmptyExtraData(),
                              Unencrypted(), base::TimeDelta(), 0);
-    stream_.reset(new SourceBufferStream(audio_config_, &media_log_));
+    stream_.reset(new StreamType(audio_config_, &media_log_));
 
     // Equivalent to 2ms per frame.
     SetStreamInfo(500, 500);
@@ -336,10 +347,10 @@
     int current_position = starting_position;
     for (; current_position <= ending_position; current_position++) {
       scoped_refptr<StreamParserBuffer> buffer;
-      SourceBufferStream::Status status = stream_->GetNextBuffer(&buffer);
+      StreamType::Status status = stream_->GetNextBuffer(&buffer);
 
-      EXPECT_NE(status, SourceBufferStream::kConfigChange);
-      if (status != SourceBufferStream::kSuccess)
+      EXPECT_NE(status, StreamType::kConfigChange);
+      if (status != StreamType::kSuccess)
         break;
 
       if (expect_keyframe && current_position == starting_position)
@@ -367,23 +378,23 @@
     std::vector<std::string> timestamps = base::SplitString(
         expected, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
     std::stringstream ss;
-    const SourceBufferStream::Type type = stream_->GetType();
+    const StreamType::Type type = stream_->GetType();
     for (size_t i = 0; i < timestamps.size(); i++) {
       scoped_refptr<StreamParserBuffer> buffer;
-      SourceBufferStream::Status status = stream_->GetNextBuffer(&buffer);
+      StreamType::Status status = stream_->GetNextBuffer(&buffer);
 
       if (i > 0)
         ss << " ";
 
-      if (status == SourceBufferStream::kConfigChange) {
+      if (status == StreamType::kConfigChange) {
         switch (type) {
-          case SourceBufferStream::kVideo:
+          case StreamType::kVideo:
             stream_->GetCurrentVideoDecoderConfig();
             break;
-          case SourceBufferStream::kAudio:
+          case StreamType::kAudio:
             stream_->GetCurrentAudioDecoderConfig();
             break;
-          case SourceBufferStream::kText:
+          case StreamType::kText:
             stream_->GetCurrentTextTrackConfig();
             break;
         }
@@ -394,8 +405,8 @@
         continue;
       }
 
-      EXPECT_EQ(SourceBufferStream::kSuccess, status);
-      if (status != SourceBufferStream::kSuccess)
+      EXPECT_EQ(StreamType::kSuccess, status);
+      if (status != StreamType::kSuccess)
         break;
 
       if (granularity == TimeGranularity::kMillisecond)
@@ -432,8 +443,7 @@
         // more buffer.  The first buffer should match the timestamp and config
         // of the second buffer, except that its discard_padding() should be its
         // duration.
-        ASSERT_EQ(SourceBufferStream::kSuccess,
-                  stream_->GetNextBuffer(&buffer));
+        ASSERT_EQ(StreamType::kSuccess, stream_->GetNextBuffer(&buffer));
         ASSERT_EQ(buffer->GetConfigId(), preroll_buffer->GetConfigId());
         ASSERT_EQ(buffer->track_id(), preroll_buffer->track_id());
         ASSERT_EQ(buffer->timestamp(), preroll_buffer->timestamp());
@@ -453,13 +463,12 @@
 
   void CheckNoNextBuffer() {
     scoped_refptr<StreamParserBuffer> buffer;
-    EXPECT_EQ(SourceBufferStream::kNeedBuffer, stream_->GetNextBuffer(&buffer));
+    EXPECT_EQ(StreamType::kNeedBuffer, stream_->GetNextBuffer(&buffer));
   }
 
   void CheckEOSReached() {
     scoped_refptr<StreamParserBuffer> buffer;
-    EXPECT_EQ(SourceBufferStream::kEndOfStream,
-              stream_->GetNextBuffer(&buffer));
+    EXPECT_EQ(StreamType::kEndOfStream, stream_->GetNextBuffer(&buffer));
   }
 
   void CheckVideoConfig(const VideoDecoderConfig& config) {
@@ -479,7 +488,7 @@
   base::TimeDelta frame_duration() const { return frame_duration_; }
 
   StrictMock<MockMediaLog> media_log_;
-  std::unique_ptr<SourceBufferStream> stream_;
+  std::unique_ptr<StreamType> stream_;
   VideoDecoderConfig video_config_;
   AudioDecoderConfig audio_config_;
   BufferingApi buffering_api_;
@@ -2008,7 +2017,7 @@
   scoped_refptr<StreamParserBuffer> buffer;
 
   // GetNextBuffer() should return the next buffer at position (5 + |bump|).
-  EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess);
+  EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kSuccess);
   EXPECT_EQ(buffer->GetDecodeTimestamp(),
             DecodeTimestamp::FromPresentationTime(5 * frame_duration() + bump));
 
@@ -2023,7 +2032,7 @@
   NewCodedFrameGroupAppend_OffsetFirstBuffer(15, 5, bump);
 
   // GetNextBuffer() should return the next buffer at position (15 + |bump|).
-  EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess);
+  EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kSuccess);
   EXPECT_EQ(buffer->GetDecodeTimestamp(), DecodeTimestamp::FromPresentationTime(
       15 * frame_duration() + bump));
 
@@ -2382,7 +2391,7 @@
   // Check for IBB...BBP pattern.
   for (int i = 0; i < 20; i++) {
     scoped_refptr<StreamParserBuffer> buffer;
-    ASSERT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess);
+    ASSERT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kSuccess);
 
     if (buffer->is_key_frame()) {
       EXPECT_EQ(DecodeTimestamp::FromPresentationTime(buffer->timestamp()),
@@ -3247,13 +3256,13 @@
   // Consume the buffers associated with the initial config.
   scoped_refptr<StreamParserBuffer> buffer;
   for (int i = 0; i < 5; i++) {
-    EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess);
+    EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kSuccess);
     CheckVideoConfig(video_config_);
   }
 
   // Verify the next attempt to get a buffer will signal that a config change
   // has happened.
-  EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kConfigChange);
+  EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kConfigChange);
 
   // Verify that the new config is now returned.
   CheckVideoConfig(new_config);
@@ -3261,7 +3270,7 @@
   // Consume the remaining buffers associated with the new config.
   for (int i = 0; i < 5; i++) {
     CheckVideoConfig(new_config);
-    EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess);
+    EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kSuccess);
   }
 }
 
@@ -3279,7 +3288,7 @@
   CheckVideoConfig(video_config_);
   Seek(5);
   CheckVideoConfig(video_config_);
-  EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kConfigChange);
+  EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kConfigChange);
   CheckVideoConfig(new_config);
   CheckExpectedBuffers(5, 9, &kDataB);
 
@@ -3297,7 +3306,7 @@
   CheckVideoConfig(new_config);
   Seek(0);
   CheckVideoConfig(new_config);
-  EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kConfigChange);
+  EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kConfigChange);
   CheckVideoConfig(video_config_);
   CheckExpectedBuffers(0, 4, &kDataA);
 }
@@ -3759,7 +3768,7 @@
 TEST_P(SourceBufferStreamTest, SameTimestamp_Audio) {
   AudioDecoderConfig config(kCodecMP3, kSampleFormatF32, CHANNEL_LAYOUT_STEREO,
                             44100, EmptyExtraData(), Unencrypted());
-  stream_.reset(new SourceBufferStream(config, &media_log_));
+  stream_.reset(new StreamType(config, &media_log_));
   Seek(0);
   NewCodedFrameGroupAppend("0K 0K 30K 30 60 60");
   CheckExpectedBuffers("0K 0K 30K 30 60 60");
@@ -3770,7 +3779,7 @@
 
   AudioDecoderConfig config(kCodecMP3, kSampleFormatF32, CHANNEL_LAYOUT_STEREO,
                             44100, EmptyExtraData(), Unencrypted());
-  stream_.reset(new SourceBufferStream(config, &media_log_));
+  stream_.reset(new StreamType(config, &media_log_));
   Seek(0);
 
   // Note, in reality, a non-keyframe audio frame is rare or perhaps not
@@ -4247,7 +4256,7 @@
   for (int i = 0; i < 10; i++) {
     // Verify buffer timestamps and durations are preserved and no buffers have
     // discard padding (indicating no splice trimming).
-    EXPECT_EQ(SourceBufferStream::kSuccess, stream_->GetNextBuffer(&buffer));
+    EXPECT_EQ(StreamType::kSuccess, stream_->GetNextBuffer(&buffer));
     EXPECT_EQ(base::TimeDelta::FromMilliseconds(i * 2), buffer->timestamp());
     EXPECT_EQ(base::TimeDelta::FromMilliseconds(2), buffer->duration());
     EXPECT_EQ(kEmptyDiscardPadding, buffer->discard_padding());
@@ -4343,7 +4352,7 @@
   scoped_refptr<StreamParserBuffer> read_buffer;
 
   // Buffer A1 was not spliced, should be unchanged.
-  EXPECT_EQ(SourceBufferStream::kSuccess, stream_->GetNextBuffer(&read_buffer));
+  EXPECT_EQ(StreamType::kSuccess, stream_->GetNextBuffer(&read_buffer));
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(0), read_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, read_buffer->duration());
   EXPECT_EQ(discardA1, read_buffer->discard_padding());
@@ -4351,7 +4360,7 @@
   // Buffer A2 was overlapped by buffer B1 1ms. Splice trimming should trim A2's
   // duration and increase its discard padding by 1ms.
   const base::TimeDelta overlap = base::TimeDelta::FromMilliseconds(1);
-  EXPECT_EQ(SourceBufferStream::kSuccess, stream_->GetNextBuffer(&read_buffer));
+  EXPECT_EQ(StreamType::kSuccess, stream_->GetNextBuffer(&read_buffer));
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(2), read_buffer->timestamp());
   EXPECT_EQ((kDuration / 2) - overlap, read_buffer->duration());
   const DecoderBuffer::DiscardPadding overlap_discard =
@@ -4360,13 +4369,13 @@
 
   // Buffer B1 is overlapping A2, but B1 should be unchanged - splice trimming
   // only modifies the earlier buffer (A1).
-  EXPECT_EQ(SourceBufferStream::kSuccess, stream_->GetNextBuffer(&read_buffer));
+  EXPECT_EQ(StreamType::kSuccess, stream_->GetNextBuffer(&read_buffer));
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(3), read_buffer->timestamp());
   EXPECT_EQ(kDuration / 2, read_buffer->duration());
   EXPECT_EQ(discardB1, read_buffer->discard_padding());
 
   // Buffer B2 is not spliced, should be unchanged.
-  EXPECT_EQ(SourceBufferStream::kSuccess, stream_->GetNextBuffer(&read_buffer));
+  EXPECT_EQ(StreamType::kSuccess, stream_->GetNextBuffer(&read_buffer));
   EXPECT_EQ(base::TimeDelta::FromMilliseconds(5), read_buffer->timestamp());
   EXPECT_EQ(kDuration, read_buffer->duration());
   EXPECT_EQ(std::make_pair(kNoDiscard, kNoDiscard),
@@ -4384,7 +4393,7 @@
   audio_config_.Initialize(kCodecVorbis, kSampleFormatPlanarF32,
                            CHANNEL_LAYOUT_STEREO, 4000, EmptyExtraData(),
                            Unencrypted(), base::TimeDelta(), 0);
-  stream_.reset(new SourceBufferStream(audio_config_, &media_log_));
+  stream_.reset(new StreamType(audio_config_, &media_log_));
   // Equivalent to 0.5ms per frame.
   SetStreamInfo(2000, 2000);
   Seek(0);
@@ -4441,7 +4450,7 @@
   // Verify the next attempt to get a buffer will signal that a config change
   // has happened.
   scoped_refptr<StreamParserBuffer> buffer;
-  EXPECT_EQ(SourceBufferStream::kConfigChange, stream_->GetNextBuffer(&buffer));
+  EXPECT_EQ(StreamType::kConfigChange, stream_->GetNextBuffer(&buffer));
 
   // Verify upcoming buffers will use the new config.
   CheckAudioConfig(new_config);
@@ -4669,7 +4678,7 @@
   CheckVideoConfig(video_config_);
   SeekToTimestampMs(2030);
   CheckVideoConfig(video_config_);
-  EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kConfigChange);
+  EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kConfigChange);
   CheckVideoConfig(new_config);
 
   // Trigger the re-seek.
@@ -4690,11 +4699,11 @@
   SeekToTimestampMs(2000);
   CheckVideoConfig(new_config);
   ASSERT_FALSE(new_config.Matches(video_config_));
-  EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kConfigChange);
+  EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kConfigChange);
   CheckVideoConfig(video_config_);
   CheckExpectedBuffers("2000K 2010 2020D10");
   CheckVideoConfig(video_config_);
-  EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kConfigChange);
+  EXPECT_EQ(stream_->GetNextBuffer(&buffer), StreamType::kConfigChange);
   CheckVideoConfig(new_config);
   CheckExpectedBuffers("2030K 2040 2050D10");
   CheckNoNextBuffer();
@@ -5158,8 +5167,7 @@
                         SourceBufferStreamTest,
                         Values(BufferingApi::kLegacyByDts));
 
-// TODO(wolenetz): Update impl and tests to verify when kMseBufferByPts is
-// enabled. See https://crbug.com/718641. INSTANTIATE_TEST_CASE_P(NewByPts,
-// SourceBufferStreamTest, Values(BufferingApi::kNewByPts));
+// TODO(wolenetz): Update impl and tests to verify both ByDts and ByPts. See
+// https://crbug.com/718641.
 
 }  // namespace media
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 1cdcd58..53ab37b 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -11,7 +11,8 @@
           "--output-format=histograms",
           "--output-format=json-test-results",
           "--browser=release",
-          "--xvfb"
+          "--xvfb",
+          "--also-run-disabled-tests"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "dummy_benchmark.histogram_benchmark_1",
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index bbd96e1..de442077 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -69,6 +69,48 @@
             ]
         }
     ],
+    "AndroidInProductHelpChromeHomeExpand": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Tracking",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:chrome_home_cold_start_iph_trigger;comparator:==0;window:90;storage:360",
+                        "event_used": "name:bottom_sheet_expanded;comparator:any;window:90;storage:360",
+                        "session_rate": "<0"
+                    },
+                    "enable_features": [
+                        "IPH_ChromeHomeExpand"
+                    ]
+                }
+            ]
+        }
+    ],
+    "AndroidInProductHelpDataSaverDetail": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Tracking",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:data_saver_detail_iph_trigger;comparator:==0;window:90;storage:360",
+                        "event_used": "name:data_saver_overview_opened;comparator:<=1;window:90;storage:360",
+                        "session_rate": "<0"
+                    },
+                    "enable_features": [
+                        "IPH_DataSaverDetail"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidInProductHelpDataSaverPreview": [
         {
             "platforms": [
@@ -134,6 +176,28 @@
             ]
         }
     ],
+    "AndroidInProductHelpDownloadPageScreenshot": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Tracking",
+                    "params": {
+                        "availability": ">=14",
+                        "event_1": "name:screenshot_taken_chrome_in_foreground;comparator:>=1;window:135;storage:360",
+                        "event_trigger": "name:download_page_iph_trigger;comparator:==0;window:90;storage:360",
+                        "event_used": "name:download_page_started;comparator:==0;window:135;storage:360",
+                        "session_rate": "<0"
+                    },
+                    "enable_features": [
+                        "IPH_DownloadPageScreenshot"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidSpellChecker": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index e352a18..ce6d73d 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -594,6 +594,8 @@
 
 # ====== DevTools test migration failures until here ======
 
+crbug.com/767269 [ Win ] inspector-protocol/layout-fonts/cjk-ideograph-fallback-by-lang.js [ Pass Failure ]
+
 # Run these tests with under virtual/scalefactor... only.
 crbug.com/567837 fast/hidpi/static [ Skip ]
 
@@ -3051,6 +3053,9 @@
 crbug.com/736056 [ Mac ] external/wpt/encoding/legacy-mb-tchinese [ Timeout Pass ]
 crbug.com/736056 external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-encode-form-errors-hangul.html [ Timeout Pass ]
 
+# Known issue: module script errors are not derteministic
+crbug.com/763597 external/wpt/html/semantics/scripting-1/the-script-element/module/instantiation-error-5.html [ Failure Pass ]
+
 # module script lacks XHTML support
 crbug.com/717643 external/wpt/html/semantics/scripting-1/the-script-element/module/module-in-xhtml.xhtml [ Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/accessibility/text-changes-with-relations.html b/third_party/WebKit/LayoutTests/accessibility/text-changes-with-relations.html
new file mode 100644
index 0000000..6af930d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/accessibility/text-changes-with-relations.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<!DOCTYPE html>
+<html>
+<body>
+  <div>
+    <input id="input">
+    <label id="input-name" for="input">cats</label>
+  </div>
+
+  <div id="main" role="main" aria-labelledby="main-name">
+  </div>
+  <h1 id="main-name">apples</h1>
+
+  <div id="nav" role="navigation" aria-describedby="nav-desc">
+  </div>
+  <h1 id="nav-desc">butter</h1>
+
+</body>
+</html>
+
+<script>
+function axElementById(id) {
+    return accessibilityController.accessibleElementById(id);
+}
+
+test(function(t) {
+    var ax = axElementById("input");
+    assert_equals(ax.name, "cats");
+    document.getElementById("input-name").innerText = "dogs";
+    assert_equals(ax.name, "dogs");
+}, "Changing text in a <label for> changes a name pointed to by @for");
+
+test(function(t) {
+    var ax = axElementById("main");
+    assert_equals(ax.name, "apples");
+    document.getElementById("main-name").innerText = "oranges";
+    assert_equals(ax.name, "oranges");
+}, "Changing text pointed to by aria-labelledby changes the name pointed to by @aria-labelledby");
+
+test(function(t) {
+    var ax = axElementById("nav");
+    assert_equals(ax.description, "butter");
+    document.getElementById("nav-desc").innerText = "margarine";
+    assert_equals(ax.description, "margarine");
+}, "Changing text pointed to by aria-describedby changes the description pointed to by @aria-describedby");
+</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/dont-squash-into-videos-expected.txt b/third_party/WebKit/LayoutTests/compositing/squashing/dont-squash-into-videos-expected.txt
index 65498a7a..15860c5 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/dont-squash-into-videos-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/dont-squash-into-videos-expected.txt
@@ -13,7 +13,7 @@
       "backgroundColor": "#ADD8E6"
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-pre-ready state-no-source'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "bounds": [100, 100]
     },
     {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/dom.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/dom.idl
index b1a959ec..fce74328 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/dom.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/dom.idl
@@ -420,7 +420,7 @@
 
 [Exposed=Window]
 interface CharacterData : Node {
-  [TreatNullAs=EmptyString] attribute DOMString data;
+  attribute [TreatNullAs=EmptyString] DOMString data;
   readonly attribute unsigned long length;
   DOMString substringData(unsigned long offset, unsigned long count);
   void appendData(DOMString data);
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/controls/toggle-class-with-state-source-buffer.html b/third_party/WebKit/LayoutTests/http/tests/media/controls/toggle-class-with-state-source-buffer.html
deleted file mode 100644
index f5618587..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/media/controls/toggle-class-with-state-source-buffer.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<html>
-<title>Test that player buffering is reflected in CSS classes</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../../media-resources/media-controls.js"></script>
-<script src="../../../media-resources/media-file.js"></script>
-<video controls width=400 preload=metadata></video>
-<script>
-async_test(t => {
-  const video = document.querySelector('video');
-  checkControlsClassName(video, "phase-pre-ready state-no-source");
-
-  video.onstalled = t.step_func_done(() => {
-    checkControlsClassName(video, "phase-ready state-buffering");
-  });
-
-  const mediaFile = "../../../media/" + findMediaFile("video", "content/test");
-  const mimeType = mimeTypeForFile(mediaFile);
-  video.src = "http://127.0.0.1:8000/resources/load-and-stall.php?name="
-    + mediaFile + "&mimeType=" + mimeType + "&stallAt=100000&stallFor=8";
-  video.play();
-});
-</script>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/media/controls/toggle-class-with-state-source.html b/third_party/WebKit/LayoutTests/http/tests/media/controls/toggle-class-with-state-source.html
deleted file mode 100644
index 3b42acb..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/media/controls/toggle-class-with-state-source.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!DOCTYPE html>
-<html>
-<title>Test that player source state is reflected in CSS classes.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../../../media-resources/media-controls.js"></script>
-<script src="../../../media-resources/media-file.js"></script>
-<video controls width=400 preload=metadata></video>
-<script>
-async_test(t => {
-  const video = document.querySelector('video');
-  let was_paused = false;
-
-  checkControlsClassName(video, "phase-pre-ready state-no-source");
-
-  video.onstalled = t.step_func(() => {
-    checkControlsClassName(video, "state-loading-metadata");
-  });
-
-  video.onloadeddata = t.step_func(() => {
-    checkControlsClassName(video, "phase-ready state-playing");
-  });
-
-  video.onplaying = t.step_func(() => {
-    if (was_paused) {
-      t.done();
-    } else {
-      video.pause();
-      was_paused = true;
-    }
-  });
-
-  video.onpause = t.step_func(() => {
-    checkControlsClassName(video, "phase-ready state-stopped");
-    video.play();
-  });
-
-  // Use a .webm to ensure metadata is near the beginning of the file.
-  const mediaFile = "resources/media-source/webm/test.webm";
-  video.src = "http://127.0.0.1:8000/media/video-throttled-load.cgi?" +
-      "nph=1&name=" + mediaFile + "&throttle=40&type=video/webm";
-  video.play();
-});
-</script>
-</html>
diff --git a/third_party/WebKit/LayoutTests/media/controls/toggle-class-with-state-source-error.html b/third_party/WebKit/LayoutTests/media/controls/toggle-class-with-state-source-error.html
deleted file mode 100644
index 65b234d..0000000
--- a/third_party/WebKit/LayoutTests/media/controls/toggle-class-with-state-source-error.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!DOCTYPE html>
-<html>
-<title>Test that player state is correctly reflected when there is an error.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../media-controls.js"></script>
-<script src="../media-file.js"></script>
-<video controls width=400></video>
-<script>
-async_test(t => {
-  const video = document.querySelector('video');
-  checkControlsClassName(video, "phase-pre-ready state-no-source");
-
-  video.onerror = t.step_func_done(() => {
-    checkControlsClassName(video, "phase-pre-ready state-no-source");
-  });
-
-  video.src = findMediaFile("video", "../content/missing");
-  // Catch the error promise.
-  video.play().catch(() => {});
-});
-</script>
-</html>
diff --git a/third_party/WebKit/LayoutTests/media/controls/toggle-class-with-state-source-reset.html b/third_party/WebKit/LayoutTests/media/controls/toggle-class-with-state-source-reset.html
deleted file mode 100644
index c499a49..0000000
--- a/third_party/WebKit/LayoutTests/media/controls/toggle-class-with-state-source-reset.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<html>
-<title>Test that player state is correctly reset.</title>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script src="../media-controls.js"></script>
-<script src="../media-file.js"></script>
-<video controls width=400></video>
-<script>
-async_test(t => {
-  const video = document.querySelector('video');
-  let count = 0;
-
-  checkControlsClassName(video, "phase-pre-ready state-no-source");
-
-  video.onplaying = t.step_func(() => {
-    checkControlsClassName(video, "phase-ready state-playing");
-
-    count++;
-    if (count == 2)
-      t.done();
-    else
-      video.pause();
-  });
-
-  video.onpause = t.step_func(() => {
-    checkControlsClassName(video, "phase-ready state-stopped");
-    video.src = findMediaFile("video", "../content/test");
-    video.play();
-  });
-
-  video.src = findMediaFile("video", "../content/counting");
-  video.play();
-});
-</script>
-</html>
diff --git a/third_party/WebKit/LayoutTests/media/media-controls.js b/third_party/WebKit/LayoutTests/media/media-controls.js
index 31c02c6..0483cc0 100644
--- a/third_party/WebKit/LayoutTests/media/media-controls.js
+++ b/third_party/WebKit/LayoutTests/media/media-controls.js
@@ -250,15 +250,3 @@
     return computedStyle.display !== "none" &&
            computedStyle.visibility === "visible";
 }
-
-function checkButtonHasClass(button, className) {
-  assert_true(button.classList.contains(className));
-}
-
-function checkButtonNotHasClass(button, className) {
-  assert_false(button.classList.contains(className));
-}
-
-function checkControlsClassName(videoElement, className) {
-  assert_equals(window.internals.shadowRoot(videoElement).firstChild.className, className);
-}
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index 412791f..c44c8f4 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -239,7 +239,7 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [15, 859],
       "bounds": [150, 60]
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-poster-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-poster-expected.txt
index 756e71e4..b99459a2 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-poster-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/video/video-poster-expected.txt
@@ -47,7 +47,7 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 8],
       "bounds": [352, 288]
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index 87920cd6..b429522 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -239,7 +239,7 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [15, 854],
       "bounds": [150, 60]
     },
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/video-mute-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/video-mute-repaint-expected.txt
index d081c42..e1da66c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/video-mute-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/video-mute-repaint-expected.txt
@@ -24,12 +24,12 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 8],
       "bounds": [700, 525],
       "paintInvalidations": [
         {
-          "object": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+          "object": "LayoutFlexibleBox (relative positioned) DIV",
           "rect": [0, 0, 700, 525],
           "reason": "geometry"
         }
@@ -82,7 +82,7 @@
       "reason": "style change"
     },
     {
-      "object": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "object": "LayoutFlexibleBox (relative positioned) DIV",
       "reason": "geometry"
     },
     {
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/video-unmute-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/video-unmute-repaint-expected.txt
index dd1a664..2298d2f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/video-unmute-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/video-unmute-repaint-expected.txt
@@ -24,12 +24,12 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 8],
       "bounds": [700, 525],
       "paintInvalidations": [
         {
-          "object": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+          "object": "LayoutFlexibleBox (relative positioned) DIV",
           "rect": [0, 0, 700, 525],
           "reason": "geometry"
         }
@@ -82,7 +82,7 @@
       "reason": "style change"
     },
     {
-      "object": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "object": "LayoutFlexibleBox (relative positioned) DIV",
       "reason": "geometry"
     },
     {
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-poster-expected.txt b/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-poster-expected.txt
index 85f6a74e..c189c00 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-poster-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/video/video-poster-expected.txt
@@ -47,7 +47,7 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 8],
       "bounds": [352, 288]
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index d5f203b..705c296 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -239,7 +239,7 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [15, 859],
       "bounds": [150, 60]
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-mute-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-mute-repaint-expected.txt
index fae52f9..fde2ac0 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-mute-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-mute-repaint-expected.txt
@@ -24,12 +24,12 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 8],
       "bounds": [700, 525],
       "paintInvalidations": [
         {
-          "object": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+          "object": "LayoutFlexibleBox (relative positioned) DIV",
           "rect": [0, 0, 700, 525],
           "reason": "geometry"
         }
@@ -82,7 +82,7 @@
       "reason": "style change"
     },
     {
-      "object": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "object": "LayoutFlexibleBox (relative positioned) DIV",
       "reason": "geometry"
     },
     {
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-paint-invalidation-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-paint-invalidation-expected.txt
index 5acc686..ae2ebc7 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-paint-invalidation-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-paint-invalidation-expected.txt
@@ -17,7 +17,7 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 8],
       "bounds": [320, 240]
     },
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-unmute-repaint-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-unmute-repaint-expected.txt
index ee56a55..10eb59f 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-unmute-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/video-unmute-repaint-expected.txt
@@ -24,12 +24,12 @@
       "drawsContent": false
     },
     {
-      "name": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "name": "LayoutFlexibleBox (relative positioned) DIV",
       "position": [8, 8],
       "bounds": [700, 525],
       "paintInvalidations": [
         {
-          "object": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+          "object": "LayoutFlexibleBox (relative positioned) DIV",
           "rect": [0, 0, 700, 525],
           "reason": "geometry"
         }
@@ -82,7 +82,7 @@
       "reason": "style change"
     },
     {
-      "object": "LayoutFlexibleBox (relative positioned) DIV class='phase-ready state-stopped'",
+      "object": "LayoutFlexibleBox (relative positioned) DIV",
       "reason": "geometry"
     },
     {
diff --git a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
index 4fe686c..1d7340d 100644
--- a/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
+++ b/third_party/WebKit/Source/bindings/tests/idls/core/TestObject.idl
@@ -205,8 +205,8 @@
     [RuntimeEnabled=FeatureName] attribute long runtimeEnabledLongAttribute;
     [SetterCallWith=(CurrentWindow,EnteredWindow)] attribute DOMString setterCallWithCurrentWindowAndEnteredWindowStringAttribute;
     [SetterCallWith=ExecutionContext] attribute DOMString setterCallWithExecutionContextStringAttribute;
-    [TreatNullAs=EmptyString] attribute DOMString treatNullAsEmptyStringStringAttribute;
-    [TreatNullAs=NullString] attribute DOMString treatNullAsNullStringStringAttribute;
+    attribute [TreatNullAs=EmptyString] DOMString treatNullAsEmptyStringStringAttribute;
+    attribute [TreatNullAs=NullString] DOMString treatNullAsNullStringStringAttribute;
     [LegacyInterfaceTypeChecking] attribute float legacyInterfaceTypeCheckingFloatAttribute; // nop for non-interface types
     [LegacyInterfaceTypeChecking] attribute TestInterface legacyInterfaceTypeCheckingTestInterfaceAttribute;
     [LegacyInterfaceTypeChecking] attribute TestInterface? legacyInterfaceTypeCheckingTestInterfaceOrNullAttribute;
diff --git a/third_party/WebKit/Source/core/css/CSSCustomFontData.h b/third_party/WebKit/Source/core/css/CSSCustomFontData.h
index 87868fe..df4d3a0 100644
--- a/third_party/WebKit/Source/core/css/CSSCustomFontData.h
+++ b/third_party/WebKit/Source/core/css/CSSCustomFontData.h
@@ -33,7 +33,7 @@
 
   static RefPtr<CSSCustomFontData> Create(RemoteFontFaceSource* source,
                                           FallbackVisibility visibility) {
-    return AdoptRef(new CSSCustomFontData(source, visibility));
+    return WTF::AdoptRef(new CSSCustomFontData(source, visibility));
   }
 
   ~CSSCustomFontData() override {}
diff --git a/third_party/WebKit/Source/core/css/CSSFontFace.h b/third_party/WebKit/Source/core/css/CSSFontFace.h
index 246031e..9b19d96 100644
--- a/third_party/WebKit/Source/core/css/CSSFontFace.h
+++ b/third_party/WebKit/Source/core/css/CSSFontFace.h
@@ -49,7 +49,7 @@
 
  public:
   CSSFontFace(FontFace* font_face, Vector<UnicodeRange>& ranges)
-      : ranges_(AdoptRef(new UnicodeRangeSet(ranges))),
+      : ranges_(WTF::AdoptRef(new UnicodeRangeSet(ranges))),
         segmented_font_face_(nullptr),
         font_face_(font_face) {
     DCHECK(font_face_);
diff --git a/third_party/WebKit/Source/core/css/CSSSegmentedFontFace.cpp b/third_party/WebKit/Source/core/css/CSSSegmentedFontFace.cpp
index b306d2e..0a55bbf 100644
--- a/third_party/WebKit/Source/core/css/CSSSegmentedFontFace.cpp
+++ b/third_party/WebKit/Source/core/css/CSSSegmentedFontFace.cpp
@@ -130,10 +130,10 @@
             (*it)->CssFontFace()->GetFontData(requested_font_description)) {
       DCHECK(!face_font_data->IsSegmented());
       if (face_font_data->IsCustomFont()) {
-        font_data->AppendFace(AdoptRef(new FontDataForRangeSet(
+        font_data->AppendFace(WTF::AdoptRef(new FontDataForRangeSet(
             std::move(face_font_data), (*it)->CssFontFace()->Ranges())));
       } else {
-        font_data->AppendFace(AdoptRef(new FontDataForRangeSetFromCache(
+        font_data->AppendFace(WTF::AdoptRef(new FontDataForRangeSetFromCache(
             std::move(face_font_data), (*it)->CssFontFace()->Ranges())));
       }
     }
diff --git a/third_party/WebKit/Source/core/css/CSSSelector.h b/third_party/WebKit/Source/core/css/CSSSelector.h
index 5463376..90132add 100644
--- a/third_party/WebKit/Source/core/css/CSSSelector.h
+++ b/third_party/WebKit/Source/core/css/CSSSelector.h
@@ -383,7 +383,7 @@
 
   struct RareData : public RefCounted<RareData> {
     static RefPtr<RareData> Create(const AtomicString& value) {
-      return AdoptRef(new RareData(value));
+      return WTF::AdoptRef(new RareData(value));
     }
     ~RareData();
 
diff --git a/third_party/WebKit/Source/core/css/CSSVariableData.h b/third_party/WebKit/Source/core/css/CSSVariableData.h
index 638c5f0..2380cb8 100644
--- a/third_party/WebKit/Source/core/css/CSSVariableData.h
+++ b/third_party/WebKit/Source/core/css/CSSVariableData.h
@@ -25,15 +25,15 @@
   static RefPtr<CSSVariableData> Create(const CSSParserTokenRange& range,
                                         bool is_animation_tainted,
                                         bool needs_variable_resolution) {
-    return AdoptRef(new CSSVariableData(range, is_animation_tainted,
-                                        needs_variable_resolution));
+    return WTF::AdoptRef(new CSSVariableData(range, is_animation_tainted,
+                                             needs_variable_resolution));
   }
 
   static RefPtr<CSSVariableData> CreateResolved(
       const Vector<CSSParserToken>& resolved_tokens,
       Vector<String> backing_strings,
       bool is_animation_tainted) {
-    return AdoptRef(new CSSVariableData(
+    return WTF::AdoptRef(new CSSVariableData(
         resolved_tokens, std::move(backing_strings), is_animation_tainted));
   }
 
diff --git a/third_party/WebKit/Source/core/css/MediaList.h b/third_party/WebKit/Source/core/css/MediaList.h
index 4176987..9a3b208 100644
--- a/third_party/WebKit/Source/core/css/MediaList.h
+++ b/third_party/WebKit/Source/core/css/MediaList.h
@@ -42,7 +42,7 @@
 class CORE_EXPORT MediaQuerySet : public RefCounted<MediaQuerySet> {
  public:
   static RefPtr<MediaQuerySet> Create() {
-    return AdoptRef(new MediaQuerySet());
+    return WTF::AdoptRef(new MediaQuerySet());
   }
   static RefPtr<MediaQuerySet> Create(const String& media_string);
 
@@ -59,7 +59,7 @@
   String MediaText() const;
 
   RefPtr<MediaQuerySet> Copy() const {
-    return AdoptRef(new MediaQuerySet(*this));
+    return WTF::AdoptRef(new MediaQuerySet(*this));
   }
 
  private:
diff --git a/third_party/WebKit/Source/core/css/invalidation/InvalidationSet.h b/third_party/WebKit/Source/core/css/invalidation/InvalidationSet.h
index 39d4a1f..790c8c7 100644
--- a/third_party/WebKit/Source/core/css/invalidation/InvalidationSet.h
+++ b/third_party/WebKit/Source/core/css/invalidation/InvalidationSet.h
@@ -223,7 +223,7 @@
 class CORE_EXPORT DescendantInvalidationSet final : public InvalidationSet {
  public:
   static RefPtr<DescendantInvalidationSet> Create() {
-    return AdoptRef(new DescendantInvalidationSet);
+    return WTF::AdoptRef(new DescendantInvalidationSet);
   }
 
  private:
@@ -234,7 +234,7 @@
  public:
   static RefPtr<SiblingInvalidationSet> Create(
       RefPtr<DescendantInvalidationSet> descendants) {
-    return AdoptRef(new SiblingInvalidationSet(std::move(descendants)));
+    return WTF::AdoptRef(new SiblingInvalidationSet(std::move(descendants)));
   }
 
   unsigned MaxDirectAdjacentSelectors() const {
diff --git a/third_party/WebKit/Source/core/dom/CharacterData.idl b/third_party/WebKit/Source/core/dom/CharacterData.idl
index 5be78883..1d21611 100644
--- a/third_party/WebKit/Source/core/dom/CharacterData.idl
+++ b/third_party/WebKit/Source/core/dom/CharacterData.idl
@@ -20,7 +20,7 @@
 // https://dom.spec.whatwg.org/#interface-characterdata
 
 interface CharacterData : Node {
-    [TreatNullAs=NullString] attribute DOMString data;
+    attribute [TreatNullAs=NullString] DOMString data;
     readonly attribute unsigned long length;
     [RaisesException] DOMString substringData(unsigned long offset, unsigned long count);
     void appendData(DOMString data);
diff --git a/third_party/WebKit/Source/core/dom/QualifiedName.cpp b/third_party/WebKit/Source/core/dom/QualifiedName.cpp
index 70c371f8..1f14f96 100644
--- a/third_party/WebKit/Source/core/dom/QualifiedName.cpp
+++ b/third_party/WebKit/Source/core/dom/QualifiedName.cpp
@@ -81,7 +81,7 @@
   QualifiedNameCache::AddResult add_result =
       GetQualifiedNameCache().AddWithTranslator<QNameComponentsTranslator>(
           data);
-  impl_ = add_result.is_new_entry ? AdoptRef(*add_result.stored_value)
+  impl_ = add_result.is_new_entry ? WTF::AdoptRef(*add_result.stored_value)
                                   : *add_result.stored_value;
 }
 
@@ -93,7 +93,7 @@
   QualifiedNameCache::AddResult add_result =
       GetQualifiedNameCache().AddWithTranslator<QNameComponentsTranslator>(
           data);
-  impl_ = add_result.is_new_entry ? AdoptRef(*add_result.stored_value)
+  impl_ = add_result.is_new_entry ? WTF::AdoptRef(*add_result.stored_value)
                                   : *add_result.stored_value;
 }
 
diff --git a/third_party/WebKit/Source/core/dom/QualifiedName.h b/third_party/WebKit/Source/core/dom/QualifiedName.h
index 2615966..cea2e83 100644
--- a/third_party/WebKit/Source/core/dom/QualifiedName.h
+++ b/third_party/WebKit/Source/core/dom/QualifiedName.h
@@ -58,7 +58,7 @@
                                             const AtomicString& local_name,
                                             const AtomicString& namespace_uri,
                                             bool is_static) {
-      return AdoptRef(
+      return WTF::AdoptRef(
           new QualifiedNameImpl(prefix, local_name, namespace_uri, is_static));
     }
 
diff --git a/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp b/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp
index 47e5ca07..8179784 100644
--- a/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptedIdleTaskController.cpp
@@ -27,7 +27,7 @@
   static RefPtr<IdleRequestCallbackWrapper> Create(
       ScriptedIdleTaskController::CallbackId id,
       ScriptedIdleTaskController* controller) {
-    return AdoptRef(new IdleRequestCallbackWrapper(id, controller));
+    return WTF::AdoptRef(new IdleRequestCallbackWrapper(id, controller));
   }
   virtual ~IdleRequestCallbackWrapper() {}
 
diff --git a/third_party/WebKit/Source/core/dom/SpaceSplitString.cpp b/third_party/WebKit/Source/core/dom/SpaceSplitString.cpp
index 2f335d4..97a1bda 100644
--- a/third_party/WebKit/Source/core/dom/SpaceSplitString.cpp
+++ b/third_party/WebKit/Source/core/dom/SpaceSplitString.cpp
@@ -173,14 +173,14 @@
   Data*& data = SharedDataMap().insert(string, nullptr).stored_value->value;
   if (!data) {
     data = new Data(string);
-    return AdoptRef(data);
+    return WTF::AdoptRef(data);
   }
   return data;
 }
 
 RefPtr<SpaceSplitString::Data> SpaceSplitString::Data::CreateUnique(
     const Data& other) {
-  return AdoptRef(new SpaceSplitString::Data(other));
+  return WTF::AdoptRef(new SpaceSplitString::Data(other));
 }
 
 SpaceSplitString::Data::Data(const AtomicString& string) : key_string_(string) {
diff --git a/third_party/WebKit/Source/core/dom/UserGestureIndicator.cpp b/third_party/WebKit/Source/core/dom/UserGestureIndicator.cpp
index 08244b11..69d5940 100644
--- a/third_party/WebKit/Source/core/dom/UserGestureIndicator.cpp
+++ b/third_party/WebKit/Source/core/dom/UserGestureIndicator.cpp
@@ -106,7 +106,7 @@
 UserGestureIndicator::UserGestureIndicator(UserGestureToken::Status status) {
   if (!IsMainThread())
     return;
-  token_ = AdoptRef(new UserGestureToken(status));
+  token_ = WTF::AdoptRef(new UserGestureToken(status));
   UpdateRootToken();
 }
 
diff --git a/third_party/WebKit/Source/core/editing/FrameCaretTest.cpp b/third_party/WebKit/Source/core/editing/FrameCaretTest.cpp
index 1f1c8f8a..08ef38aa 100644
--- a/third_party/WebKit/Source/core/editing/FrameCaretTest.cpp
+++ b/third_party/WebKit/Source/core/editing/FrameCaretTest.cpp
@@ -39,7 +39,7 @@
 TEST_F(FrameCaretTest, BlinkAfterTyping) {
   FrameCaret& caret = Selection().FrameCaretForTesting();
   RefPtr<scheduler::FakeWebTaskRunner> task_runner =
-      AdoptRef(new scheduler::FakeWebTaskRunner);
+      WTF::AdoptRef(new scheduler::FakeWebTaskRunner);
   task_runner->SetTime(0);
   caret.RecreateCaretBlinkTimerForTesting(task_runner.Get());
   const double kInterval = 10;
diff --git a/third_party/WebKit/Source/core/exported/WebPagePopupImpl.cpp b/third_party/WebKit/Source/core/exported/WebPagePopupImpl.cpp
index a1cdb0e..b7c52bd 100644
--- a/third_party/WebKit/Source/core/exported/WebPagePopupImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebPagePopupImpl.cpp
@@ -594,7 +594,7 @@
   //    WebPagePopupImpl to close.
   // We need them because the closing operation is asynchronous and the widget
   // can be closed while the WebViewImpl is unaware of it.
-  return AdoptRef(new WebPagePopupImpl(client)).LeakRef();
+  return WTF::AdoptRef(new WebPagePopupImpl(client)).LeakRef();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/exported/WebViewImpl.cpp b/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
index c791f34..3ed8a5a 100644
--- a/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebViewImpl.cpp
@@ -301,7 +301,7 @@
 WebViewImpl* WebViewImpl::Create(WebViewClient* client,
                                  WebPageVisibilityState visibility_state) {
   // Pass the WebViewImpl's self-reference to the caller.
-  return AdoptRef(new WebViewImpl(client, visibility_state)).LeakRef();
+  return WTF::AdoptRef(new WebViewImpl(client, visibility_state)).LeakRef();
 }
 
 void WebView::UpdateVisitedLinkState(unsigned long long link_hash) {
diff --git a/third_party/WebKit/Source/core/html/HTMLDocument.idl b/third_party/WebKit/Source/core/html/HTMLDocument.idl
index fd43bb1..7a49a53 100644
--- a/third_party/WebKit/Source/core/html/HTMLDocument.idl
+++ b/third_party/WebKit/Source/core/html/HTMLDocument.idl
@@ -26,11 +26,11 @@
 interface HTMLDocument : Document {
     // https://html.spec.whatwg.org/#Document-partial
 
-    [TreatNullAs=EmptyString, CEReactions, CustomElementCallbacks] attribute DOMString fgColor;
-    [TreatNullAs=EmptyString, CEReactions, CustomElementCallbacks] attribute DOMString linkColor;
-    [TreatNullAs=EmptyString, CEReactions, CustomElementCallbacks] attribute DOMString vlinkColor;
-    [TreatNullAs=EmptyString, CEReactions, CustomElementCallbacks] attribute DOMString alinkColor;
-    [TreatNullAs=EmptyString, CEReactions, CustomElementCallbacks] attribute DOMString bgColor;
+    [CEReactions, CustomElementCallbacks] attribute [TreatNullAs=EmptyString] DOMString fgColor;
+    [CEReactions, CustomElementCallbacks] attribute [TreatNullAs=EmptyString] DOMString linkColor;
+    [CEReactions, CustomElementCallbacks] attribute [TreatNullAs=EmptyString] DOMString vlinkColor;
+    [CEReactions, CustomElementCallbacks] attribute [TreatNullAs=EmptyString] DOMString alinkColor;
+    [CEReactions, CustomElementCallbacks] attribute [TreatNullAs=EmptyString] DOMString bgColor;
 
     [MeasureAs=DocumentClear] void clear();
     [MeasureAs=DocumentCaptureEvents] void captureEvents();
diff --git a/third_party/WebKit/Source/core/html/HTMLElement.idl b/third_party/WebKit/Source/core/html/HTMLElement.idl
index af3f8f5..de97f7e 100644
--- a/third_party/WebKit/Source/core/html/HTMLElement.idl
+++ b/third_party/WebKit/Source/core/html/HTMLElement.idl
@@ -58,8 +58,8 @@
     [SameObject, PerWorldBindings, PutForwards=cssText] readonly attribute CSSStyleDeclaration style;
 
     // Non-standard APIs
-    [TreatNullAs=NullString, CEReactions, CustomElementCallbacks, RaisesException=Setter, MeasureAs=HTMLElementInnerText] attribute DOMString innerText;
-    [TreatNullAs=NullString, CEReactions, CustomElementCallbacks, RaisesException=Setter, MeasureAs=HTMLElementOuterText] attribute DOMString outerText;
+    [CEReactions, CustomElementCallbacks, RaisesException=Setter, MeasureAs=HTMLElementInnerText] attribute [TreatNullAs=NullString] DOMString innerText;
+    [CEReactions, CustomElementCallbacks, RaisesException=Setter, MeasureAs=HTMLElementOuterText] attribute [TreatNullAs=NullString] DOMString outerText;
 };
 
 HTMLElement implements GlobalEventHandlers;
diff --git a/third_party/WebKit/Source/core/html/forms/FileChooser.cpp b/third_party/WebKit/Source/core/html/forms/FileChooser.cpp
index 37275588..d97509d 100644
--- a/third_party/WebKit/Source/core/html/forms/FileChooser.cpp
+++ b/third_party/WebKit/Source/core/html/forms/FileChooser.cpp
@@ -47,7 +47,7 @@
 
 RefPtr<FileChooser> FileChooser::Create(FileChooserClient* client,
                                         const WebFileChooserParams& params) {
-  return AdoptRef(new FileChooser(client, params));
+  return WTF::AdoptRef(new FileChooser(client, params));
 }
 
 FileChooser::~FileChooser() {}
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.cpp b/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.cpp
index c890617..387806bd 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLParserReentryPermit.cpp
@@ -7,7 +7,7 @@
 namespace blink {
 
 RefPtr<HTMLParserReentryPermit> HTMLParserReentryPermit::Create() {
-  return AdoptRef(new HTMLParserReentryPermit());
+  return WTF::AdoptRef(new HTMLParserReentryPermit());
 }
 
 HTMLParserReentryPermit::HTMLParserReentryPermit()
diff --git a/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.h b/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.h
index b7080a4..d1dbf16f 100644
--- a/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.h
+++ b/third_party/WebKit/Source/core/html/parser/TokenizedChunkQueue.h
@@ -26,7 +26,7 @@
 class TokenizedChunkQueue : public ThreadSafeRefCounted<TokenizedChunkQueue> {
  public:
   static RefPtr<TokenizedChunkQueue> Create() {
-    return AdoptRef(new TokenizedChunkQueue);
+    return WTF::AdoptRef(new TokenizedChunkQueue);
   }
 
   ~TokenizedChunkQueue();
diff --git a/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
index 175b8e0a..6b0b88c 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorLayerTreeAgent.cpp
@@ -453,7 +453,7 @@
   GraphicsContext context(layer->GetPaintController());
   context.BeginRecording(interest_rect);
   layer->GetPaintController().GetPaintArtifact().Replay(interest_rect, context);
-  RefPtr<PictureSnapshot> snapshot = AdoptRef(
+  RefPtr<PictureSnapshot> snapshot = WTF::AdoptRef(
       new PictureSnapshot(ToSkPicture(context.EndRecording(), interest_rect)));
 
   *snapshot_id = String::Number(++last_snapshot_id_);
@@ -471,7 +471,7 @@
   decoded_tiles.Grow(tiles->length());
   for (size_t i = 0; i < tiles->length(); ++i) {
     protocol::LayerTree::PictureTile* tile = tiles->get(i);
-    decoded_tiles[i] = AdoptRef(new PictureSnapshot::TilePictureStream());
+    decoded_tiles[i] = WTF::AdoptRef(new PictureSnapshot::TilePictureStream());
     decoded_tiles[i]->layer_offset.Set(tile->getX(), tile->getY());
     if (!Base64Decode(tile->getPicture(), decoded_tiles[i]->data))
       return Response::Error("Invalid base64 encoding");
diff --git a/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
index aff11351..fd28bf7e 100644
--- a/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
@@ -108,7 +108,7 @@
     : public ThreadSafeRefCounted<WaitableEventWithTasks> {
  public:
   static RefPtr<WaitableEventWithTasks> Create() {
-    return AdoptRef(new WaitableEventWithTasks);
+    return WTF::AdoptRef(new WaitableEventWithTasks);
   }
 
   void Signal() {
diff --git a/third_party/WebKit/Source/core/paint/ClipRects.h b/third_party/WebKit/Source/core/paint/ClipRects.h
index 35efcfef..4f028e6 100644
--- a/third_party/WebKit/Source/core/paint/ClipRects.h
+++ b/third_party/WebKit/Source/core/paint/ClipRects.h
@@ -35,9 +35,9 @@
   USING_FAST_MALLOC(ClipRects);
 
  public:
-  static RefPtr<ClipRects> Create() { return AdoptRef(new ClipRects); }
+  static RefPtr<ClipRects> Create() { return WTF::AdoptRef(new ClipRects); }
   static RefPtr<ClipRects> Create(const ClipRects& other) {
-    return AdoptRef(new ClipRects(other));
+    return WTF::AdoptRef(new ClipRects(other));
   }
 
   ClipRects() : fixed_(0) {}
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImage.h b/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
index 1d7f269..d7cc658 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImage.h
@@ -59,7 +59,7 @@
  public:
   static RefPtr<SVGImage> Create(ImageObserver* observer,
                                  bool is_multipart = false) {
-    return AdoptRef(new SVGImage(observer, is_multipart));
+    return WTF::AdoptRef(new SVGImage(observer, is_multipart));
   }
 
   static bool IsInSVGImage(const Node*);
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h b/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
index c509236..294e08a7 100644
--- a/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
+++ b/third_party/WebKit/Source/core/svg/graphics/SVGImageForContainer.h
@@ -64,8 +64,8 @@
                                              const KURL& url) {
     FloatSize container_size_without_zoom(container_size);
     container_size_without_zoom.Scale(1 / zoom);
-    return AdoptRef(new SVGImageForContainer(image, container_size_without_zoom,
-                                             zoom, url));
+    return WTF::AdoptRef(new SVGImageForContainer(
+        image, container_size_without_zoom, zoom, url));
   }
 
   IntSize Size() const override;
diff --git a/third_party/WebKit/Source/core/testing/Internals.cpp b/third_party/WebKit/Source/core/testing/Internals.cpp
index 6dc3709..9bc296bb 100644
--- a/third_party/WebKit/Source/core/testing/Internals.cpp
+++ b/third_party/WebKit/Source/core/testing/Internals.cpp
@@ -2092,7 +2092,8 @@
 }
 
 void Internals::setMockHyphenation(const AtomicString& locale) {
-  LayoutLocale::SetHyphenationForTesting(locale, AdoptRef(new MockHyphenation));
+  LayoutLocale::SetHyphenationForTesting(locale,
+                                         WTF::AdoptRef(new MockHyphenation));
 }
 
 bool Internals::isOverwriteModeEnabled(Document* document) {
diff --git a/third_party/WebKit/Source/core/typed_arrays/DOMDataView.cpp b/third_party/WebKit/Source/core/typed_arrays/DOMDataView.cpp
index ad3b3dd..5dc48d15 100644
--- a/third_party/WebKit/Source/core/typed_arrays/DOMDataView.cpp
+++ b/third_party/WebKit/Source/core/typed_arrays/DOMDataView.cpp
@@ -21,7 +21,7 @@
     CheckedNumeric<uint32_t> checked_max = byte_offset;
     checked_max += byte_length;
     CHECK_LE(checked_max.ValueOrDie(), buffer->ByteLength());
-    return AdoptRef(new DataView(buffer, byte_offset, byte_length));
+    return WTF::AdoptRef(new DataView(buffer, byte_offset, byte_length));
   }
 
   unsigned ByteLength() const override { return byte_length_; }
diff --git a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.h b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.h
index be7a17d..c38d486 100644
--- a/third_party/WebKit/Source/core/workers/WorkerScriptLoader.h
+++ b/third_party/WebKit/Source/core/workers/WorkerScriptLoader.h
@@ -57,7 +57,7 @@
 
  public:
   static RefPtr<WorkerScriptLoader> Create() {
-    return AdoptRef(new WorkerScriptLoader());
+    return WTF::AdoptRef(new WorkerScriptLoader());
   }
 
   void LoadSynchronously(ExecutionContext&,
diff --git a/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp b/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp
index 090fc826e..e8dc5e17 100644
--- a/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp
+++ b/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp
@@ -692,7 +692,7 @@
   xmlCtxtUseOptions(parser, XML_PARSE_HUGE);
   parser->_private = user_data;
   parser->replaceEntities = true;
-  return AdoptRef(new XMLParserContext(parser));
+  return WTF::AdoptRef(new XMLParserContext(parser));
 }
 
 // Chunk should be encoded in UTF-8
@@ -728,7 +728,7 @@
   parser->str_xml_ns = xmlDictLookup(parser->dict, XML_XML_NAMESPACE, 36);
   parser->_private = user_data;
 
-  return AdoptRef(new XMLParserContext(parser));
+  return WTF::AdoptRef(new XMLParserContext(parser));
 }
 
 // --------------------------------
diff --git a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
index 50da8a1..435ce43 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp
@@ -1338,11 +1338,6 @@
                                        describedby);
 }
 
-void AXLayoutObject::AriaLabelledbyElements(AXObjectVector& labelledby) const {
-  AccessibilityChildrenFromAOMProperty(AOMRelationListProperty::kLabeledBy,
-                                       labelledby);
-}
-
 bool AXLayoutObject::AriaHasPopup() const {
   const AtomicString& has_popup =
       GetAOMPropertyOrARIAAttribute(AOMStringProperty::kHasPopUp);
diff --git a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.h b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.h
index 4bee3a1..b7408d1a 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXLayoutObject.h
@@ -118,7 +118,6 @@
 
   // ARIA attributes.
   void AriaDescribedbyElements(AXObjectVector&) const override;
-  void AriaLabelledbyElements(AXObjectVector&) const override;
   void AriaOwnsElements(AXObjectVector&) const override;
 
   bool AriaHasPopup() const override;
diff --git a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
index fb7a520..2874bfe 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXNodeObject.cpp
@@ -2144,8 +2144,9 @@
 
   // Step 2B from: http://www.w3.org/TR/accname-aam-1.1
   HeapVector<Member<Element>> elements;
-  AriaLabelledbyElementVector(elements);
-  if (elements.size() > 0)
+  Vector<String> ids;
+  AriaLabelledbyElementVector(elements, ids);
+  if (ids.size() > 0)
     return false;
 
   // Step 2C from: http://www.w3.org/TR/accname-aam-1.1
@@ -3248,7 +3249,9 @@
     if (description_sources)
       description_sources->back().attribute_value = aria_describedby;
 
-    description = TextFromAriaDescribedby(related_objects);
+    Vector<String> ids;
+    description = TextFromAriaDescribedby(related_objects, ids);
+    AxObjectCache().UpdateReverseRelations(this, ids);
 
     if (!description.IsNull()) {
       if (description_sources) {
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
index 536c4f3..af99eac 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp
@@ -1233,8 +1233,11 @@
         // the set of visited objects is preserved unmodified for future
         // calculations.
         AXObjectSet visited_copy = visited;
+        Vector<String> ids;
         text_alternative =
-            TextFromAriaLabelledby(visited_copy, related_objects);
+            TextFromAriaLabelledby(visited_copy, related_objects, ids);
+        if (!ids.IsEmpty())
+          AxObjectCache().UpdateReverseRelations(this, ids);
         if (!text_alternative.IsNull()) {
           if (name_sources) {
             NameSource& source = name_sources->back();
@@ -1327,8 +1330,8 @@
 }
 
 void AXObject::ElementsFromAttribute(HeapVector<Member<Element>>& elements,
-                                     const QualifiedName& attribute) const {
-  Vector<String> ids;
+                                     const QualifiedName& attribute,
+                                     Vector<String>& ids) const {
   TokenVectorFromAttribute(ids, attribute);
   if (ids.IsEmpty())
     return;
@@ -1341,26 +1344,27 @@
 }
 
 void AXObject::AriaLabelledbyElementVector(
-    HeapVector<Member<Element>>& elements) const {
+    HeapVector<Member<Element>>& elements,
+    Vector<String>& ids) const {
   // Try both spellings, but prefer aria-labelledby, which is the official spec.
-  ElementsFromAttribute(elements, aria_labelledbyAttr);
-  if (!elements.size())
-    ElementsFromAttribute(elements, aria_labeledbyAttr);
+  ElementsFromAttribute(elements, aria_labelledbyAttr, ids);
+  if (!ids.size())
+    ElementsFromAttribute(elements, aria_labeledbyAttr, ids);
 }
 
-String AXObject::TextFromAriaLabelledby(
-    AXObjectSet& visited,
-    AXRelatedObjectVector* related_objects) const {
+String AXObject::TextFromAriaLabelledby(AXObjectSet& visited,
+                                        AXRelatedObjectVector* related_objects,
+                                        Vector<String>& ids) const {
   HeapVector<Member<Element>> elements;
-  AriaLabelledbyElementVector(elements);
+  AriaLabelledbyElementVector(elements, ids);
   return TextFromElements(true, visited, elements, related_objects);
 }
 
-String AXObject::TextFromAriaDescribedby(
-    AXRelatedObjectVector* related_objects) const {
+String AXObject::TextFromAriaDescribedby(AXRelatedObjectVector* related_objects,
+                                         Vector<String>& ids) const {
   AXObjectSet visited;
   HeapVector<Member<Element>> elements;
-  ElementsFromAttribute(elements, aria_describedbyAttr);
+  ElementsFromAttribute(elements, aria_describedbyAttr, ids);
   return TextFromElements(true, visited, elements, related_objects);
 }
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.h b/third_party/WebKit/Source/modules/accessibility/AXObject.h
index d54d8eb..35b1025 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.h
@@ -369,6 +369,8 @@
   bool HasAOMPropertyOrARIAAttribute(AOMStringProperty,
                                      AtomicString& result) const;
 
+  void TokenVectorFromAttribute(Vector<String>&, const QualifiedName&) const;
+
   virtual void GetSparseAXAttributes(AXSparseAttributeClient&) const {}
 
   // Determine subclass type.
@@ -642,7 +644,6 @@
   virtual String AriaAutoComplete() const { return String(); }
   virtual void AriaOwnsElements(AXObjectVector& owns) const {}
   virtual void AriaDescribedbyElements(AXObjectVector&) const {}
-  virtual void AriaLabelledbyElements(AXObjectVector&) const {}
   virtual bool AriaHasPopup() const { return false; }
   virtual bool IsEditable() const { return false; }
   bool IsEditableRoot() const;
@@ -863,13 +864,16 @@
                           AXObjectSet& visited,
                           HeapVector<Member<Element>>& elements,
                           AXRelatedObjectVector* related_objects) const;
-  void TokenVectorFromAttribute(Vector<String>&, const QualifiedName&) const;
   void ElementsFromAttribute(HeapVector<Member<Element>>& elements,
-                             const QualifiedName&) const;
-  void AriaLabelledbyElementVector(HeapVector<Member<Element>>& elements) const;
+                             const QualifiedName&,
+                             Vector<String>& ids) const;
+  void AriaLabelledbyElementVector(HeapVector<Member<Element>>& elements,
+                                   Vector<String>& ids) const;
   String TextFromAriaLabelledby(AXObjectSet& visited,
-                                AXRelatedObjectVector* related_objects) const;
-  String TextFromAriaDescribedby(AXRelatedObjectVector* related_objects) const;
+                                AXRelatedObjectVector* related_objects,
+                                Vector<String>& ids) const;
+  String TextFromAriaDescribedby(AXRelatedObjectVector* related_objects,
+                                 Vector<String>& ids) const;
   virtual const AXObject* InheritsPresentationalRoleFrom() const { return 0; }
 
   bool CanReceiveAccessibilityFocus() const;
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
index 0af35679..551040f 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.cpp
@@ -434,8 +434,7 @@
   new_obj->Init();
   new_obj->SetLastKnownIsIgnoredValue(new_obj->AccessibilityIsIgnored());
 
-  if (node->IsElementNode())
-    relation_cache_->UpdateTreeIfElementIdIsAriaOwned(ToElement(node));
+  relation_cache_->UpdateRelatedTree(node);
 
   return new_obj;
 }
@@ -644,19 +643,29 @@
     nearestAncestor->SelectionChanged();
 }
 
+void AXObjectCacheImpl::UpdateReverseRelations(
+    const AXObject* relation_source,
+    const Vector<String>& target_ids) {
+  relation_cache_->UpdateReverseRelations(relation_source, target_ids);
+}
+
 void AXObjectCacheImpl::TextChanged(Node* node) {
-  TextChanged(Get(node));
+  TextChanged(Get(node), node);
 }
 
 void AXObjectCacheImpl::TextChanged(LayoutObject* layout_object) {
-  TextChanged(Get(layout_object));
+  if (layout_object)
+    TextChanged(Get(layout_object), layout_object->GetNode());
 }
 
-void AXObjectCacheImpl::TextChanged(AXObject* obj) {
-  if (!obj)
-    return;
+void AXObjectCacheImpl::TextChanged(AXObject* obj,
+                                    Node* node_for_relation_update) {
+  if (obj)
+    obj->TextChanged();
 
-  obj->TextChanged();
+  if (node_for_relation_update)
+    relation_cache_->UpdateRelatedTree(node_for_relation_update);
+
   PostNotification(obj, AXObjectCacheImpl::kAXTextChanged);
 }
 
@@ -665,27 +674,33 @@
   // we need an AXLayoutObject, because it was reparented to a location outside
   // of a canvas.
   Get(node);
-  if (node->IsElementNode())
-    relation_cache_->UpdateTreeIfElementIdIsAriaOwned(ToElement(node));
+  relation_cache_->UpdateRelatedTree(node);
 }
 
 void AXObjectCacheImpl::ChildrenChanged(Node* node) {
-  ChildrenChanged(Get(node));
+  ChildrenChanged(Get(node), node);
 }
 
 void AXObjectCacheImpl::ChildrenChanged(LayoutObject* layout_object) {
-  ChildrenChanged(Get(layout_object));
+  if (layout_object) {
+    AXObject* object = Get(layout_object);
+    ChildrenChanged(object, layout_object->GetNode());
+  }
 }
 
 void AXObjectCacheImpl::ChildrenChanged(AccessibleNode* accessible_node) {
-  ChildrenChanged(Get(accessible_node));
+  AXObject* object = Get(accessible_node);
+  if (object)
+    ChildrenChanged(object, object->GetNode());
 }
 
-void AXObjectCacheImpl::ChildrenChanged(AXObject* obj) {
-  if (!obj)
-    return;
+void AXObjectCacheImpl::ChildrenChanged(AXObject* obj,
+                                        Node* node_for_relation_update) {
+  if (obj)
+    obj->ChildrenChanged();
 
-  obj->ChildrenChanged();
+  if (node_for_relation_update)
+    relation_cache_->UpdateRelatedTree(node_for_relation_update);
 }
 
 void AXObjectCacheImpl::NotificationPostTimerFired(TimerBase*) {
@@ -865,11 +880,12 @@
   else if (attr_name == forAttr && isHTMLLabelElement(*element))
     LabelChanged(element);
   else if (attr_name == idAttr)
-    relation_cache_->UpdateTreeIfElementIdIsAriaOwned(element);
+    relation_cache_->UpdateRelatedTree(element);
 
   if (!attr_name.LocalName().StartsWith("aria-"))
     return;
 
+  // Perform updates specific to each attribute.
   if (attr_name == aria_activedescendantAttr)
     HandleActiveDescendantChanged(element);
   else if (attr_name == aria_valuenowAttr || attr_name == aria_valuetextAttr)
@@ -877,6 +893,8 @@
   else if (attr_name == aria_labelAttr || attr_name == aria_labeledbyAttr ||
            attr_name == aria_labelledbyAttr)
     TextChanged(element);
+  else if (attr_name == aria_describedbyAttr)
+    TextChanged(element);  // TODO do we need a DescriptionChanged() ?
   else if (attr_name == aria_checkedAttr || attr_name == aria_pressedAttr)
     CheckedStateChanged(element);
   else if (attr_name == aria_selectedAttr)
@@ -894,6 +912,7 @@
 }
 
 void AXObjectCacheImpl::LabelChanged(Element* element) {
+  DCHECK(isHTMLLabelElement(element));
   TextChanged(toHTMLLabelElement(element)->control());
 }
 
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h
index 2f1cb91..40006ac3 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXObjectCacheImpl.h
@@ -67,6 +67,8 @@
   void Dispose() override;
 
   void SelectionChanged(Node*) override;
+  void UpdateReverseRelations(const AXObject* relation_source,
+                              const Vector<String>& target_ids);
   void ChildrenChanged(Node*) override;
   void ChildrenChanged(LayoutObject*) override;
   void ChildrenChanged(AccessibleNode*) override;
@@ -86,7 +88,7 @@
   // Called by a node when text or a text equivalent (e.g. alt) attribute is
   // changed.
   void TextChanged(LayoutObject*) override;
-  void TextChanged(AXObject*);
+  void TextChanged(AXObject*, Node* node_for_relation_update = nullptr);
   // Called when a node has just been attached, so we can make sure we have the
   // right subclass of AXObject.
   void UpdateCacheAfterNodeIsAttached(Node*) override;
@@ -149,7 +151,7 @@
 
   void Remove(AXID);
 
-  void ChildrenChanged(AXObject*);
+  void ChildrenChanged(AXObject*, Node* node_for_relation_update = nullptr);
 
   void HandleActiveDescendantChanged(Node*);
   void HandleAriaRoleChanged(Node*);
diff --git a/third_party/WebKit/Source/modules/accessibility/AXRelationCache.cpp b/third_party/WebKit/Source/modules/accessibility/AXRelationCache.cpp
index d01cc36..9fcb057 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXRelationCache.cpp
+++ b/third_party/WebKit/Source/modules/accessibility/AXRelationCache.cpp
@@ -27,6 +27,8 @@
  */
 
 #include "modules/accessibility/AXRelationCache.h"
+#include "core/html/HTMLLabelElement.h"
+#include "core/html/LabelableElement.h"
 
 namespace blink {
 
@@ -44,47 +46,20 @@
       aria_owned_child_to_owner_mapping_.at(child->AxObjectID()));
 }
 
-// relation_source is related to add_ids, and no longer related to remove_ids
-static void UpdateReverseRelationMap(
-    HashMap<String, std::unique_ptr<HashSet<AXID>>>& reverse_map,
-    AXID relation_source,
-    HashSet<String>& add_ids,
-    HashSet<String>& remove_ids) {
+// Update reverse relation map, where relation_source is related to target_ids.
+void AXRelationCache::UpdateReverseRelations(const AXObject* relation_source,
+                                             const Vector<String>& target_ids) {
+  AXID relation_source_axid = relation_source->AxObjectID();
+
   // Add entries to reverse map.
-  // Vector<String> id_vector;
-  // CopyToVector(add_ids, id_vector);
-  for (const String& id : add_ids) {
-    HashSet<AXID>* axids = reverse_map.at(id);
-    if (!axids) {
-      axids = new HashSet<AXID>();
-      reverse_map.Set(id, WTF::WrapUnique(axids));
+  for (const String& target_id : target_ids) {
+    HashSet<AXID>* source_axids = id_attr_to_related_mapping_.at(target_id);
+    if (!source_axids) {
+      source_axids = new HashSet<AXID>();
+      id_attr_to_related_mapping_.Set(target_id, WTF::WrapUnique(source_axids));
     }
-    axids->insert(relation_source);
+    source_axids->insert(relation_source_axid);
   }
-
-  // Remove entries from reverse map that are no longer needed.
-  for (const String& id : remove_ids) {
-    HashSet<AXID>* axids = reverse_map.at(id);
-    if (axids) {
-      axids->erase(relation_source);
-      if (axids->IsEmpty())
-        reverse_map.erase(id);
-    }
-  }
-}
-
-static HashSet<String> CopyToSet(const Vector<String>& source) {
-  HashSet<String> dest;
-  for (const String& entry : source)
-    dest.insert(entry);
-  return dest;
-}
-
-static HashSet<String> SubtractSet(const HashSet<String>& set1,
-                                   const HashSet<String>& set2) {
-  HashSet<String> dest(set1);
-  dest.RemoveAll(set2);
-  return dest;
 }
 
 static bool ContainsCycle(AXObject* owner, AXObject* child) {
@@ -179,28 +154,11 @@
     const AXObject* owner,
     const Vector<String>& owned_id_vector,
     HeapVector<Member<AXObject>>& validated_owned_children_result) {
-  //
-  // Update the map from the AXID of this element to the ids of the owned
-  // children, and the reverse map from ids to possible AXID owners.
-  //
-
-  HashSet<String> current_owned_ids(
-      aria_owner_to_ids_mapping_.at(owner->AxObjectID()));
-  HashSet<String> all_new_owned_ids(CopyToSet(owned_id_vector));
-  HashSet<String> add_ids(SubtractSet(all_new_owned_ids, current_owned_ids));
-  HashSet<String> remove_ids(SubtractSet(current_owned_ids, all_new_owned_ids));
-
-  // Update the maps if necessary (aria_owner_to_ids_mapping_).
-  if (!add_ids.IsEmpty() || !remove_ids.IsEmpty()) {
-    // Update reverse map.
-    UpdateReverseRelationMap(id_to_aria_owners_mapping_, owner->AxObjectID(),
-                             add_ids, remove_ids);
-    // Update forward map.
-    aria_owner_to_ids_mapping_.Set(owner->AxObjectID(), all_new_owned_ids);
-  }
+  // Track reverse relations for future tree updates.
+  UpdateReverseRelations(owner, owned_id_vector);
 
   //
-  // Now figure out the ids that actually correspond to children that exist
+  // Figure out the ids that actually correspond to children that exist
   // and that we can legally own (not cyclical, not already owned, etc.) and
   // update the maps and |validated_owned_children_result| based on that.
   //
@@ -235,35 +193,73 @@
                                       validated_owned_child_axids);
 }
 
-void AXRelationCache::UpdateTreeIfElementIdIsAriaOwned(Element* element) {
+// Return target AXObject and fill source_objects with AXObjects for
+// relations pointing to target.
+AXObject* AXRelationCache::GetReverseRelated(
+    Node* target,
+    HeapVector<Member<AXObject>>& source_objects) {
+  if (!target || !target->IsElementNode())
+    return nullptr;
+
+  Element* element = ToElement(target);
   if (!element->HasID())
-    return;
+    return nullptr;
 
   String id = element->GetIdAttribute();
-  HashSet<AXID>* owners = id_to_aria_owners_mapping_.at(id);
-  if (!owners)
-    return;
+  HashSet<AXID>* source_axids = id_attr_to_related_mapping_.at(id);
+  if (!source_axids)
+    return nullptr;
 
-  AXObject* ax_element = GetOrCreate(element);
-  if (!ax_element)
-    return;
-
-  // If it's already owned, call childrenChanged on the owner to make sure
-  // it's still an owner.
-  if (IsAriaOwned(ax_element)) {
-    AXObject* owned_parent = GetAriaOwnedParent(ax_element);
-    DCHECK(owned_parent);
-    ChildrenChanged(owned_parent);
-    return;
+  // Not safe to call GetOrCreate() as this method is called during layout
+  // changes such as AttributeChanged().
+  AXObject* ax_element = Get(element);
+  for (const auto& source_axid : *source_axids) {
+    AXObject* source_object = ObjectFromAXID(source_axid);
+    if (source_object)
+      source_objects.push_back(source_object);
   }
 
-  // If it's not already owned, check the possible owners based on our mapping
-  // from ids to elements that have that id listed in their aria-owns
-  // attribute.
-  for (const auto& ax_id : *owners) {
-    AXObject* owner = ObjectFromAXID(ax_id);
-    if (owner)
-      ChildrenChanged(owner);
+  return ax_element;
+}
+
+void AXRelationCache::UpdateRelatedTree(Node* node) {
+  HeapVector<Member<AXObject>> related_sources;
+  AXObject* related_target = GetReverseRelated(node, related_sources);
+  // If it's already owned, call childrenChanged on the owner to make sure
+  // it's still an owner.
+  if (related_target && IsAriaOwned(related_target)) {
+    AXObject* owned_parent = GetAriaOwnedParent(related_target);
+    DCHECK(owned_parent);
+    ChildrenChanged(owned_parent);
+  }
+
+  // Ensure children are updated if there is a change.
+  for (AXObject* related : related_sources) {
+    if (related) {
+      ChildrenChanged(related);
+    }
+  }
+
+  UpdateRelatedText(node);
+}
+
+void AXRelationCache::UpdateRelatedText(Node* node) {
+  // Walk up ancestor chain from node and refresh text of any related content.
+  while (node) {
+    // Reverse relations via aria-labelledby, aria-describedby, aria-owns.
+    HeapVector<Member<AXObject>> related_sources;
+    if (GetReverseRelated(node, related_sources)) {
+      for (AXObject* related : related_sources) {
+        if (related)
+          TextChanged(related);
+      }
+    }
+
+    // Forward relation via <label for="[id]">.
+    if (isHTMLLabelElement(*node))
+      LabelChanged(node);
+
+    node = node->parentNode();
   }
 }
 
@@ -276,13 +272,16 @@
   }
   aria_owned_child_to_owner_mapping_.erase(obj_id);
   aria_owned_child_to_real_parent_mapping_.erase(obj_id);
-  aria_owner_to_ids_mapping_.erase(obj_id);
 }
 
 AXObject* AXRelationCache::ObjectFromAXID(AXID axid) const {
   return object_cache_->ObjectFromAXID(axid);
 }
 
+AXObject* AXRelationCache::Get(Node* node) {
+  return object_cache_->Get(node);
+}
+
 AXObject* AXRelationCache::GetOrCreate(Node* node) {
   return object_cache_->GetOrCreate(node);
 }
@@ -291,4 +290,15 @@
   object_cache_->ChildrenChanged(object);
 }
 
+void AXRelationCache::TextChanged(AXObject* object) {
+  object_cache_->TextChanged(object);
+}
+
+void AXRelationCache::LabelChanged(Node* node) {
+  DCHECK(isHTMLLabelElement(node));
+  LabelableElement* control = toHTMLLabelElement(node)->control();
+  if (control)
+    TextChanged(Get(control));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/accessibility/AXRelationCache.h b/third_party/WebKit/Source/modules/accessibility/AXRelationCache.h
index ed356ba3..5a131d4 100644
--- a/third_party/WebKit/Source/modules/accessibility/AXRelationCache.h
+++ b/third_party/WebKit/Source/modules/accessibility/AXRelationCache.h
@@ -57,10 +57,6 @@
   // set of children owned by this object, returning the result in
   // |ownedChildren|. The result is validated - illegal, duplicate, or cyclical
   // references have been removed.
-  //
-  // If one or more ids aren't found, they're added to a lookup table so that if
-  // an element with that id appears later, it can be added when you call
-  // updateTreeIfElementIdIsAriaOwned.
   void UpdateAriaOwns(const AXObject* owner,
                       const Vector<String>& id_vector,
                       HeapVector<Member<AXObject>>& owned_children);
@@ -69,15 +65,28 @@
   // just changed, check to see if another object wants to be its parent due to
   // aria-owns. If so, update the tree by calling childrenChanged() on the
   // potential owner, possibly reparenting this element.
-  void UpdateTreeIfElementIdIsAriaOwned(Element*);
+  void UpdateRelatedTree(Node*);
 
   // Remove given AXID from cache.
   void RemoveAXID(AXID);
 
+  // Update map of ids to related objects.
+  // If one or more ids aren't found, they're added to a lookup table so that if
+  // an element with that id appears later, it can be added when you call
+  // UpdateRelatedTree.
+  void UpdateReverseRelations(const AXObject* relation_source,
+                              const Vector<String>& target_ids);
+
  private:
+  // If any object is related to this object via <label for>, aria-owns,
+  // aria-describedby or aria-labeledby, update the text for the related object.
+  void UpdateRelatedText(Node*);
+
   bool IsValidOwnsRelation(AXObject* owner, AXObject* child) const;
   void UnmapOwnedChildren(const AXObject* owner, Vector<AXID>);
   void MapOwnedChildren(const AXObject* owner, Vector<AXID>);
+  // Get reverse relations, returns target AXObject* and filling sources&.
+  AXObject* GetReverseRelated(Node*, HeapVector<Member<AXObject>>& sources);
 
   WeakPersistent<AXObjectCacheImpl> object_cache_;
 
@@ -94,22 +103,24 @@
   // own it.
   HashMap<AXID, AXID> aria_owned_child_to_real_parent_mapping_;
 
-  // Map from the AXID of any object with an aria-owns attribute to the set of
-  // ids of its children. This is *unvalidated*, it includes ids that may not
-  // currently exist in the tree.
-  HashMap<AXID, HashSet<String>> aria_owner_to_ids_mapping_;
-
-  // Map from an ID (the ID attribute of a DOM element) to the set of elements
-  // that want to own that ID. This is *unvalidated*, it includes possible
-  // duplicates.  This is used so that when an element with an ID is added to
-  // the tree or changes its ID, we can quickly determine if it affects an
-  // aria-owns relationship.
-  HashMap<String, std::unique_ptr<HashSet<AXID>>> id_to_aria_owners_mapping_;
+  // Reverse relation map from an ID (the ID attribute of a DOM element) to the
+  // set of elements that at some time pointed to that ID via aria-owns,
+  // aria-labelledby, aria-desribedby. This is *unvalidated*, it includes
+  // possible extras and duplicates.
+  // This is used so that:
+  // - When an element with an ID is added to the tree or changes its ID, we can
+  //   quickly determine if it affects an aria-owns relationship.
+  // - When text changes, we can recompute any label or description based on it
+  //   and fire the appropriate change events.
+  HashMap<String, std::unique_ptr<HashSet<AXID>>> id_attr_to_related_mapping_;
 
   // Helpers that call back into object cache
   AXObject* ObjectFromAXID(AXID) const;
   AXObject* GetOrCreate(Node*);
+  AXObject* Get(Node*);
   void ChildrenChanged(AXObject*);
+  void TextChanged(AXObject*);
+  void LabelChanged(Node*);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
index 58b136d..61d543f4 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
@@ -98,15 +98,6 @@
 // LayoutTests/media/media-controls.js.
 const double kTimeWithoutMouseMovementBeforeHidingMediaControls = 3;
 
-const char* kStateCSSClasses[6] = {
-    "phase-pre-ready state-no-source",    // kNoSource
-    "phase-pre-ready state-no-metadata",  // kNotLoaded
-    "state-loading-metadata",             // kLoadingMetadata
-    "phase-ready state-stopped",          // kStopped
-    "phase-ready state-playing",          // kPlaying
-    "phase-ready state-buffering",        // kBuffering
-};
-
 bool ShouldShowFullscreenButton(const HTMLMediaElement& media_element) {
   // Unconditionally allow the user to exit fullscreen if we are in it
   // now.  Especially on android, when we might not yet know if
@@ -476,9 +467,6 @@
   overflow_list_->AppendChild(
       toggle_closed_captions_button_->CreateOverflowElement(
           new MediaControlToggleClosedCaptionsButtonElement(*this)));
-
-  // Set the default CSS classes.
-  UpdateCSSClassFromState();
 }
 
 Node::InsertionNotificationRequest MediaControlsImpl::InsertedInto(
@@ -511,34 +499,6 @@
   return HTMLDivElement::InsertedInto(root);
 }
 
-void MediaControlsImpl::UpdateCSSClassFromState() {
-  const char* classes = kStateCSSClasses[State()];
-  if (getAttribute("class") != classes)
-    setAttribute("class", classes);
-}
-
-MediaControlsImpl::ControlsState MediaControlsImpl::State() const {
-  switch (MediaElement().getNetworkState()) {
-    case HTMLMediaElement::kNetworkEmpty:
-    case HTMLMediaElement::kNetworkNoSource:
-      return ControlsState::kNoSource;
-    case HTMLMediaElement::kNetworkLoading:
-      if (MediaElement().getReadyState() == HTMLMediaElement::kHaveNothing)
-        return ControlsState::kLoadingMetadata;
-      if (!MediaElement().paused())
-        return ControlsState::kBuffering;
-      break;
-    case HTMLMediaElement::kNetworkIdle:
-      if (MediaElement().getReadyState() == HTMLMediaElement::kHaveNothing)
-        return ControlsState::kNotLoaded;
-      break;
-  }
-
-  if (!MediaElement().paused())
-    return ControlsState::kPlaying;
-  return ControlsState::kStopped;
-}
-
 void MediaControlsImpl::RemovedFrom(ContainerNode*) {
   DCHECK(!MediaElement().isConnected());
 
@@ -1029,15 +989,12 @@
 
   if (download_iph_manager_)
     download_iph_manager_->SetIsPlaying(true);
-
-  UpdateCSSClassFromState();
 }
 
 void MediaControlsImpl::OnPlaying() {
   timeline_->OnPlaying();
 
   StartHideMediaControlsTimer();
-  UpdateCSSClassFromState();
 }
 
 void MediaControlsImpl::OnPause() {
@@ -1050,8 +1007,6 @@
 
   if (download_iph_manager_)
     download_iph_manager_->SetIsPlaying(false);
-
-  UpdateCSSClassFromState();
 }
 
 void MediaControlsImpl::OnTextTracksAddedOrRemoved() {
@@ -1068,14 +1023,12 @@
   // TODO(mlamouri): we should only change the aspects of the control that need
   // to be changed.
   Reset();
-  UpdateCSSClassFromState();
 }
 
 void MediaControlsImpl::OnLoadedMetadata() {
   // TODO(mlamouri): we should only change the aspects of the control that need
   // to be changed.
   Reset();
-  UpdateCSSClassFromState();
 }
 
 void MediaControlsImpl::OnEnteredFullscreen() {
@@ -1290,8 +1243,6 @@
   // source or no longer have a source.
   download_button_->SetIsWanted(
       download_button_->ShouldDisplayDownloadButton());
-
-  UpdateCSSClassFromState();
 }
 
 bool MediaControlsImpl::OverflowMenuVisible() {
@@ -1336,10 +1287,6 @@
   return download_iph_manager_;
 }
 
-void MediaControlsImpl::OnWaiting() {
-  UpdateCSSClassFromState();
-}
-
 DEFINE_TRACE(MediaControlsImpl) {
   visitor->Trace(element_mutation_callback_);
   visitor->Trace(resize_observer_);
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
index c04cad6..de6446f 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
@@ -153,33 +153,6 @@
   // Notify us that our controls enclosure has changed size.
   void NotifyElementSizeChanged(DOMRectReadOnly* new_size);
 
-  // Update the CSS class when we think the state has updated.
-  void UpdateCSSClassFromState();
-
-  // Track the state of the controls.
-  enum ControlsState {
-    // There is no video source.
-    kNoSource,
-
-    // Metadata has not been loaded.
-    kNotLoaded,
-
-    // Metadata is being loaded.
-    kLoadingMetadata,
-
-    // Metadata is loaded and the media is ready to play. This can be when the
-    // media is paused, when it has ended or before the media has started
-    // playing.
-    kStopped,
-
-    // The media is playing.
-    kPlaying,
-
-    // Playback has stopped to buffer.
-    kBuffering,
-  };
-  ControlsState State() const;
-
   explicit MediaControlsImpl(HTMLMediaElement&);
 
   void InitializeControls();
@@ -248,7 +221,6 @@
   void OnExitedFullscreen();
   void OnPanelKeypress();
   void OnMediaKeyboardEvent(Event* event) { DefaultEventHandler(event); }
-  void OnWaiting();
 
   // Media control elements.
   Member<MediaControlOverlayEnclosureElement> overlay_enclosure_;
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsMediaEventListener.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsMediaEventListener.cpp
index 71aed33..e7a3fcf5 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsMediaEventListener.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsMediaEventListener.cpp
@@ -38,7 +38,6 @@
   GetMediaElement().addEventListener(EventTypeNames::keypress, this, false);
   GetMediaElement().addEventListener(EventTypeNames::keydown, this, false);
   GetMediaElement().addEventListener(EventTypeNames::keyup, this, false);
-  GetMediaElement().addEventListener(EventTypeNames::waiting, this, false);
 
   // Listen to two different fullscreen events in order to make sure the new and
   // old APIs are handled.
@@ -163,10 +162,6 @@
     media_controls_->OnLoadedMetadata();
     return;
   }
-  if (event->type() == EventTypeNames::waiting) {
-    media_controls_->OnWaiting();
-    return;
-  }
 
   // Fullscreen handling.
   if (event->type() == EventTypeNames::fullscreenchange ||
diff --git a/third_party/WebKit/Source/modules/websockets/WebSocket.idl b/third_party/WebKit/Source/modules/websockets/WebSocket.idl
index 3a8d9f9..852894e 100644
--- a/third_party/WebKit/Source/modules/websockets/WebSocket.idl
+++ b/third_party/WebKit/Source/modules/websockets/WebSocket.idl
@@ -58,7 +58,7 @@
     attribute EventHandler onclose;
     readonly attribute DOMString extensions;
     readonly attribute DOMString protocol;
-    [RaisesException] void close([Clamp] optional unsigned short code, optional USVString reason);
+    [RaisesException] void close(optional [Clamp] unsigned short code, optional USVString reason);
 
     // messaging
     attribute EventHandler onmessage;
diff --git a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp
index 7356bae..8017efcfd 100644
--- a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp
+++ b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp
@@ -785,7 +785,7 @@
 
 void USBDevice::AsyncControlTransferIn(ScriptPromiseResolver* resolver,
                                        UsbTransferStatus status,
-                                       const Optional<Vector<uint8_t>>& data) {
+                                       const Vector<uint8_t>& data) {
   if (!MarkRequestComplete(resolver))
     return;
 
@@ -826,7 +826,7 @@
 
 void USBDevice::AsyncTransferIn(ScriptPromiseResolver* resolver,
                                 UsbTransferStatus status,
-                                const Optional<Vector<uint8_t>>& data) {
+                                const Vector<uint8_t>& data) {
   if (!MarkRequestComplete(resolver))
     return;
 
@@ -856,13 +856,12 @@
 
 void USBDevice::AsyncIsochronousTransferIn(
     ScriptPromiseResolver* resolver,
-    const Optional<Vector<uint8_t>>& data,
+    const Vector<uint8_t>& data,
     Vector<UsbIsochronousPacketPtr> mojo_packets) {
   if (!MarkRequestComplete(resolver))
     return;
 
-  DOMArrayBuffer* buffer =
-      data ? DOMArrayBuffer::Create(data->data(), data->size()) : nullptr;
+  DOMArrayBuffer* buffer = DOMArrayBuffer::Create(data.data(), data.size());
   HeapVector<Member<USBIsochronousInTransferPacket>> packets;
   packets.ReserveCapacity(mojo_packets.size());
   size_t byte_offset = 0;
diff --git a/third_party/WebKit/Source/modules/webusb/USBDevice.h b/third_party/WebKit/Source/modules/webusb/USBDevice.h
index 79229b1e..a6cb52a 100644
--- a/third_party/WebKit/Source/modules/webusb/USBDevice.h
+++ b/third_party/WebKit/Source/modules/webusb/USBDevice.h
@@ -145,20 +145,20 @@
                                      bool success);
   void AsyncControlTransferIn(ScriptPromiseResolver*,
                               device::mojom::blink::UsbTransferStatus,
-                              const Optional<Vector<uint8_t>>&);
+                              const Vector<uint8_t>&);
   void AsyncControlTransferOut(unsigned,
                                ScriptPromiseResolver*,
                                device::mojom::blink::UsbTransferStatus);
   void AsyncClearHalt(ScriptPromiseResolver*, bool success);
   void AsyncTransferIn(ScriptPromiseResolver*,
                        device::mojom::blink::UsbTransferStatus,
-                       const Optional<Vector<uint8_t>>&);
+                       const Vector<uint8_t>&);
   void AsyncTransferOut(unsigned,
                         ScriptPromiseResolver*,
                         device::mojom::blink::UsbTransferStatus);
   void AsyncIsochronousTransferIn(
       ScriptPromiseResolver*,
-      const Optional<Vector<uint8_t>>&,
+      const Vector<uint8_t>&,
       Vector<device::mojom::blink::UsbIsochronousPacketPtr>);
   void AsyncIsochronousTransferOut(
       ScriptPromiseResolver*,
diff --git a/third_party/WebKit/Source/modules/webusb/USBInTransferResult.h b/third_party/WebKit/Source/modules/webusb/USBInTransferResult.h
index 0ab4b50..128afda 100644
--- a/third_party/WebKit/Source/modules/webusb/USBInTransferResult.h
+++ b/third_party/WebKit/Source/modules/webusb/USBInTransferResult.h
@@ -21,12 +21,9 @@
 
  public:
   static USBInTransferResult* Create(const String& status,
-                                     const Optional<Vector<uint8_t>>& data) {
-    DOMDataView* data_view = nullptr;
-    if (data) {
-      data_view = DOMDataView::Create(
-          DOMArrayBuffer::Create(data->data(), data->size()), 0, data->size());
-    }
+                                     const Vector<uint8_t>& data) {
+    DOMDataView* data_view = DOMDataView::Create(
+        DOMArrayBuffer::Create(data.data(), data.size()), 0, data.size());
     return new USBInTransferResult(status, data_view);
   }
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/path_finder.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/path_finder.py
index 0f9575ac..91b4538 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/path_finder.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/path_finder.py
@@ -67,7 +67,11 @@
         os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
 
 
+@memoized
 def get_source_dir():
+    post_move_path = os.path.join(get_chromium_src_dir(), 'third_party', 'blink', 'renderer')
+    if os.path.exists(post_move_path):
+        return post_move_path
     return os.path.join(get_blink_dir(), 'Source')
 
 
@@ -123,8 +127,16 @@
     def path_from_chromium_base(self, *comps):
         return self._filesystem.join(self.chromium_base(), *comps)
 
+    @memoized
+    def _blink_source_dir(self):
+        post_move_path = self._filesystem.join(
+            self.chromium_base(), 'third_party', 'blink', 'renderer')
+        if self._filesystem.exists(post_move_path):
+            return post_move_path
+        return self._filesystem.join(self._webkit_base(), 'Source')
+
     def path_from_blink_source(self, *comps):
-        return self._filesystem.join(self._filesystem.join(self._webkit_base(), 'Source'), *comps)
+        return self._filesystem.join(self._blink_source_dir(), *comps)
 
     def path_from_tools_scripts(self, *comps):
         return self._filesystem.join(self._filesystem.join(self._webkit_base(), 'Tools', 'Scripts'), *comps)
diff --git a/third_party/blink/tools/move_blink_source.py b/third_party/blink/tools/move_blink_source.py
index faaca05..3261f90 100755
--- a/third_party/blink/tools/move_blink_source.py
+++ b/third_party/blink/tools/move_blink_source.py
@@ -125,6 +125,8 @@
              [self._update_basename]),
             ('third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5',
              [self._update_basename]),
+            ('third_party/WebKit/Source/core/css/ComputedStyleFieldAliases.json5',
+             [self._update_basename]),
             ('third_party/WebKit/Source/core/html/parser/create-html-entity-table',
              [self._update_basename]),
             ('third_party/WebKit/Source/core/inspector/inspector_protocol_config.json',
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json
index 3ce70e94c..80a8156 100644
--- a/third_party/polymer/v1_0/bower.json
+++ b/third_party/polymer/v1_0/bower.json
@@ -36,7 +36,6 @@
     "paper-behaviors": "PolymerElements/paper-behaviors#1.0.12",
     "paper-button": "PolymerElements/paper-button#1.0.13",
     "paper-checkbox": "PolymerElements/paper-checkbox#1.4.0",
-    "paper-drawer-panel": "PolymerElements/paper-drawer-panel#1.0.10",
     "paper-fab": "PolymerElements/paper-fab#1.2.0",
     "paper-header-panel": "PolymerElements/paper-header-panel#1.1.6",
     "paper-icon-button": "PolymerElements/paper-icon-button#1.1.6",
diff --git a/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/bower.json b/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/bower.json
deleted file mode 100644
index 28eac2f..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/bower.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "name": "paper-drawer-panel",
-  "version": "1.0.10",
-  "description": "A responsive drawer panel",
-  "authors": [
-    "The Polymer Authors"
-  ],
-  "keywords": [
-    "web-components",
-    "polymer",
-    "drawer",
-    "responsive",
-    "layout"
-  ],
-  "repository": {
-    "type": "git",
-    "url": "git://github.com/PolymerElements/paper-drawer-panel.git"
-  },
-  "license": "http://polymer.github.io/LICENSE.txt",
-  "homepage": "https://github.com/PolymerElements/paper-drawer-panel",
-  "dependencies": {
-    "iron-media-query": "PolymerElements/iron-media-query#^1.0.0",
-    "iron-resizable-behavior": "PolymerElements/iron-resizable-behavior#^1.0.0",
-    "iron-selector": "PolymerElements/iron-selector#^1.0.0",
-    "polymer": "Polymer/polymer#^1.1.0"
-  },
-  "devDependencies": {
-    "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
-    "paper-button": "PolymerElements/paper-button#^1.0.0",
-    "paper-styles": "PolymerElements/paper-styles#^1.0.0",
-    "iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
-    "test-fixture": "PolymerElements/test-fixture#^1.0.0",
-    "web-component-tester": "^4.0.0",
-    "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
-  },
-  "main": "paper-drawer-panel.html",
-  "ignore": []
-}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/compiled_resources2.gyp b/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/compiled_resources2.gyp
deleted file mode 100644
index 40856f89..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/compiled_resources2.gyp
+++ /dev/null
@@ -1,18 +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.
-#
-# NOTE: Created with generate_compiled_resources_gyp.py, please do not edit.
-{
-  'targets': [
-    {
-      'target_name': 'paper-drawer-panel-extracted',
-      'dependencies': [
-        '../iron-media-query/compiled_resources2.gyp:iron-media-query-extracted',
-        '../iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted',
-        '../iron-selector/compiled_resources2.gyp:iron-selector-extracted',
-      ],
-      'includes': ['../../../../closure_compiler/compile_js2.gypi'],
-    },
-  ],
-}
diff --git a/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/paper-drawer-panel-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/paper-drawer-panel-extracted.js
deleted file mode 100644
index 9580766..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/paper-drawer-panel-extracted.js
+++ /dev/null
@@ -1,531 +0,0 @@
-(function() {
-      'use strict';
-
-      // this would be the only `paper-drawer-panel` in
-      // the whole app that can be in `dragging` state
-      var sharedPanel = null;
-
-      function classNames(obj) {
-        var classes = [];
-        for (var key in obj) {
-          if (obj.hasOwnProperty(key) && obj[key]) {
-            classes.push(key);
-          }
-        }
-
-        return classes.join(' ');
-      }
-
-      Polymer({
-
-        is: 'paper-drawer-panel',
-
-        behaviors: [Polymer.IronResizableBehavior],
-
-        /**
-         * Fired when the narrow layout changes.
-         *
-         * @event paper-responsive-change {{narrow: boolean}} detail -
-         *     narrow: true if the panel is in narrow layout.
-         */
-
-        /**
-         * Fired when the a panel is selected.
-         *
-         * Listening for this event is an alternative to observing changes in the `selected` attribute.
-         * This event is fired both when a panel is selected.
-         *
-         * @event iron-select {{item: Object}} detail -
-         *     item: The panel that the event refers to.
-         */
-
-        /**
-         * Fired when a panel is deselected.
-         *
-         * Listening for this event is an alternative to observing changes in the `selected` attribute.
-         * This event is fired both when a panel is deselected.
-         *
-         * @event iron-deselect {{item: Object}} detail -
-         *     item: The panel that the event refers to.
-         */
-        properties: {
-
-          /**
-           * The panel to be selected when `paper-drawer-panel` changes to narrow
-           * layout.
-           */
-          defaultSelected: {
-            type: String,
-            value: 'main'
-          },
-
-          /**
-           * If true, swipe from the edge is disabled.
-           */
-          disableEdgeSwipe: {
-            type: Boolean,
-            value: false
-          },
-
-          /**
-           * If true, swipe to open/close the drawer is disabled.
-           */
-          disableSwipe: {
-            type: Boolean,
-            value: false
-          },
-
-          /**
-           * Whether the user is dragging the drawer interactively.
-           */
-          dragging: {
-            type: Boolean,
-            value: false,
-            readOnly: true,
-            notify: true
-          },
-
-          /**
-           * Width of the drawer panel.
-           */
-          drawerWidth: {
-            type: String,
-            value: '256px'
-          },
-
-          /**
-           * How many pixels on the side of the screen are sensitive to edge
-           * swipes and peek.
-           */
-          edgeSwipeSensitivity: {
-            type: Number,
-            value: 30
-          },
-
-          /**
-           * If true, ignore `responsiveWidth` setting and force the narrow layout.
-           */
-          forceNarrow: {
-            type: Boolean,
-            value: false
-          },
-
-          /**
-           * Whether the browser has support for the transform CSS property.
-           */
-          hasTransform: {
-            type: Boolean,
-            value: function() {
-              return 'transform' in this.style;
-            }
-          },
-
-          /**
-           * Whether the browser has support for the will-change CSS property.
-           */
-          hasWillChange: {
-            type: Boolean,
-            value: function() {
-              return 'willChange' in this.style;
-            }
-          },
-
-          /**
-           * Returns true if the panel is in narrow layout.  This is useful if you
-           * need to show/hide elements based on the layout.
-           */
-          narrow: {
-            reflectToAttribute: true,
-            type: Boolean,
-            value: false,
-            readOnly: true,
-            notify: true
-          },
-
-          /**
-           * Whether the drawer is peeking out from the edge.
-           */
-          peeking: {
-            type: Boolean,
-            value: false,
-            readOnly: true,
-            notify: true
-          },
-
-          /**
-           * Max-width when the panel changes to narrow layout.
-           */
-          responsiveWidth: {
-            type: String,
-            value: '768px'
-          },
-
-          /**
-           * If true, position the drawer to the right.
-           */
-          rightDrawer: {
-            type: Boolean,
-            value: false
-          },
-
-          /**
-           * The panel that is being selected. `drawer` for the drawer panel and
-           * `main` for the main panel.
-           *
-           * @type {string|null}
-           */
-          selected: {
-            reflectToAttribute: true,
-            notify: true,
-            type: String,
-            value: null
-          },
-
-          /**
-           * The attribute on elements that should toggle the drawer on tap, also elements will
-           * automatically be hidden in wide layout.
-           */
-          drawerToggleAttribute: {
-            type: String,
-            value: 'paper-drawer-toggle'
-          },
-
-          /**
-           * The CSS selector for the element that should receive focus when the drawer is open.
-           * By default, when the drawer opens, it focuses the first tabbable element. That is,
-           * the first element that can receive focus.
-           *
-           * To disable this behavior, you can set `drawerFocusSelector` to `null` or an empty string.
-           *
-           */
-          drawerFocusSelector: {
-            type: String,
-            value:
-                'a[href]:not([tabindex="-1"]),'+
-                'area[href]:not([tabindex="-1"]),'+
-                'input:not([disabled]):not([tabindex="-1"]),'+
-                'select:not([disabled]):not([tabindex="-1"]),'+
-                'textarea:not([disabled]):not([tabindex="-1"]),'+
-                'button:not([disabled]):not([tabindex="-1"]),'+
-                'iframe:not([tabindex="-1"]),'+
-                '[tabindex]:not([tabindex="-1"]),'+
-                '[contentEditable=true]:not([tabindex="-1"])'
-          },
-
-          /**
-           * Whether the transition is enabled.
-           */
-          _transition: {
-            type: Boolean,
-            value: false
-          },
-
-        },
-
-        listeners: {
-          tap: '_onTap',
-          track: '_onTrack',
-          down: '_downHandler',
-          up: '_upHandler',
-          transitionend: '_onTransitionEnd'
-        },
-
-        observers: [
-          '_forceNarrowChanged(forceNarrow, defaultSelected)',
-          '_toggleFocusListener(selected)'
-        ],
-
-        ready: function() {
-          // Avoid transition at the beginning e.g. page loads and enable
-          // transitions only after the element is rendered and ready.
-          this._transition = true;
-          this._boundFocusListener = this._didFocus.bind(this);
-        },
-
-        /**
-         * Toggles the panel open and closed.
-         *
-         * @method togglePanel
-         */
-        togglePanel: function() {
-          if (this._isMainSelected()) {
-            this.openDrawer();
-          } else {
-            this.closeDrawer();
-          }
-        },
-
-        /**
-         * Opens the drawer.
-         *
-         * @method openDrawer
-         */
-        openDrawer: function() {
-          this.selected = 'drawer';
-        },
-
-        /**
-         * Closes the drawer.
-         *
-         * @method closeDrawer
-         */
-        closeDrawer: function() {
-          this.selected = 'main';
-        },
-
-        _onTransitionEnd: function (e) {
-          var target = Polymer.dom(e).localTarget;
-          if (target !== this) {
-            // ignore events coming from the light dom
-            return;
-          }
-          if (e.propertyName === 'left' || e.propertyName === 'right') {
-            this.notifyResize();
-          }
-          if (e.propertyName === 'transform' && this.selected === 'drawer') {
-            var focusedChild = this._getAutoFocusedNode();
-            focusedChild && focusedChild.focus();
-          }
-        },
-
-        _computeIronSelectorClass: function(narrow, transition, dragging, rightDrawer, peeking) {
-          return classNames({
-            dragging: dragging,
-            'narrow-layout': narrow,
-            'right-drawer': rightDrawer,
-            'left-drawer': !rightDrawer,
-            transition: transition,
-            peeking: peeking
-          });
-        },
-
-        _computeDrawerStyle: function(drawerWidth) {
-          return 'width:' + drawerWidth + ';';
-        },
-
-        _computeMainStyle: function(narrow, rightDrawer, drawerWidth) {
-          var style = '';
-
-          style += 'left:' + ((narrow || rightDrawer) ? '0' : drawerWidth) + ';';
-
-          if (rightDrawer) {
-            style += 'right:' + (narrow ? '' : drawerWidth) + ';';
-          }
-
-          return style;
-        },
-
-        _computeMediaQuery: function(forceNarrow, responsiveWidth) {
-          return forceNarrow ? '' : '(max-width: ' + responsiveWidth + ')';
-        },
-
-        _computeSwipeOverlayHidden: function(narrow, disableEdgeSwipe) {
-          return !narrow || disableEdgeSwipe;
-        },
-
-        _onTrack: function(event) {
-          if (sharedPanel && this !== sharedPanel) {
-            return;
-          }
-          switch (event.detail.state) {
-            case 'start':
-              this._trackStart(event);
-              break;
-            case 'track':
-              this._trackX(event);
-              break;
-            case 'end':
-              this._trackEnd(event);
-              break;
-          }
-        },
-
-        _responsiveChange: function(narrow) {
-          this._setNarrow(narrow);
-
-          this.selected = this.narrow ? this.defaultSelected : null;
-
-          this.setScrollDirection(this._swipeAllowed() ? 'y' : 'all');
-          this.fire('paper-responsive-change', {narrow: this.narrow});
-        },
-
-        _onQueryMatchesChanged: function(event) {
-          this._responsiveChange(event.detail.value);
-        },
-
-        _forceNarrowChanged: function() {
-          // set the narrow mode only if we reached the `responsiveWidth`
-          this._responsiveChange(this.forceNarrow || this.$.mq.queryMatches);
-        },
-
-        _swipeAllowed: function() {
-          return this.narrow && !this.disableSwipe;
-        },
-
-        _isMainSelected: function() {
-          return this.selected === 'main';
-        },
-
-        _startEdgePeek: function() {
-          this.width = this.$.drawer.offsetWidth;
-          this._moveDrawer(this._translateXForDeltaX(this.rightDrawer ?
-              -this.edgeSwipeSensitivity : this.edgeSwipeSensitivity));
-          this._setPeeking(true);
-        },
-
-        _stopEdgePeek: function() {
-          if (this.peeking) {
-            this._setPeeking(false);
-            this._moveDrawer(null);
-          }
-        },
-
-        _downHandler: function(event) {
-          if (!this.dragging && this._isMainSelected() && this._isEdgeTouch(event) && !sharedPanel) {
-            this._startEdgePeek();
-            // cancel selection
-            event.preventDefault();
-            // grab this panel
-            sharedPanel = this;
-          }
-        },
-
-        _upHandler: function() {
-          this._stopEdgePeek();
-          // release the panel
-          sharedPanel = null;
-        },
-
-        _onTap: function(event) {
-          var targetElement = Polymer.dom(event).localTarget;
-          var isTargetToggleElement = targetElement &&
-            this.drawerToggleAttribute &&
-            targetElement.hasAttribute(this.drawerToggleAttribute);
-
-          if (isTargetToggleElement) {
-            this.togglePanel();
-          }
-        },
-
-        _isEdgeTouch: function(event) {
-          var x = event.detail.x;
-
-          return !this.disableEdgeSwipe && this._swipeAllowed() &&
-            (this.rightDrawer ?
-              x >= this.offsetWidth - this.edgeSwipeSensitivity :
-              x <= this.edgeSwipeSensitivity);
-        },
-
-        _trackStart: function(event) {
-          if (this._swipeAllowed()) {
-            sharedPanel = this;
-            this._setDragging(true);
-
-            if (this._isMainSelected()) {
-              this._setDragging(this.peeking || this._isEdgeTouch(event));
-            }
-
-            if (this.dragging) {
-              this.width = this.$.drawer.offsetWidth;
-              this._transition = false;
-            }
-          }
-        },
-
-        _translateXForDeltaX: function(deltaX) {
-          var isMain = this._isMainSelected();
-
-          if (this.rightDrawer) {
-            return Math.max(0, isMain ? this.width + deltaX : deltaX);
-          } else {
-            return Math.min(0, isMain ? deltaX - this.width : deltaX);
-          }
-        },
-
-        _trackX: function(event) {
-          if (this.dragging) {
-            var dx = event.detail.dx;
-
-            if (this.peeking) {
-              if (Math.abs(dx) <= this.edgeSwipeSensitivity) {
-                // Ignore trackx until we move past the edge peek.
-                return;
-              }
-              this._setPeeking(false);
-            }
-
-            this._moveDrawer(this._translateXForDeltaX(dx));
-          }
-        },
-
-        _trackEnd: function(event) {
-          if (this.dragging) {
-            var xDirection = event.detail.dx > 0;
-
-            this._setDragging(false);
-            this._transition = true;
-            sharedPanel = null;
-            this._moveDrawer(null);
-
-            if (this.rightDrawer) {
-              this[xDirection ? 'closeDrawer' : 'openDrawer']();
-            } else {
-              this[xDirection ? 'openDrawer' : 'closeDrawer']();
-            }
-          }
-        },
-
-        _transformForTranslateX: function(translateX) {
-          if (translateX === null) {
-            return '';
-          }
-          return this.hasWillChange ? 'translateX(' + translateX + 'px)' :
-              'translate3d(' + translateX + 'px, 0, 0)';
-        },
-
-        _moveDrawer: function(translateX) {
-          this.transform(this._transformForTranslateX(translateX), this.$.drawer);
-        },
-
-        _getDrawerContent: function() {
-          return Polymer.dom(this.$.drawerContent).getDistributedNodes()[0];
-        },
-
-        _getAutoFocusedNode: function() {
-          var drawerContent = this._getDrawerContent();
-
-          return this.drawerFocusSelector ?
-              Polymer.dom(drawerContent).querySelector(this.drawerFocusSelector) || drawerContent : null;
-        },
-
-        _toggleFocusListener: function(selected) {
-          if (selected === 'drawer') {
-            this.addEventListener('focus', this._boundFocusListener, true);
-          } else {
-            this.removeEventListener('focus', this._boundFocusListener, true);
-          }
-        },
-
-        _didFocus: function(event) {
-          var autoFocusedNode = this._getAutoFocusedNode();
-          if (!autoFocusedNode) {
-            return;
-          }
-
-          var path = Polymer.dom(event).path;
-          var focusedChild = path[0];
-          var drawerContent = this._getDrawerContent();
-          var focusedChildCameFromDrawer = path.indexOf(drawerContent) !== -1;
-
-          if (!focusedChildCameFromDrawer) {
-            event.stopPropagation();
-            autoFocusedNode.focus();
-          }
-        },
-
-        _isDrawerClosed: function(narrow, selected) {
-          return !narrow || selected !== 'drawer';
-        }
-      });
-
-    }());
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/paper-drawer-panel.html b/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/paper-drawer-panel.html
deleted file mode 100644
index 1b6bbe1d..0000000
--- a/third_party/polymer/v1_0/components-chromium/paper-drawer-panel/paper-drawer-panel.html
+++ /dev/null
@@ -1,301 +0,0 @@
-<!--
-@license
-Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
-This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
-The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
-The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
-Code distributed by Google as part of the polymer project is also
-subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
---><html><head><link rel="import" href="../polymer/polymer.html">
-<link rel="import" href="../iron-media-query/iron-media-query.html">
-<link rel="import" href="../iron-selector/iron-selector.html">
-<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
-
-<!--
-Material design: [Navigation drawer](https://www.google.com/design/spec/patterns/navigation-drawer.html)
-
-`paper-drawer-panel` contains a drawer panel and a main panel.  The drawer
-and the main panel are side-by-side with drawer on the left.  When the browser
-window size is smaller than the `responsiveWidth`, `paper-drawer-panel`
-changes to narrow layout.  In narrow layout, the drawer will be stacked on top
-of the main panel.  The drawer will slide in/out to hide/reveal the main
-panel.
-
-Use the attribute `drawer` to indicate that the element is the drawer panel and
-`main` to indicate that the element is the main panel.
-
-Example:
-
-    <paper-drawer-panel>
-      <div drawer> Drawer panel... </div>
-      <div main> Main panel... </div>
-    </paper-drawer-panel>
-
-The drawer and the main panels are not scrollable.  You can set CSS overflow
-property on the elements to make them scrollable or use `paper-header-panel`.
-
-Example:
-
-    <paper-drawer-panel>
-      <paper-header-panel drawer>
-        <paper-toolbar></paper-toolbar>
-        <div> Drawer content... </div>
-      </paper-header-panel>
-      <paper-header-panel main>
-        <paper-toolbar></paper-toolbar>
-        <div> Main content... </div>
-      </paper-header-panel>
-    </paper-drawer-panel>
-
-An element that should toggle the drawer will automatically do so if it's
-given the `paper-drawer-toggle` attribute.  Also this element will automatically
-be hidden in wide layout.
-
-Example:
-
-    <paper-drawer-panel>
-      <paper-header-panel drawer>
-        <paper-toolbar>
-          <div>Application</div>
-        </paper-toolbar>
-        <div> Drawer content... </div>
-      </paper-header-panel>
-      <paper-header-panel main>
-        <paper-toolbar>
-          <paper-icon-button icon="menu" paper-drawer-toggle></paper-icon-button>
-          <div>Title</div>
-        </paper-toolbar>
-        <div> Main content... </div>
-      </paper-header-panel>
-    </paper-drawer-panel>
-
-To position the drawer to the right, add `right-drawer` attribute.
-
-    <paper-drawer-panel right-drawer>
-      <div drawer> Drawer panel... </div>
-      <div main> Main panel... </div>
-    </paper-drawer-panel>
-
-### Styling
-
-To change the main container:
-
-    paper-drawer-panel {
-      --paper-drawer-panel-main-container: {
-        background-color: gray;
-      };
-    }
-
-To change the drawer container when it's in the left side:
-
-    paper-drawer-panel {
-      --paper-drawer-panel-left-drawer-container: {
-        background-color: white;
-      };
-    }
-
-To change the drawer container when it's in the right side:
-
-    paper-drawer-panel {
-      --paper-drawer-panel-right-drawer-container: {
-        background-color: white;
-      };
-    }
-
-To customize the scrim:
-
-    paper-drawer-panel {
-      --paper-drawer-panel-scrim: {
-        background-color: red;
-      };
-    }
-
-The following custom properties and mixins are available for styling:
-
-Custom property | Description | Default
-----------------|-------------|----------
-`--paper-drawer-panel-scrim-opacity` | Scrim opacity | 1
-`--paper-drawer-panel-drawer-container` | Mixin applied to drawer container | {}
-`--paper-drawer-panel-left-drawer-container` | Mixin applied to container when it's in the left side | {}
-`--paper-drawer-panel-main-container` | Mixin applied to main container | {}
-`--paper-drawer-panel-right-drawer-container` | Mixin applied to container when it's in the right side | {}
-`--paper-drawer-panel-scrim` | Mixin applied to scrim | {}
-
-@group Paper elements
-@element paper-drawer-panel
-@demo demo/index.html
-@hero hero.svg
--->
-
-</head><body><dom-module id="paper-drawer-panel">
-  <template>
-    <style>
-      :host {
-        display: block;
-        position: absolute;
-        top: 0;
-        left: 0;
-        width: 100%;
-        height: 100%;
-        overflow: hidden;
-      }
-
-      iron-selector > #drawer {
-        position: absolute;
-        top: 0;
-        left: 0;
-        height: 100%;
-        background-color: white;
-
-        -moz-box-sizing: border-box;
-        box-sizing: border-box;
-
-        @apply(--paper-drawer-panel-drawer-container);
-      }
-
-      .transition > #drawer {
-        transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;
-        transition: transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;
-      }
-
-      .left-drawer > #drawer {
-        @apply(--paper-drawer-panel-left-drawer-container);
-      }
-
-      .right-drawer > #drawer {
-        left: auto;
-        right: 0;
-
-        @apply(--paper-drawer-panel-right-drawer-container);
-      }
-
-      iron-selector > #main {
-        position: absolute;
-        top: 0;
-        right: 0;
-        bottom: 0;
-
-        @apply(--paper-drawer-panel-main-container);
-      }
-
-      .transition > #main {
-        transition: left ease-in-out 0.3s, padding ease-in-out 0.3s;
-      }
-
-      .right-drawer > #main {
-        left: 0;
-      }
-
-      .right-drawer.transition > #main {
-        transition: right ease-in-out 0.3s, padding ease-in-out 0.3s;
-      }
-
-      #main > ::content > [main] {
-        height: 100%;
-      }
-
-      #drawer > ::content > [drawer] {
-        height: 100%;
-      }
-
-      #scrim {
-        position: absolute;
-        top: 0;
-        right: 0;
-        bottom: 0;
-        left: 0;
-        visibility: hidden;
-        opacity: 0;
-        transition: opacity ease-in-out 0.38s, visibility ease-in-out 0.38s;
-        background-color: rgba(0, 0, 0, 0.3);
-
-        @apply(--paper-drawer-panel-scrim);
-      }
-
-      .narrow-layout > #drawer {
-        will-change: transform;
-      }
-
-      .narrow-layout > #drawer.iron-selected {
-        box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15);
-      }
-
-      .right-drawer.narrow-layout > #drawer.iron-selected {
-        box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.15);
-      }
-
-      .narrow-layout > #drawer > ::content > [drawer] {
-        border: 0;
-      }
-
-      .left-drawer.narrow-layout > #drawer:not(.iron-selected) {
-        visibility: hidden;
-
-        -webkit-transform: translateX(-100%);
-        transform: translateX(-100%);
-      }
-
-      .right-drawer.narrow-layout > #drawer:not(.iron-selected) {
-        left: auto;
-        visibility: hidden;
-
-        -webkit-transform: translateX(100%);
-        transform: translateX(100%);
-      }
-
-      .left-drawer.dragging > #drawer:not(.iron-selected),
-      .left-drawer.peeking > #drawer:not(.iron-selected),
-      .right-drawer.dragging > #drawer:not(.iron-selected),
-      .right-drawer.peeking > #drawer:not(.iron-selected) {
-        visibility: visible;
-      }
-
-      .narrow-layout > #main {
-        padding: 0;
-      }
-
-      .right-drawer.narrow-layout > #main {
-        left: 0;
-        right: 0;
-      }
-
-      .narrow-layout > #main:not(.iron-selected) > #scrim,
-      .dragging > #main > #scrim {
-        visibility: visible;
-        opacity: var(--paper-drawer-panel-scrim-opacity, 1);
-      }
-
-      .narrow-layout > #main > * {
-        margin: 0;
-        min-height: 100%;
-        left: 0;
-        right: 0;
-
-        -moz-box-sizing: border-box;
-        box-sizing: border-box;
-      }
-
-      iron-selector:not(.narrow-layout) ::content [paper-drawer-toggle] {
-        display: none;
-      }
-    </style>
-
-    <iron-media-query id="mq" on-query-matches-changed="_onQueryMatchesChanged" query="[[_computeMediaQuery(forceNarrow, responsiveWidth)]]">
-    </iron-media-query>
-
-    <iron-selector attr-for-selected="id" class$="[[_computeIronSelectorClass(narrow, _transition, dragging, rightDrawer, peeking)]]" activate-event="" selected="[[selected]]">
-
-      <div id="main" style$="[[_computeMainStyle(narrow, rightDrawer, drawerWidth)]]">
-        <content select="[main]"></content>
-        <div id="scrim" on-tap="closeDrawer"></div>
-      </div>
-
-      <div id="drawer" style$="[[_computeDrawerStyle(drawerWidth)]]">
-        <content id="drawerContent" select="[drawer]"></content>
-      </div>
-
-    </iron-selector>
-  </template>
-
-  </dom-module>
-<script src="paper-drawer-panel-extracted.js"></script></body></html>
\ No newline at end of file
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt
index e0e58a0..4aee9aa 100644
--- a/third_party/polymer/v1_0/components_summary.txt
+++ b/third_party/polymer/v1_0/components_summary.txt
@@ -202,12 +202,6 @@
 Revision: 1d1c9439fe3a056356233e04171fd9c62f0857fb
 Tree link: https://github.com/PolymerElements/paper-checkbox/tree/v1.4.0
 
-Name: paper-drawer-panel
-Repository: https://github.com/PolymerElements/paper-drawer-panel.git
-Tree: v1.0.10
-Revision: 34c1d82dc3048dff33c83047bfaa95414e36d673
-Tree link: https://github.com/PolymerElements/paper-drawer-panel/tree/v1.0.10
-
 Name: paper-fab
 Repository: https://github.com/PolymerElements/paper-fab.git
 Tree: v1.2.0
diff --git a/third_party/s2cellid/AUTHORS b/third_party/s2cellid/AUTHORS
new file mode 100644
index 0000000..8ee1e1a
--- /dev/null
+++ b/third_party/s2cellid/AUTHORS
@@ -0,0 +1,11 @@
+# This is the official list of glog authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+#
+# Names should be added to this file as:
+#	Name or Organization <email address>
+# The email address is not required for organizations.
+#
+# Please keep the list sorted.
+
+Google Inc.
diff --git a/third_party/s2cellid/BUILD.gn b/third_party/s2cellid/BUILD.gn
new file mode 100644
index 0000000..2d71a80
--- /dev/null
+++ b/third_party/s2cellid/BUILD.gn
@@ -0,0 +1,44 @@
+# Copyright 2017 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#==============================================================================
+
+config("s2_config") {
+  include_dirs = [ "src" ]
+}
+
+static_library("s2cellid") {
+  sources = [
+    "src/s2/_fpcontractoff.h",
+    "src/s2/r2.h",
+    "src/s2/r2rect.cc",
+    "src/s2/r2rect.h",
+    "src/s2/s1angle.cc",
+    "src/s2/s1angle.h",
+    "src/s2/s2.h",
+    "src/s2/s2cellid.cc",
+    "src/s2/s2cellid.h",
+    "src/s2/s2coords-internal.h",
+    "src/s2/s2coords.cc",
+    "src/s2/s2coords.h",
+    "src/s2/s2latlng.cc",
+    "src/s2/s2latlng.h",
+    "src/s2/s2point.h",
+  ]
+  configs -= [ "//build/config/compiler:chromium_code" ]
+  configs += [ "//build/config/compiler:no_chromium_code" ]
+  public_configs = [ ":s2_config" ]
+  deps = [
+    "//base",
+  ]
+}
diff --git a/third_party/s2cellid/CONTRIBUTORS b/third_party/s2cellid/CONTRIBUTORS
new file mode 100644
index 0000000..6c74ced
--- /dev/null
+++ b/third_party/s2cellid/CONTRIBUTORS
@@ -0,0 +1,27 @@
+# People who have agreed to one of the CLAs and can contribute patches.
+# The AUTHORS file lists the copyright holders; this file
+# lists people.  For example, Google employees are listed here
+# but not in AUTHORS, because Google holds the copyright.
+#
+# Names should be added to this file only after verifying that
+# the individual or the individual's organization has agreed to
+# the appropriate Contributor License Agreement, found here:
+#
+# https://developers.google.com/open-source/cla/individual
+# https://developers.google.com/open-source/cla/corporate
+#
+# The agreement for individuals can be filled out on the web.
+#
+# When adding J Random Contributor's name to this file,
+# either J's name or J's organization's name should be
+# added to the AUTHORS file, depending on whether the
+# individual or corporate CLA was used.
+#
+# Names should be added to this file as:
+#     Name <email address>
+#
+# Please keep the list sorted.
+
+Eric Veach <ericv@google.com>
+Jesse Rosenstock <jmr@google.com>
+Julien Basch <julienbasch@google.com>
diff --git a/third_party/s2cellid/DEPS b/third_party/s2cellid/DEPS
new file mode 100644
index 0000000..c4acdd2c
--- /dev/null
+++ b/third_party/s2cellid/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  '+base',
+  '+build',
+  '+s2',
+]
\ No newline at end of file
diff --git a/third_party/s2cellid/LICENSE b/third_party/s2cellid/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/s2cellid/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/s2cellid/NOTICE b/third_party/s2cellid/NOTICE
new file mode 100644
index 0000000..2b6d6fd
--- /dev/null
+++ b/third_party/s2cellid/NOTICE
@@ -0,0 +1,5 @@
+S2 Geometry Library
+Copyright 2017 Google Inc.  All Rights Reserved.
+
+This product includes software developed at
+Google (https://www.google.com/).
diff --git a/third_party/s2cellid/OWNERS b/third_party/s2cellid/OWNERS
new file mode 100644
index 0000000..a215aac
--- /dev/null
+++ b/third_party/s2cellid/OWNERS
@@ -0,0 +1,3 @@
+amoylan@chromium.org
+napper@chromium.org
+renjieliu@chromium.org
\ No newline at end of file
diff --git a/third_party/s2cellid/README.chromium b/third_party/s2cellid/README.chromium
new file mode 100644
index 0000000..2308087
--- /dev/null
+++ b/third_party/s2cellid/README.chromium
@@ -0,0 +1,62 @@
+Name: S2 CellId Library
+Short Name: s2cellid
+URL: 0
+Version: 0
+License: License: Apache Version 2.0
+License File: NOT_SHIPPED
+Security Critical: no
+License Android Compatible: yes
+
+Description:
+This library extracted the core S2CellId functions from the S2 geometry
+library.
+
+Local Modifications:
+- Extract subset of the whole s2 library, including only:
+ - util/bits/bits.h
+ - math/
+   - mathutil.h mathutil.cc
+   - vector.h
+ - _fpcontractoff.h
+ - r1interval.h
+ - r2.h
+ - r2rect.h r2rect.cc
+ - s1angle.h s1angle.cc
+ - s2cellid.h s2cellid.cc
+ - s2coords.h s2coords.cc s2coords-internal.cc
+ - s2latlng.h s2latlng.cc
+ - s2point.h
+- Extract functions under util/bits/bits.h:
+ - Log2Floor*
+ - FindLSBSetNonZero*
+ - FindMSBSetNonZero*
+ - Remove all uint128 declaration and implementation.
+ - Remove corresponding comments.
+ - Remove unnecessary dependencies.
+- Remove absl dependency:
+  - Change uint8 -> uint8_t
+  - Change uint16 -> uint16_t
+  - Change uint32 -> uint32_t
+  - Change uint64 -> uint64_t
+  - Change int32 -> int32_t
+  - Change int64 -> int64
+  - Change GG_ULONGLONG -> static_cast<uint64_t>
+  - Remove absl/base/macros.h and use chromium base/macros.h
+- Change glog to use chromium base/logging.h
+- Remove s2/base/strings/stringprintf.h to use chromium base/strings/stringprintf.h
+- Remove base/casts.h and change implicit_cast to static_cast  
+- Append std:: to string type.
+- Under s2cellid:
+  - Remove Encode declaration and implementation.
+  - Remove Decode declaration and implementation.
+  - Remove util/coding/coder.h dependency.
+  - Change StringPrintf format from "Invalid: %016llx" to "Invalid: %016lx".
+- Under s2point.h
+  - Remove s2/util/math/vector3_hash.h
+  - Remove S2PointHash and comments.
+- Add BUILD.gn file.
+- Add DEPS file.
+- Remove CMakeList.txt file.
+- Remove CONTRIBUTING.md.
+- Remove README.md.
+- Remove docs.
\ No newline at end of file
diff --git a/third_party/s2cellid/src/s2/_fpcontractoff.h b/third_party/s2cellid/src/s2/_fpcontractoff.h
new file mode 100644
index 0000000..7509ff6
--- /dev/null
+++ b/third_party/s2cellid/src/s2/_fpcontractoff.h
@@ -0,0 +1,60 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2__FPCONTRACTOFF_H_
+#define S2__FPCONTRACTOFF_H_
+
+// Turn off the fused multiply-add optimization ("fp-contract").  With
+// fp-contract on, any expression of the form "a * b + c" has two possible
+// results, and the compiler is free to choose either of them.  Effectively
+// this makes it impossible to write deterministic functions that involve
+// floating-point math.
+//
+// S2 requires deterministic arithmetic for correctness.  We need to turn off
+// fp-contract for the entire compilation unit, because S2 has public inline
+// functions, and the optimization is controlled by the setting in effect when
+// inline functions are instantiated (not when they are defined).
+//
+// Note that there is a standard C pragma to turn off FP contraction:
+//   #pragma STDC FP_CONTRACT OFF
+// but it is not implemented in GCC because the standard pragma allows control
+// at the level of compound statements rather than entire functions.
+//
+// This file may be included with other files in any order, as long as it
+// appears before the first non-inline function definition.  It is
+// named with an underscore so that it is included first among the S2 headers.
+
+// TODO(compiler-team): Figure out how to do this in a portable way.
+#if defined(HAVE_ARMEABI_V7A)
+// Some android builds use a buggy compiler that runs out of memory while
+// parsing the pragma (--cpu=armeabi-v7a).
+
+#elif defined(ANDROID)
+// Other android builds use a buggy compiler that crashes with an internal
+// error (Android NDK R9).
+
+#elif defined(__clang__)
+// Clang supports the standard C++ pragma for turning off this optimization.
+#pragma STDC FP_CONTRACT OFF
+
+#elif defined(__GNUC__)
+// GCC defines its own pragma that operates at the function level rather than
+// the statement level.
+#pragma GCC optimize("fp-contract=off")
+#endif
+
+#endif  // S2__FPCONTRACTOFF_H_
diff --git a/third_party/s2cellid/src/s2/r1interval.h b/third_party/s2cellid/src/s2/r1interval.h
new file mode 100644
index 0000000..5590574
--- /dev/null
+++ b/third_party/s2cellid/src/s2/r1interval.h
@@ -0,0 +1,235 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_R1INTERVAL_H_
+#define S2_R1INTERVAL_H_
+
+#include <algorithm>
+#include <cmath>
+#include <iosfwd>
+#include <iostream>
+
+#include "base/logging.h"
+#include "s2/_fpcontractoff.h"
+#include "s2/util/math/vector.h"  // IWYU pragma: export
+
+// An R1Interval represents a closed, bounded interval on the real line.
+// It is capable of representing the empty interval (containing no points)
+// and zero-length intervals (containing a single point).
+//
+// This class is intended to be copied by value as desired.  It uses
+// the default copy constructor and assignment operator.
+class R1Interval {
+ public:
+  // Constructor.  If lo > hi, the interval is empty.
+  R1Interval(double lo, double hi) : bounds_(lo, hi) {}
+
+  // The default constructor creates an empty interval.  (Any interval where
+  // lo > hi is considered to be empty.)
+  //
+  // Note: Don't construct an interval using the default constructor and
+  // set_lo()/set_hi(), since this technique doesn't work with S1Interval and
+  // is bad programming style anyways.  If you need to set both endpoints, use
+  // the constructor above:
+  //
+  //   lat_bounds_ = R1Interval(lat_lo, lat_hi);
+  R1Interval() : bounds_(1, 0) {}
+
+  // Returns an empty interval.
+  static inline R1Interval Empty() { return R1Interval(); }
+
+  // Convenience method to construct an interval containing a single point.
+  static R1Interval FromPoint(double p) { return R1Interval(p, p); }
+
+  // Convenience method to construct the minimal interval containing
+  // the two given points.  This is equivalent to starting with an empty
+  // interval and calling AddPoint() twice, but it is more efficient.
+  static R1Interval FromPointPair(double p1, double p2) {
+    if (p1 <= p2) {
+      return R1Interval(p1, p2);
+    } else {
+      return R1Interval(p2, p1);
+    }
+  }
+
+  // Accessors methods.
+  double lo() const { return bounds_[0]; }
+  double hi() const { return bounds_[1]; }
+
+  // Methods to modify one endpoint of an existing R1Interval.  Do not use
+  // these methods if you want to replace both endpoints of the interval; use
+  // a constructor instead.  For example:
+  //
+  //   *lat_bounds = R1Interval(lat_lo, lat_hi);
+  void set_lo(double p) { bounds_[0] = p; }
+  void set_hi(double p) { bounds_[1] = p; }
+
+  // Methods that allow the R1Interval to be accessed as a vector.  (The
+  // recommended style is to use lo() and hi() whenever possible, but these
+  // methods are useful when the endpoint to be selected is not constant.)
+  double operator[](int i) const { return bounds_[i]; }
+  double& operator[](int i) { return bounds_[i]; }
+  Vector2_d const& bounds() const { return bounds_; }
+  Vector2_d* mutable_bounds() { return &bounds_; }
+
+  // Return true if the interval is empty, i.e. it contains no points.
+  bool is_empty() const { return lo() > hi(); }
+
+  // Return the center of the interval.  For empty intervals,
+  // the result is arbitrary.
+  double GetCenter() const { return 0.5 * (lo() + hi()); }
+
+  // Return the length of the interval.  The length of an empty interval
+  // is negative.
+  double GetLength() const { return hi() - lo(); }
+
+  bool Contains(double p) const { return p >= lo() && p <= hi(); }
+
+  bool InteriorContains(double p) const { return p > lo() && p < hi(); }
+
+  // Return true if this interval contains the interval 'y'.
+  bool Contains(R1Interval const& y) const {
+    if (y.is_empty())
+      return true;
+    return y.lo() >= lo() && y.hi() <= hi();
+  }
+
+  // Return true if the interior of this interval contains the entire
+  // interval 'y' (including its boundary).
+  bool InteriorContains(R1Interval const& y) const {
+    if (y.is_empty())
+      return true;
+    return y.lo() > lo() && y.hi() < hi();
+  }
+
+  // Return true if this interval intersects the given interval,
+  // i.e. if they have any points in common.
+  bool Intersects(R1Interval const& y) const {
+    if (lo() <= y.lo()) {
+      return y.lo() <= hi() && y.lo() <= y.hi();
+    } else {
+      return lo() <= y.hi() && lo() <= hi();
+    }
+  }
+
+  // Return true if the interior of this interval intersects
+  // any point of the given interval (including its boundary).
+  bool InteriorIntersects(R1Interval const& y) const {
+    return y.lo() < hi() && lo() < y.hi() && lo() < hi() && y.lo() <= y.hi();
+  }
+
+  // Return the Hausdorff distance to the given interval 'y'. For two
+  // R1Intervals x and y, this distance is defined as
+  //     h(x, y) = max_{p in x} min_{q in y} d(p, q).
+  double GetDirectedHausdorffDistance(R1Interval const& y) const {
+    if (is_empty())
+      return 0.0;
+    if (y.is_empty())
+      return HUGE_VAL;
+    return std::max(0.0, std::max(hi() - y.hi(), y.lo() - lo()));
+  }
+
+  // Expand the interval so that it contains the given point "p".
+  void AddPoint(double p) {
+    if (is_empty()) {
+      set_lo(p);
+      set_hi(p);
+    } else if (p < lo()) {
+      set_lo(p);
+    }  // NOLINT
+    else if (p > hi()) {
+      set_hi(p);
+    }  // NOLINT
+  }
+
+  // Expand the interval so that it contains the given interval "y".
+  void AddInterval(R1Interval const& y) {
+    if (y.is_empty())
+      return;
+    if (is_empty()) {
+      *this = y;
+      return;
+    }
+    if (y.lo() < lo())
+      set_lo(y.lo());
+    if (y.hi() > hi())
+      set_hi(y.hi());
+  }
+
+  // Return the closest point in the interval to the given point "p".
+  // The interval must be non-empty.
+  double Project(double p) const {
+    DCHECK(!is_empty());
+    return std::max(lo(), std::min(hi(), p));
+  }
+
+  // Return an interval that has been expanded on each side by the given
+  // distance "margin".  If "margin" is negative, then shrink the interval on
+  // each side by "margin" instead.  The resulting interval may be empty.  Any
+  // expansion of an empty interval remains empty.
+  R1Interval Expanded(double margin) const {
+    if (is_empty())
+      return *this;
+    return R1Interval(lo() - margin, hi() + margin);
+  }
+
+  // Return the smallest interval that contains this interval and the
+  // given interval "y".
+  R1Interval Union(R1Interval const& y) const {
+    if (is_empty())
+      return y;
+    if (y.is_empty())
+      return *this;
+    return R1Interval(std::min(lo(), y.lo()), std::max(hi(), y.hi()));
+  }
+
+  // Return the intersection of this interval with the given interval.
+  // Empty intervals do not need to be special-cased.
+  R1Interval Intersection(R1Interval const& y) const {
+    return R1Interval(std::max(lo(), y.lo()), std::min(hi(), y.hi()));
+  }
+
+  // Return true if two intervals contain the same set of points.
+  bool operator==(R1Interval const& y) const {
+    return (lo() == y.lo() && hi() == y.hi()) || (is_empty() && y.is_empty());
+  }
+
+  // Return true if two intervals do not contain the same set of points.
+  bool operator!=(R1Interval const& y) const { return !operator==(y); }
+
+  // Return true if this interval can be transformed into the given interval
+  // by moving each endpoint by at most "max_error".  The empty interval is
+  // considered to be positioned arbitrarily on the real line, thus any
+  // interval with (length <= 2*max_error) matches the empty interval.
+  bool ApproxEquals(R1Interval const& y, double max_error = 1e-15) const {
+    if (is_empty())
+      return y.GetLength() <= 2 * max_error;
+    if (y.is_empty())
+      return GetLength() <= 2 * max_error;
+    return (std::fabs(y.lo() - lo()) <= max_error &&
+            std::fabs(y.hi() - hi()) <= max_error);
+  }
+
+ private:
+  Vector2_d bounds_;
+};
+
+inline std::ostream& operator<<(std::ostream& os, R1Interval const& x) {
+  return os << "[" << x.lo() << ", " << x.hi() << "]";
+}
+
+#endif  // S2_R1INTERVAL_H_
diff --git a/third_party/s2cellid/src/s2/r2.h b/third_party/s2cellid/src/s2/r2.h
new file mode 100644
index 0000000..3e785d8
--- /dev/null
+++ b/third_party/s2cellid/src/s2/r2.h
@@ -0,0 +1,26 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_R2_H_
+#define S2_R2_H_
+
+#include "s2/_fpcontractoff.h"
+#include "s2/util/math/vector.h"  // IWYU pragma: export
+
+using R2Point = Vector2_d;
+
+#endif  // S2_R2_H_
diff --git a/third_party/s2cellid/src/s2/r2rect.cc b/third_party/s2cellid/src/s2/r2rect.cc
new file mode 100644
index 0000000..0761e6f
--- /dev/null
+++ b/third_party/s2cellid/src/s2/r2rect.cc
@@ -0,0 +1,94 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/r2rect.h"
+
+#include <iosfwd>
+
+#include "base/logging.h"
+#include "s2/r1interval.h"
+#include "s2/r2.h"
+
+R2Rect R2Rect::FromCenterSize(R2Point const& center, R2Point const& size) {
+  return R2Rect(
+      R1Interval(center.x() - 0.5 * size.x(), center.x() + 0.5 * size.x()),
+      R1Interval(center.y() - 0.5 * size.y(), center.y() + 0.5 * size.y()));
+}
+
+R2Rect R2Rect::FromPointPair(R2Point const& p1, R2Point const& p2) {
+  return R2Rect(R1Interval::FromPointPair(p1.x(), p2.x()),
+                R1Interval::FromPointPair(p1.y(), p2.y()));
+}
+
+bool R2Rect::Contains(R2Rect const& other) const {
+  return x().Contains(other.x()) && y().Contains(other.y());
+}
+
+bool R2Rect::InteriorContains(R2Rect const& other) const {
+  return x().InteriorContains(other.x()) && y().InteriorContains(other.y());
+}
+
+bool R2Rect::Intersects(R2Rect const& other) const {
+  return x().Intersects(other.x()) && y().Intersects(other.y());
+}
+
+bool R2Rect::InteriorIntersects(R2Rect const& other) const {
+  return x().InteriorIntersects(other.x()) && y().InteriorIntersects(other.y());
+}
+
+void R2Rect::AddPoint(R2Point const& p) {
+  bounds_[0].AddPoint(p[0]);
+  bounds_[1].AddPoint(p[1]);
+}
+
+void R2Rect::AddRect(R2Rect const& other) {
+  bounds_[0].AddInterval(other[0]);
+  bounds_[1].AddInterval(other[1]);
+}
+
+R2Point R2Rect::Project(R2Point const& p) const {
+  return R2Point(x().Project(p.x()), y().Project(p.y()));
+}
+
+R2Rect R2Rect::Expanded(R2Point const& margin) const {
+  R1Interval xx = x().Expanded(margin.x());
+  R1Interval yy = y().Expanded(margin.y());
+  if (xx.is_empty() || yy.is_empty())
+    return Empty();
+  return R2Rect(xx, yy);
+}
+
+R2Rect R2Rect::Union(R2Rect const& other) const {
+  return R2Rect(x().Union(other.x()), y().Union(other.y()));
+}
+
+R2Rect R2Rect::Intersection(R2Rect const& other) const {
+  R1Interval xx = x().Intersection(other.x());
+  R1Interval yy = y().Intersection(other.y());
+  if (xx.is_empty() || yy.is_empty())
+    return Empty();
+  return R2Rect(xx, yy);
+}
+
+bool R2Rect::ApproxEquals(R2Rect const& other, double max_error) const {
+  return (x().ApproxEquals(other.x(), max_error) &&
+          y().ApproxEquals(other.y(), max_error));
+}
+
+std::ostream& operator<<(std::ostream& os, R2Rect const& r) {
+  return os << "[Lo" << r.lo() << ", Hi" << r.hi() << "]";
+}
diff --git a/third_party/s2cellid/src/s2/r2rect.h b/third_party/s2cellid/src/s2/r2rect.h
new file mode 100644
index 0000000..1aa82fc
--- /dev/null
+++ b/third_party/s2cellid/src/s2/r2rect.h
@@ -0,0 +1,234 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_R2RECT_H_
+#define S2_R2RECT_H_
+
+#include <iosfwd>
+
+#include "base/logging.h"
+#include "s2/_fpcontractoff.h"
+#include "s2/r1interval.h"
+#include "s2/r2.h"
+
+// An R2Rect represents a closed axis-aligned rectangle in the (x,y) plane.
+//
+// This class is intended to be copied by value as desired.  It uses
+// the default copy constructor and assignment operator, however it is
+// not a "plain old datatype" (POD) because it has virtual functions.
+class R2Rect {
+ public:
+  // Construct a rectangle from the given lower-left and upper-right points.
+  R2Rect(R2Point const& lo, R2Point const& hi);
+
+  // Construct a rectangle from the given intervals in x and y.  The two
+  // intervals must either be both empty or both non-empty.
+  R2Rect(R1Interval const& x, R1Interval const& y);
+
+  // The default constructor creates an empty R2Rect.
+  R2Rect();
+
+  // The canonical empty rectangle.  Use is_empty() to test for empty
+  // rectangles, since they have more than one representation.
+  static R2Rect Empty();
+
+  // Construct a rectangle from a center point and size in each dimension.
+  // Both components of size should be non-negative, i.e. this method cannot
+  // be used to create an empty rectangle.
+  static R2Rect FromCenterSize(R2Point const& center, R2Point const& size);
+
+  // Convenience method to construct a rectangle containing a single point.
+  static R2Rect FromPoint(R2Point const& p);
+
+  // Convenience method to construct the minimal bounding rectangle containing
+  // the two given points.  This is equivalent to starting with an empty
+  // rectangle and calling AddPoint() twice.  Note that it is different than
+  // the R2Rect(lo, hi) constructor, where the first point is always
+  // used as the lower-left corner of the resulting rectangle.
+  static R2Rect FromPointPair(R2Point const& p1, R2Point const& p2);
+
+  // Accessor methods.
+  R1Interval const& x() const { return bounds_[0]; }
+  R1Interval const& y() const { return bounds_[1]; }
+  R2Point lo() const { return R2Point(x().lo(), y().lo()); }
+  R2Point hi() const { return R2Point(x().hi(), y().hi()); }
+
+  // Methods that allow the R2Rect to be accessed as a vector.
+  R1Interval const& operator[](int i) const { return bounds_[i]; }
+  R1Interval& operator[](int i) { return bounds_[i]; }
+
+  // Return true if the rectangle is valid, which essentially just means
+  // that if the bound for either axis is empty then both must be.
+  bool is_valid() const;
+
+  // Return true if the rectangle is empty, i.e. it contains no points at all.
+  bool is_empty() const;
+
+  // Return the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order.
+  // Vertex 0 is in the lower-left corner.  For convenience, the argument is
+  // reduced modulo 4 to the range [0..3].
+  R2Point GetVertex(int k) const;
+
+  // Return the vertex in direction "i" along the x-axis (0=left, 1=right) and
+  // direction "j" along the y-axis (0=down, 1=up).  Equivalently, return the
+  // vertex constructed by selecting endpoint "i" of the x-interval (0=lo,
+  // 1=hi) and vertex "j" of the y-interval.
+  R2Point GetVertex(int i, int j) const;
+
+  // Return the center of the rectangle in (x,y)-space.
+  R2Point GetCenter() const;
+
+  // Return the width and height of this rectangle in (x,y)-space.  Empty
+  // rectangles have a negative width and height.
+  R2Point GetSize() const;
+
+  // Return true if the rectangle contains the given point.  Note that
+  // rectangles are closed regions, i.e. they contain their boundary.
+  bool Contains(R2Point const& p) const;
+
+  // Return true if and only if the given point is contained in the interior
+  // of the region (i.e. the region excluding its boundary).
+  bool InteriorContains(R2Point const& p) const;
+
+  // Return true if and only if the rectangle contains the given other
+  // rectangle.
+  bool Contains(R2Rect const& other) const;
+
+  // Return true if and only if the interior of this rectangle contains all
+  // points of the given other rectangle (including its boundary).
+  bool InteriorContains(R2Rect const& other) const;
+
+  // Return true if this rectangle and the given other rectangle have any
+  // points in common.
+  bool Intersects(R2Rect const& other) const;
+
+  // Return true if and only if the interior of this rectangle intersects
+  // any point (including the boundary) of the given other rectangle.
+  bool InteriorIntersects(R2Rect const& other) const;
+
+  // Expand the rectangle to include the given point.  The rectangle is
+  // expanded by the minimum amount possible.
+  void AddPoint(R2Point const& p);
+
+  // Expand the rectangle to include the given other rectangle.  This is the
+  // same as replacing the rectangle by the union of the two rectangles, but
+  // is somewhat more efficient.
+  void AddRect(R2Rect const& other);
+
+  // Return the closest point in the rectangle to the given point "p".
+  // The rectangle must be non-empty.
+  R2Point Project(R2Point const& p) const;
+
+  // Return a rectangle that has been expanded on each side in the x-direction
+  // by margin.x(), and on each side in the y-direction by margin.y().  If
+  // either margin is empty, then shrink the interval on the corresponding
+  // sides instead.  The resulting rectangle may be empty.  Any expansion of
+  // an empty rectangle remains empty.
+  R2Rect Expanded(R2Point const& margin) const;
+  R2Rect Expanded(double margin) const;
+
+  // Return the smallest rectangle containing the union of this rectangle and
+  // the given rectangle.
+  R2Rect Union(R2Rect const& other) const;
+
+  // Return the smallest rectangle containing the intersection of this
+  // rectangle and the given rectangle.
+  R2Rect Intersection(R2Rect const& other) const;
+
+  // Return true if two rectangles contains the same set of points.
+  bool operator==(R2Rect const& other) const;
+
+  // Return true if the x- and y-intervals of the two rectangles are the same
+  // up to the given tolerance (see r1interval.h for details).
+  bool ApproxEquals(R2Rect const& other, double max_error = 1e-15) const;
+
+ private:
+  R1Interval bounds_[2];
+};
+
+inline R2Rect::R2Rect(R2Point const& lo, R2Point const& hi) {
+  bounds_[0] = R1Interval(lo.x(), hi.x());
+  bounds_[1] = R1Interval(lo.y(), hi.y());
+  DCHECK(is_valid());
+}
+
+inline R2Rect::R2Rect(R1Interval const& x, R1Interval const& y) {
+  bounds_[0] = x;
+  bounds_[1] = y;
+  DCHECK(is_valid());
+}
+
+inline R2Rect::R2Rect() {
+  // The default R1Interval constructor creates an empty interval.
+  DCHECK(is_valid());
+}
+
+inline R2Rect R2Rect::Empty() {
+  return R2Rect(R1Interval::Empty(), R1Interval::Empty());
+}
+
+inline bool R2Rect::is_valid() const {
+  // The x/y ranges must either be both empty or both non-empty.
+  return x().is_empty() == y().is_empty();
+}
+
+inline bool R2Rect::is_empty() const {
+  return x().is_empty();
+}
+
+inline R2Rect R2Rect::FromPoint(R2Point const& p) {
+  return R2Rect(p, p);
+}
+
+inline R2Point R2Rect::GetVertex(int k) const {
+  // Twiddle bits to return the points in CCW order (lower left, lower right,
+  // upper right, upper left).
+  int j = (k >> 1) & 1;
+  return GetVertex(j ^ (k & 1), j);
+}
+
+inline R2Point R2Rect::GetVertex(int i, int j) const {
+  return R2Point(bounds_[0][i], bounds_[1][j]);
+}
+
+inline R2Point R2Rect::GetCenter() const {
+  return R2Point(x().GetCenter(), y().GetCenter());
+}
+
+inline R2Point R2Rect::GetSize() const {
+  return R2Point(x().GetLength(), y().GetLength());
+}
+
+inline bool R2Rect::Contains(R2Point const& p) const {
+  return x().Contains(p.x()) && y().Contains(p.y());
+}
+
+inline bool R2Rect::InteriorContains(R2Point const& p) const {
+  return x().InteriorContains(p.x()) && y().InteriorContains(p.y());
+}
+
+inline R2Rect R2Rect::Expanded(double margin) const {
+  return Expanded(R2Point(margin, margin));
+}
+
+inline bool R2Rect::operator==(R2Rect const& other) const {
+  return x() == other.x() && y() == other.y();
+}
+
+std::ostream& operator<<(std::ostream& os, R2Rect const& r);
+
+#endif  // S2_R2RECT_H_
diff --git a/third_party/s2cellid/src/s2/s1angle.cc b/third_party/s2cellid/src/s2/s1angle.cc
new file mode 100644
index 0000000..faf36ee
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s1angle.cc
@@ -0,0 +1,52 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s1angle.h"
+
+#include <cmath>
+#include <cstdio>
+#include <ostream>
+
+#include "s2/s2latlng.h"
+
+S1Angle::S1Angle(S2Point const& x, S2Point const& y) : radians_(x.Angle(y)) {}
+
+S1Angle::S1Angle(S2LatLng const& x, S2LatLng const& y)
+    : radians_(x.GetDistance(y).radians()) {}
+
+S1Angle S1Angle::Normalized() const {
+  S1Angle a(radians_);
+  a.Normalize();
+  return a;
+}
+
+void S1Angle::Normalize() {
+  radians_ = remainder(radians_, 2.0 * M_PI);
+  if (radians_ <= -M_PI)
+    radians_ = M_PI;
+}
+
+std::ostream& operator<<(std::ostream& os, S1Angle a) {
+  double degrees = a.degrees();
+  char buffer[13];
+  int sz = snprintf(buffer, sizeof(buffer), "%.7f", degrees);
+  if (sz >= 0 && sz < sizeof(buffer)) {
+    return os << buffer;
+  } else {
+    return os << degrees;
+  }
+}
diff --git a/third_party/s2cellid/src/s2/s1angle.h b/third_party/s2cellid/src/s2/s1angle.h
new file mode 100644
index 0000000..99f2a48e
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s1angle.h
@@ -0,0 +1,323 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S1ANGLE_H_
+#define S2_S1ANGLE_H_
+
+#include <cmath>
+#include <limits>
+#include <ostream>
+#include <type_traits>
+
+#include "s2/_fpcontractoff.h"
+#include "s2/util/math/mathutil.h"
+#include "s2/util/math/vector.h"
+
+class S2LatLng;
+// Avoid circular include of s2.h.
+// This must be a typedef rather than a type alias declaration because of SWIG.
+typedef Vector3_d S2Point;
+
+// This class represents a one-dimensional angle (as opposed to a
+// two-dimensional solid angle).  It has methods for converting angles to
+// or from radians, degrees, and the E5/E6/E7 representations (i.e. degrees
+// multiplied by 1e5/1e6/1e7 and rounded to the nearest integer).
+//
+// The internal representation is a double-precision value in radians, so
+// conversion to and from radians is exact.  Conversions between E5, E6, E7,
+// and Degrees are not always exact; for example, Degrees(3.1) is different
+// from E6(3100000) or E7(310000000).  However, the following properties are
+// guaranteed for any integer "n", provided that "n" is in the input range of
+// both functions:
+//
+//     Degrees(n) == E6(1000000 * n)
+//     Degrees(n) == E7(10000000 * n)
+//          E6(n) == E7(10 * n)
+//
+// The corresponding properties are *not* true for E5, so if you use E5 then
+// don't test for exact equality when comparing to other formats such as
+// Degrees or E7.
+//
+// The following conversions between degrees and radians are exact:
+//
+//          Degrees(180) == Radians(M_PI)
+//       Degrees(45 * k) == Radians(k * M_PI / 4)  for k == 0..8
+//
+// These identities also hold when the arguments are scaled up or down by any
+// power of 2.  Some similar identities are also true, for example,
+// Degrees(60) == Radians(M_PI / 3), but be aware that this type of identity
+// does not hold in general.  For example, Degrees(3) != Radians(M_PI / 60).
+//
+// Similarly, the conversion to radians means that Angle::Degrees(x).degrees()
+// does not always equal "x".  For example,
+//
+//         S1Angle::Degrees(45 * k).degrees() == 45 * k      for k == 0..8
+//   but       S1Angle::Degrees(60).degrees() != 60.
+//
+// This means that when testing for equality, you should allow for numerical
+// errors (EXPECT_DOUBLE_EQ) or convert to discrete E5/E6/E7 values first.
+//
+// CAVEAT: All of the above properties depend on "double" being the usual
+// 64-bit IEEE 754 type (which is true on almost all modern platforms).
+//
+// This class is intended to be copied by value as desired.  It uses
+// the default copy constructor and assignment operator.
+class S1Angle {
+ public:
+  // These methods construct S1Angle objects from their measure in radians
+  // or degrees.
+  static constexpr S1Angle Radians(double radians);
+  static constexpr S1Angle Degrees(double degrees);
+  static constexpr S1Angle E5(int32_t e5);
+  static constexpr S1Angle E6(int32_t e6);
+  static constexpr S1Angle E7(int32_t e7);
+
+  // Convenience functions -- to use when args have been fixed32s in protos.
+  //
+  // The arguments are static_cast into int32_t, so very large unsigned values
+  // are treated as negative numbers.
+  static constexpr S1Angle UnsignedE6(uint32_t e6);
+  static constexpr S1Angle UnsignedE7(uint32_t e7);
+
+  // The default constructor yields a zero angle.  This is useful for STL
+  // containers and class methods with output arguments.
+  constexpr S1Angle() : radians_(0) {}
+
+  // Return an angle larger than any finite angle.
+  static constexpr S1Angle Infinity();
+
+  // A explicit shorthand for the default constructor.
+  static constexpr S1Angle Zero();
+
+  // Return the angle between two points, which is also equal to the distance
+  // between these points on the unit sphere.  The points do not need to be
+  // normalized.
+  S1Angle(S2Point const& x, S2Point const& y);
+
+  // Like the constructor above, but return the angle (i.e., distance)
+  // between two S2LatLng points.  The result has a maximum error of
+  // 3.25 * DBL_EPSILON (or 2.5 * DBL_EPSILON for angles up to 1 radian).
+  S1Angle(S2LatLng const& x, S2LatLng const& y);
+
+  constexpr double radians() const;
+  constexpr double degrees() const;
+
+  int32_t e5() const;
+  int32_t e6() const;
+  int32_t e7() const;
+
+  // Return the absolute value of an angle.
+  S1Angle abs() const;
+
+  // Comparison operators.
+  friend bool operator==(S1Angle x, S1Angle y);
+  friend bool operator!=(S1Angle x, S1Angle y);
+  friend bool operator<(S1Angle x, S1Angle y);
+  friend bool operator>(S1Angle x, S1Angle y);
+  friend bool operator<=(S1Angle x, S1Angle y);
+  friend bool operator>=(S1Angle x, S1Angle y);
+
+  // Simple arithmetic operators for manipulating S1Angles.
+  friend S1Angle operator-(S1Angle a);
+  friend S1Angle operator+(S1Angle a, S1Angle b);
+  friend S1Angle operator-(S1Angle a, S1Angle b);
+  friend S1Angle operator*(double m, S1Angle a);
+  friend S1Angle operator*(S1Angle a, double m);
+  friend S1Angle operator/(S1Angle a, double m);
+  friend double operator/(S1Angle a, S1Angle b);
+  S1Angle& operator+=(S1Angle a);
+  S1Angle& operator-=(S1Angle a);
+  S1Angle& operator*=(double m);
+  S1Angle& operator/=(double m);
+
+  // Trigonmetric functions (not necessary but slightly more convenient).
+  friend double sin(S1Angle a);
+  friend double cos(S1Angle a);
+  friend double tan(S1Angle a);
+
+  // Return the angle normalized to the range (-180, 180] degrees.
+  S1Angle Normalized() const;
+
+  // Normalize this angle to the range (-180, 180] degrees.
+  void Normalize();
+
+  // When S1Angle is used as a key in one of the btree container types
+  // (util/btree), indicate that linear rather than binary search should be
+  // used.  This is much faster when the comparison function is cheap.
+  typedef std::true_type goog_btree_prefer_linear_node_search;
+
+ private:
+  explicit constexpr S1Angle(double radians) : radians_(radians) {}
+  double radians_;
+};
+
+//////////////////   Implementation details follow   ////////////////////
+
+inline constexpr S1Angle S1Angle::Infinity() {
+  return S1Angle(std::numeric_limits<double>::infinity());
+}
+
+inline constexpr S1Angle S1Angle::Zero() {
+  return S1Angle(0);
+}
+
+inline constexpr double S1Angle::radians() const {
+  return radians_;
+}
+
+inline constexpr double S1Angle::degrees() const {
+  return (180 / M_PI) * radians_;
+}
+
+// Note that the E5, E6, and E7 conversion involve two multiplications rather
+// than one.  This is mainly for backwards compatibility (changing this would
+// break many tests), but it does have the nice side effect that conversions
+// between Degrees, E6, and E7 are exact when the arguments are integers.
+
+inline int32_t S1Angle::e5() const {
+  return MathUtil::FastIntRound(1e5 * degrees());
+}
+
+inline int32_t S1Angle::e6() const {
+  return MathUtil::FastIntRound(1e6 * degrees());
+}
+
+inline int32_t S1Angle::e7() const {
+  return MathUtil::FastIntRound(1e7 * degrees());
+}
+
+inline S1Angle S1Angle::abs() const {
+  return S1Angle(std::fabs(radians_));
+}
+
+inline bool operator==(S1Angle x, S1Angle y) {
+  return x.radians() == y.radians();
+}
+
+inline bool operator!=(S1Angle x, S1Angle y) {
+  return x.radians() != y.radians();
+}
+
+inline bool operator<(S1Angle x, S1Angle y) {
+  return x.radians() < y.radians();
+}
+
+inline bool operator>(S1Angle x, S1Angle y) {
+  return x.radians() > y.radians();
+}
+
+inline bool operator<=(S1Angle x, S1Angle y) {
+  return x.radians() <= y.radians();
+}
+
+inline bool operator>=(S1Angle x, S1Angle y) {
+  return x.radians() >= y.radians();
+}
+
+inline S1Angle operator-(S1Angle a) {
+  return S1Angle::Radians(-a.radians());
+}
+
+inline S1Angle operator+(S1Angle a, S1Angle b) {
+  return S1Angle::Radians(a.radians() + b.radians());
+}
+
+inline S1Angle operator-(S1Angle a, S1Angle b) {
+  return S1Angle::Radians(a.radians() - b.radians());
+}
+
+inline S1Angle operator*(double m, S1Angle a) {
+  return S1Angle::Radians(m * a.radians());
+}
+
+inline S1Angle operator*(S1Angle a, double m) {
+  return S1Angle::Radians(m * a.radians());
+}
+
+inline S1Angle operator/(S1Angle a, double m) {
+  return S1Angle::Radians(a.radians() / m);
+}
+
+inline double operator/(S1Angle a, S1Angle b) {
+  return a.radians() / b.radians();
+}
+
+inline S1Angle& S1Angle::operator+=(S1Angle a) {
+  radians_ += a.radians();
+  return *this;
+}
+
+inline S1Angle& S1Angle::operator-=(S1Angle a) {
+  radians_ -= a.radians();
+  return *this;
+}
+
+inline S1Angle& S1Angle::operator*=(double m) {
+  radians_ *= m;
+  return *this;
+}
+
+inline S1Angle& S1Angle::operator/=(double m) {
+  radians_ /= m;
+  return *this;
+}
+
+inline double sin(S1Angle a) {
+  return sin(a.radians());
+}
+
+inline double cos(S1Angle a) {
+  return cos(a.radians());
+}
+
+inline double tan(S1Angle a) {
+  return tan(a.radians());
+}
+
+inline constexpr S1Angle S1Angle::Radians(double radians) {
+  return S1Angle(radians);
+}
+
+inline constexpr S1Angle S1Angle::Degrees(double degrees) {
+  return S1Angle((M_PI / 180) * degrees);
+}
+
+inline constexpr S1Angle S1Angle::E5(int32_t e5) {
+  return Degrees(1e-5 * e5);
+}
+
+inline constexpr S1Angle S1Angle::E6(int32_t e6) {
+  return Degrees(1e-6 * e6);
+}
+
+inline constexpr S1Angle S1Angle::E7(int32_t e7) {
+  return Degrees(1e-7 * e7);
+}
+
+inline constexpr S1Angle S1Angle::UnsignedE6(uint32_t e6) {
+  return E6(static_cast<int32_t>(e6));
+}
+
+inline constexpr S1Angle S1Angle::UnsignedE7(uint32_t e7) {
+  return E7(static_cast<int32_t>(e7));
+}
+
+// Writes the angle in degrees with 7 digits of precision after the
+// decimal point, e.g. "17.3745904".
+std::ostream& operator<<(std::ostream& os, S1Angle a);
+
+#endif  // S2_S1ANGLE_H_
diff --git a/third_party/s2cellid/src/s2/s2cellid.cc b/third_party/s2cellid/src/s2/s2cellid.cc
new file mode 100644
index 0000000..1e99cf2
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s2cellid.cc
@@ -0,0 +1,603 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2cellid.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <cstring>
+#include <iosfwd>
+#include <vector>
+
+#include <mutex>
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "s2/r1interval.h"
+#include "s2/s2latlng.h"
+
+using S2::internal::kSwapMask;
+using S2::internal::kInvertMask;
+using S2::internal::kPosToIJ;
+using S2::internal::kPosToOrientation;
+using std::max;
+using std::min;
+using std::vector;
+
+// The following lookup tables are used to convert efficiently between an
+// (i,j) cell index and the corresponding position along the Hilbert curve.
+// "lookup_pos" maps 4 bits of "i", 4 bits of "j", and 2 bits representing the
+// orientation of the current cell into 8 bits representing the order in which
+// that subcell is visited by the Hilbert curve, plus 2 bits indicating the
+// new orientation of the Hilbert curve within that subcell.  (Cell
+// orientations are represented as combination of kSwapMask and kInvertMask.)
+//
+// "lookup_ij" is an inverted table used for mapping in the opposite
+// direction.
+//
+// We also experimented with looking up 16 bits at a time (14 bits of position
+// plus 2 of orientation) but found that smaller lookup tables gave better
+// performance.  (2KB fits easily in the primary cache.)
+
+// Values for these constants are *declared* in the *.h file. Even though
+// the declaration specifies a value for the constant, that declaration
+// is not a *definition* of storage for the value. Because the values are
+// supplied in the declaration, we don't need the values here. Failing to
+// define storage causes link errors for any code that tries to take the
+// address of one of these values.
+int const S2CellId::kFaceBits;
+int const S2CellId::kNumFaces;
+int const S2CellId::kMaxLevel;
+int const S2CellId::kPosBits;
+int const S2CellId::kMaxSize;
+
+static int const kLookupBits = 4;
+static uint16_t lookup_pos[1 << (2 * kLookupBits + 2)];
+static uint16_t lookup_ij[1 << (2 * kLookupBits + 2)];
+
+static void InitLookupCell(int level,
+                           int i,
+                           int j,
+                           int orig_orientation,
+                           int pos,
+                           int orientation) {
+  if (level == kLookupBits) {
+    int ij = (i << kLookupBits) + j;
+    lookup_pos[(ij << 2) + orig_orientation] = (pos << 2) + orientation;
+    lookup_ij[(pos << 2) + orig_orientation] = (ij << 2) + orientation;
+  } else {
+    level++;
+    i <<= 1;
+    j <<= 1;
+    pos <<= 2;
+    int const* r = kPosToIJ[orientation];
+    InitLookupCell(level, i + (r[0] >> 1), j + (r[0] & 1), orig_orientation,
+                   pos, orientation ^ kPosToOrientation[0]);
+    InitLookupCell(level, i + (r[1] >> 1), j + (r[1] & 1), orig_orientation,
+                   pos + 1, orientation ^ kPosToOrientation[1]);
+    InitLookupCell(level, i + (r[2] >> 1), j + (r[2] & 1), orig_orientation,
+                   pos + 2, orientation ^ kPosToOrientation[2]);
+    InitLookupCell(level, i + (r[3] >> 1), j + (r[3] & 1), orig_orientation,
+                   pos + 3, orientation ^ kPosToOrientation[3]);
+  }
+}
+
+static std::once_flag flag;
+inline static void MaybeInit() {
+  std::call_once(flag, [] {
+    InitLookupCell(0, 0, 0, 0, 0, 0);
+    InitLookupCell(0, 0, 0, kSwapMask, 0, kSwapMask);
+    InitLookupCell(0, 0, 0, kInvertMask, 0, kInvertMask);
+    InitLookupCell(0, 0, 0, kSwapMask | kInvertMask, 0,
+                   kSwapMask | kInvertMask);
+  });
+}
+
+S2CellId S2CellId::advance(int64_t steps) const {
+  if (steps == 0)
+    return *this;
+
+  // We clamp the number of steps if necessary to ensure that we do not
+  // advance past the End() or before the Begin() of this level.  Note that
+  // min_steps and max_steps always fit in a signed 64-bit integer.
+
+  int step_shift = 2 * (kMaxLevel - level()) + 1;
+  if (steps < 0) {
+    int64_t min_steps = -static_cast<int64_t>(id_ >> step_shift);
+    if (steps < min_steps)
+      steps = min_steps;
+  } else {
+    int64_t max_steps = (kWrapOffset + lsb() - id_) >> step_shift;
+    if (steps > max_steps)
+      steps = max_steps;
+  }
+  // If steps is negative, then shifting it left has undefined behavior.
+  // Cast to uint64_t for a 2's complement answer.
+  return S2CellId(id_ + (static_cast<uint64_t>(steps) << step_shift));
+}
+
+int64_t S2CellId::distance_from_begin() const {
+  const int step_shift = 2 * (kMaxLevel - level()) + 1;
+  return id_ >> step_shift;
+}
+
+S2CellId S2CellId::advance_wrap(int64_t steps) const {
+  DCHECK(is_valid());
+  if (steps == 0)
+    return *this;
+
+  int step_shift = 2 * (kMaxLevel - level()) + 1;
+  if (steps < 0) {
+    int64_t min_steps = -static_cast<int64_t>(id_ >> step_shift);
+    if (steps < min_steps) {
+      int64_t step_wrap = kWrapOffset >> step_shift;
+      steps %= step_wrap;
+      if (steps < min_steps)
+        steps += step_wrap;
+    }
+  } else {
+    // Unlike advance(), we don't want to return End(level).
+    int64_t max_steps = (kWrapOffset - id_) >> step_shift;
+    if (steps > max_steps) {
+      int64_t step_wrap = kWrapOffset >> step_shift;
+      steps %= step_wrap;
+      if (steps > max_steps)
+        steps -= step_wrap;
+    }
+  }
+  return S2CellId(id_ + (static_cast<uint64_t>(steps) << step_shift));
+}
+
+S2CellId S2CellId::maximum_tile(S2CellId const limit) const {
+  S2CellId id = *this;
+  S2CellId start = id.range_min();
+  if (start >= limit.range_min())
+    return limit;
+
+  if (id.range_max() >= limit) {
+    // The cell is too large.  Shrink it.  Note that when generating coverings
+    // of S2CellId ranges, this loop usually executes only once.  Also because
+    // id.range_min() < limit.range_min(), we will always exit the loop by the
+    // time we reach a leaf cell.
+    do {
+      id = id.child(0);
+    } while (id.range_max() >= limit);
+    return id;
+  }
+  // The cell may be too small.  Grow it if necessary.  Note that generally
+  // this loop only iterates once.
+  while (!id.is_face()) {
+    S2CellId parent = id.parent();
+    if (parent.range_min() != start || parent.range_max() >= limit)
+      break;
+    id = parent;
+  }
+  return id;
+}
+
+int S2CellId::GetCommonAncestorLevel(S2CellId other) const {
+  // Basically we find the first bit position at which the two S2CellIds
+  // differ and convert that to a level.  The max() below is necessary for the
+  // case where one S2CellId is a descendant of the other.
+  uint64_t bits = max(id() ^ other.id(), max(lsb(), other.lsb()));
+
+  // Compute the position of the most significant bit, and then map
+  // {0} -> 30, {1,2} -> 29, {3,4} -> 28, ... , {59,60} -> 0, {61,62,63} -> -1.
+  return max(60 - Bits::FindMSBSetNonZero64(bits), -1) >> 1;
+}
+
+// Print the num_digits low order hex digits.
+static std::string HexFormatString(uint64_t val, size_t num_digits) {
+  std::string result(num_digits, ' ');
+  for (; num_digits--; val >>= 4)
+    result[num_digits] = "0123456789abcdef"[val & 0xF];
+  return result;
+}
+
+std::string S2CellId::ToToken() const {
+  // Simple implementation: print the id in hex without trailing zeros.
+  // Using hex has the advantage that the tokens are case-insensitive, all
+  // characters are alphanumeric, no characters require any special escaping
+  // in queries for most indexing systems, and it's easy to compare cell
+  // tokens against the feature ids of the corresponding features.
+  //
+  // Using base 64 would produce slightly shorter tokens, but for typical cell
+  // sizes used during indexing (up to level 15 or so) the average savings
+  // would be less than 2 bytes per cell which doesn't seem worth it.
+
+  // "0" with trailing 0s stripped is the empty std::string, which is not a
+  // reasonable token.  Encode as "X".
+  if (id_ == 0)
+    return "X";
+  size_t const num_zero_digits = Bits::FindLSBSetNonZero64(id_) / 4;
+  return HexFormatString(id_ >> (4 * num_zero_digits), 16 - num_zero_digits);
+}
+
+S2CellId S2CellId::FromToken(const char* token, size_t length) {
+  if (length > 16)
+    return S2CellId::None();
+  uint64_t id = 0;
+  for (int i = 0, pos = 60; i < length; ++i, pos -= 4) {
+    uint64_t d;
+    if ('0' <= token[i] && token[i] <= '9') {
+      d = token[i] - '0';
+    } else if ('a' <= token[i] && token[i] <= 'f') {
+      d = token[i] - 'a' + 10;
+    } else if ('A' <= token[i] && token[i] <= 'F') {
+      d = token[i] - 'A' + 10;
+    } else {
+      return S2CellId::None();
+    }
+    id |= d << pos;
+  }
+  return S2CellId(id);
+}
+
+S2CellId S2CellId::FromToken(std::string const& token) {
+  return FromToken(token.data(), token.size());
+}
+
+S2CellId S2CellId::FromFaceIJ(int face, int i, int j) {
+  // Initialization if not done yet
+  MaybeInit();
+
+  // Optimization notes:
+  //  - Non-overlapping bit fields can be combined with either "+" or "|".
+  //    Generally "+" seems to produce better code, but not always.
+
+  // Note that this value gets shifted one bit to the left at the end
+  // of the function.
+  uint64_t n = static_cast<uint64_t>(face) << (kPosBits - 1);
+
+  // Alternating faces have opposite Hilbert curve orientations; this
+  // is necessary in order for all faces to have a right-handed
+  // coordinate system.
+  uint64_t bits = (face & kSwapMask);
+
+// Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert
+// curve position.  The lookup table transforms a 10-bit key of the form
+// "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the
+// letters [ijpo] denote bits of "i", "j", Hilbert curve position, and
+// Hilbert curve orientation respectively.
+#define GET_BITS(k)                                                 \
+  do {                                                              \
+    int const mask = (1 << kLookupBits) - 1;                        \
+    bits += ((i >> (k * kLookupBits)) & mask) << (kLookupBits + 2); \
+    bits += ((j >> (k * kLookupBits)) & mask) << 2;                 \
+    bits = lookup_pos[bits];                                        \
+    n |= (bits >> 2) << (k * 2 * kLookupBits);                      \
+    bits &= (kSwapMask | kInvertMask);                              \
+  } while (0)
+
+  GET_BITS(7);
+  GET_BITS(6);
+  GET_BITS(5);
+  GET_BITS(4);
+  GET_BITS(3);
+  GET_BITS(2);
+  GET_BITS(1);
+  GET_BITS(0);
+#undef GET_BITS
+
+  return S2CellId(n * 2 + 1);
+}
+
+S2CellId::S2CellId(S2Point const& p) {
+  double u, v;
+  int face = S2::XYZtoFaceUV(p, &u, &v);
+  int i = S2::STtoIJ(S2::UVtoST(u));
+  int j = S2::STtoIJ(S2::UVtoST(v));
+  id_ = FromFaceIJ(face, i, j).id();
+}
+
+S2CellId::S2CellId(S2LatLng const& ll) : S2CellId(ll.ToPoint()) {}
+
+int S2CellId::ToFaceIJOrientation(int* pi, int* pj, int* orientation) const {
+  // Initialization if not done yet
+  MaybeInit();
+
+  int i = 0, j = 0;
+  int face = this->face();
+  int bits = (face & kSwapMask);
+
+// Each iteration maps 8 bits of the Hilbert curve position into
+// 4 bits of "i" and "j".  The lookup table transforms a key of the
+// form "ppppppppoo" to a value of the form "iiiijjjjoo", where the
+// letters [ijpo] represents bits of "i", "j", the Hilbert curve
+// position, and the Hilbert curve orientation respectively.
+//
+// On the first iteration we need to be careful to clear out the bits
+// representing the cube face.
+#define GET_BITS(k)                                                           \
+  do {                                                                        \
+    int const nbits = (k == 7) ? (kMaxLevel - 7 * kLookupBits) : kLookupBits; \
+    bits += (static_cast<int>(id_ >> (k * 2 * kLookupBits + 1)) &             \
+             ((1 << (2 * nbits)) - 1))                                        \
+            << 2;                                                             \
+    bits = lookup_ij[bits];                                                   \
+    i += (bits >> (kLookupBits + 2)) << (k * kLookupBits);                    \
+    j += ((bits >> 2) & ((1 << kLookupBits) - 1)) << (k * kLookupBits);       \
+    bits &= (kSwapMask | kInvertMask);                                        \
+  } while (0)
+
+  GET_BITS(7);
+  GET_BITS(6);
+  GET_BITS(5);
+  GET_BITS(4);
+  GET_BITS(3);
+  GET_BITS(2);
+  GET_BITS(1);
+  GET_BITS(0);
+#undef GET_BITS
+
+  *pi = i;
+  *pj = j;
+
+  if (orientation != nullptr) {
+    // The position of a non-leaf cell at level "n" consists of a prefix of
+    // 2*n bits that identifies the cell, followed by a suffix of
+    // 2*(kMaxLevel-n)+1 bits of the form 10*.  If n==kMaxLevel, the suffix is
+    // just "1" and has no effect.  Otherwise, it consists of "10", followed
+    // by (kMaxLevel-n-1) repetitions of "00", followed by "0".  The "10" has
+    // no effect, while each occurrence of "00" has the effect of reversing
+    // the kSwapMask bit.
+    DCHECK_EQ(0, kPosToOrientation[2]);
+    DCHECK_EQ(kSwapMask, kPosToOrientation[0]);
+    if (lsb() & static_cast<uint64_t>(0x1111111111111110)) {
+      bits ^= kSwapMask;
+    }
+    *orientation = bits;
+  }
+  return face;
+}
+
+S2Point S2CellId::ToPointRaw() const {
+  int si, ti;
+  int face = GetCenterSiTi(&si, &ti);
+  return S2::FaceSiTitoXYZ(face, si, ti);
+}
+
+S2LatLng S2CellId::ToLatLng() const {
+  return S2LatLng(ToPointRaw());
+}
+
+R2Point S2CellId::GetCenterST() const {
+  int si, ti;
+  GetCenterSiTi(&si, &ti);
+  return R2Point(S2::SiTitoST(si), S2::SiTitoST(ti));
+}
+
+R2Point S2CellId::GetCenterUV() const {
+  int si, ti;
+  GetCenterSiTi(&si, &ti);
+  return R2Point(S2::STtoUV(S2::SiTitoST(si)), S2::STtoUV(S2::SiTitoST(ti)));
+}
+
+R2Rect S2CellId::IJLevelToBoundUV(int ij[2], int level) {
+  R2Rect bound;
+  int cell_size = GetSizeIJ(level);
+  for (int d = 0; d < 2; ++d) {
+    int ij_lo = ij[d] & -cell_size;
+    int ij_hi = ij_lo + cell_size;
+    bound[d][0] = S2::STtoUV(S2::IJtoSTMin(ij_lo));
+    bound[d][1] = S2::STtoUV(S2::IJtoSTMin(ij_hi));
+  }
+  return bound;
+}
+
+R2Rect S2CellId::GetBoundST() const {
+  double size = GetSizeST();
+  return R2Rect::FromCenterSize(GetCenterST(), R2Point(size, size));
+}
+
+R2Rect S2CellId::GetBoundUV() const {
+  int ij[2];
+  ToFaceIJOrientation(&ij[0], &ij[1], nullptr);
+  return IJLevelToBoundUV(ij, level());
+}
+
+// This is a helper function for ExpandedByDistanceUV().
+//
+// Given an edge of the form (u,v0)-(u,v1), let max_v = max(abs(v0), abs(v1)).
+// This method returns a new u-coordinate u' such that the distance from the
+// line u=u' to the given edge (u,v0)-(u,v1) is exactly the given distance
+// (which is specified as the sine of the angle corresponding to the distance).
+static double ExpandEndpoint(double u, double max_v, double sin_dist) {
+  // This is based on solving a spherical right triangle, similar to the
+  // calculation in S2Cap::GetRectBound.
+  double sin_u_shift =
+      sin_dist * sqrt((1 + u * u + max_v * max_v) / (1 + u * u));
+  double cos_u_shift = sqrt(1 - sin_u_shift * sin_u_shift);
+  // The following is an expansion of tan(atan(u) + asin(sin_u_shift)).
+  return (cos_u_shift * u + sin_u_shift) / (cos_u_shift - sin_u_shift * u);
+}
+
+/* static */
+R2Rect S2CellId::ExpandedByDistanceUV(R2Rect const& uv, S1Angle distance) {
+  // Expand each of the four sides of the rectangle just enough to include all
+  // points within the given distance of that side.  (The rectangle may be
+  // expanded by a different amount in (u,v)-space on each side.)
+  double u0 = uv[0][0], u1 = uv[0][1], v0 = uv[1][0], v1 = uv[1][1];
+  double max_u = std::max(std::abs(u0), std::abs(u1));
+  double max_v = std::max(std::abs(v0), std::abs(v1));
+  double sin_dist = sin(distance);
+  return R2Rect(R1Interval(ExpandEndpoint(u0, max_v, -sin_dist),
+                           ExpandEndpoint(u1, max_v, sin_dist)),
+                R1Interval(ExpandEndpoint(v0, max_u, -sin_dist),
+                           ExpandEndpoint(v1, max_u, sin_dist)));
+}
+
+S2CellId S2CellId::FromFaceIJWrap(int face, int i, int j) {
+  // Convert i and j to the coordinates of a leaf cell just beyond the
+  // boundary of this face.  This prevents 32-bit overflow in the case
+  // of finding the neighbors of a face cell.
+  i = max(-1, min(kMaxSize, i));
+  j = max(-1, min(kMaxSize, j));
+
+  // We want to wrap these coordinates onto the appropriate adjacent face.
+  // The easiest way to do this is to convert the (i,j) coordinates to (x,y,z)
+  // (which yields a point outside the normal face boundary), and then call
+  // S2::XYZtoFaceUV() to project back onto the correct face.
+  //
+  // The code below converts (i,j) to (si,ti), and then (si,ti) to (u,v) using
+  // the linear projection (u=2*s-1 and v=2*t-1).  (The code further below
+  // converts back using the inverse projection, s=0.5*(u+1) and t=0.5*(v+1).
+  // Any projection would work here, so we use the simplest.)  We also clamp
+  // the (u,v) coordinates so that the point is barely outside the
+  // [-1,1]x[-1,1] face rectangle, since otherwise the reprojection step
+  // (which divides by the new z coordinate) might change the other
+  // coordinates enough so that we end up in the wrong leaf cell.
+  static const double kScale = 1.0 / kMaxSize;
+  static const double kLimit = 1.0 + DBL_EPSILON;
+  // The arithmetic below is designed to avoid 32-bit integer overflows.
+  DCHECK_EQ(0, kMaxSize % 2);
+  double u = max(-kLimit, min(kLimit, kScale * (2 * (i - kMaxSize / 2) + 1)));
+  double v = max(-kLimit, min(kLimit, kScale * (2 * (j - kMaxSize / 2) + 1)));
+
+  // Find the leaf cell coordinates on the adjacent face, and convert
+  // them to a cell id at the appropriate level.
+  face = S2::XYZtoFaceUV(S2::FaceUVtoXYZ(face, u, v), &u, &v);
+  return FromFaceIJ(face, S2::STtoIJ(0.5 * (u + 1)), S2::STtoIJ(0.5 * (v + 1)));
+}
+
+inline S2CellId S2CellId::FromFaceIJSame(int face,
+                                         int i,
+                                         int j,
+                                         bool same_face) {
+  if (same_face)
+    return S2CellId::FromFaceIJ(face, i, j);
+  else
+    return S2CellId::FromFaceIJWrap(face, i, j);
+}
+
+void S2CellId::GetEdgeNeighbors(S2CellId neighbors[4]) const {
+  int i, j;
+  int level = this->level();
+  int size = GetSizeIJ(level);
+  int face = ToFaceIJOrientation(&i, &j, nullptr);
+
+  // Edges 0, 1, 2, 3 are in the down, right, up, left directions.
+  neighbors[0] = FromFaceIJSame(face, i, j - size, j - size >= 0).parent(level);
+  neighbors[1] =
+      FromFaceIJSame(face, i + size, j, i + size < kMaxSize).parent(level);
+  neighbors[2] =
+      FromFaceIJSame(face, i, j + size, j + size < kMaxSize).parent(level);
+  neighbors[3] = FromFaceIJSame(face, i - size, j, i - size >= 0).parent(level);
+}
+
+void S2CellId::AppendVertexNeighbors(int level,
+                                     vector<S2CellId>* output) const {
+  // "level" must be strictly less than this cell's level so that we can
+  // determine which vertex this cell is closest to.
+  DCHECK_LT(level, this->level());
+  int i, j;
+  int face = ToFaceIJOrientation(&i, &j, nullptr);
+
+  // Determine the i- and j-offsets to the closest neighboring cell in each
+  // direction.  This involves looking at the next bit of "i" and "j" to
+  // determine which quadrant of this->parent(level) this cell lies in.
+  int halfsize = GetSizeIJ(level + 1);
+  int size = halfsize << 1;
+  bool isame, jsame;
+  int ioffset, joffset;
+  if (i & halfsize) {
+    ioffset = size;
+    isame = (i + size) < kMaxSize;
+  } else {
+    ioffset = -size;
+    isame = (i - size) >= 0;
+  }
+  if (j & halfsize) {
+    joffset = size;
+    jsame = (j + size) < kMaxSize;
+  } else {
+    joffset = -size;
+    jsame = (j - size) >= 0;
+  }
+
+  output->push_back(parent(level));
+  output->push_back(FromFaceIJSame(face, i + ioffset, j, isame).parent(level));
+  output->push_back(FromFaceIJSame(face, i, j + joffset, jsame).parent(level));
+  // If i- and j- edge neighbors are *both* on a different face, then this
+  // vertex only has three neighbors (it is one of the 8 cube vertices).
+  if (isame || jsame) {
+    output->push_back(
+        FromFaceIJSame(face, i + ioffset, j + joffset, isame && jsame)
+            .parent(level));
+  }
+}
+
+void S2CellId::AppendAllNeighbors(int nbr_level,
+                                  vector<S2CellId>* output) const {
+  DCHECK_GE(nbr_level, level());
+  int i, j;
+  int face = ToFaceIJOrientation(&i, &j, nullptr);
+
+  // Find the coordinates of the lower left-hand leaf cell.  We need to
+  // normalize (i,j) to a known position within the cell because nbr_level
+  // may be larger than this cell's level.
+  int size = GetSizeIJ();
+  i &= -size;
+  j &= -size;
+
+  int nbr_size = GetSizeIJ(nbr_level);
+  DCHECK_LE(nbr_size, size);
+
+  // We compute the top-bottom, left-right, and diagonal neighbors in one
+  // pass.  The loop test is at the end of the loop to avoid 32-bit overflow.
+  for (int k = -nbr_size;; k += nbr_size) {
+    bool same_face;
+    if (k < 0) {
+      same_face = (j + k >= 0);
+    } else if (k >= size) {
+      same_face = (j + k < kMaxSize);
+    } else {
+      same_face = true;
+      // Top and bottom neighbors.
+      output->push_back(FromFaceIJSame(face, i + k, j - nbr_size, j - size >= 0)
+                            .parent(nbr_level));
+      output->push_back(
+          FromFaceIJSame(face, i + k, j + size, j + size < kMaxSize)
+              .parent(nbr_level));
+    }
+    // Left, right, and diagonal neighbors.
+    output->push_back(
+        FromFaceIJSame(face, i - nbr_size, j + k, same_face && i - size >= 0)
+            .parent(nbr_level));
+    output->push_back(
+        FromFaceIJSame(face, i + size, j + k, same_face && i + size < kMaxSize)
+            .parent(nbr_level));
+    if (k >= size)
+      break;
+  }
+}
+
+std::string S2CellId::ToString() const {
+  if (!is_valid()) {
+    return base::StringPrintf("Invalid: %016lx", id());
+  }
+  std::string out = base::StringPrintf("%d/", face());
+  for (int current_level = 1; current_level <= level(); ++current_level) {
+    // Avoid dependencies of SimpleItoA, and slowness of StringAppendF &
+    // std::to_string.
+    out += "0123"[child_position(current_level)];
+  }
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& os, S2CellId id) {
+  return os << id.ToString();
+}
diff --git a/third_party/s2cellid/src/s2/s2cellid.h b/third_party/s2cellid/src/s2/s2cellid.h
new file mode 100644
index 0000000..580de7a
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s2cellid.h
@@ -0,0 +1,662 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2CELLID_H_
+#define S2_S2CELLID_H_
+
+#include <cstddef>
+#include <functional>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "s2/_fpcontractoff.h"
+#include "s2/r2.h"
+#include "s2/r2rect.h"
+#include "s2/s1angle.h"
+#include "s2/s2coords.h"
+#include "s2/util/bits/bits.h"
+
+class S2LatLng;
+
+// An S2CellId is a 64-bit unsigned integer that uniquely identifies a
+// cell in the S2 cell decomposition.  It has the following format:
+//
+//   id = [face][face_pos]
+//
+//   face:     a 3-bit number (range 0..5) encoding the cube face.
+//
+//   face_pos: a 61-bit number encoding the position of the center of this
+//             cell along the Hilbert curve over this face (see the Wiki
+//             pages for details).
+//
+// Sequentially increasing cell ids follow a continuous space-filling curve
+// over the entire sphere.  They have the following properties:
+//
+//  - The id of a cell at level k consists of a 3-bit face number followed
+//    by k bit pairs that recursively select one of the four children of
+//    each cell.  The next bit is always 1, and all other bits are 0.
+//    Therefore, the level of a cell is determined by the position of its
+//    lowest-numbered bit that is turned on (for a cell at level k, this
+//    position is 2 * (kMaxLevel - k).)
+//
+//  - The id of a parent cell is at the midpoint of the range of ids spanned
+//    by its children (or by its descendants at any level).
+//
+// Leaf cells are often used to represent points on the unit sphere, and
+// this class provides methods for converting directly between these two
+// representations.  For cells that represent 2D regions rather than
+// discrete point, it is better to use the S2Cell class.
+//
+// This class is intended to be copied by value as desired.  It uses
+// the default copy constructor and assignment operator.
+class S2CellId {
+ public:
+  // The extra position bit (61 rather than 60) let us encode each cell as its
+  // Hilbert curve position at the cell center (which is halfway along the
+  // portion of the Hilbert curve that fills that cell).
+  static int const kFaceBits = 3;
+  static int const kNumFaces = 6;
+  static int const kMaxLevel = S2::kMaxCellLevel;  // Valid levels: 0..kMaxLevel
+  static int const kPosBits = 2 * kMaxLevel + 1;
+  static int const kMaxSize = 1 << kMaxLevel;
+
+  explicit constexpr S2CellId(uint64_t id) : id_(id) {}
+
+  // Construct a leaf cell containing the given point "p".  Usually there is
+  // is exactly one such cell, but for points along the edge of a cell, any
+  // adjacent cell may be (deterministically) chosen.  This is because
+  // S2CellIds are considered to be closed sets.  The returned cell will
+  // always contain the given point, i.e.
+  //
+  //   S2Cell(S2CellId(p)).Contains(p)
+  //
+  // is always true.  The point "p" does not need to be normalized.
+  explicit S2CellId(S2Point const& p);
+
+  // Construct a leaf cell containing the given normalized S2LatLng.
+  explicit S2CellId(S2LatLng const& ll);
+
+  // The default constructor returns an invalid cell id.
+  constexpr S2CellId() : id_(0) {}
+  static constexpr S2CellId None() { return S2CellId(); }
+
+  // Returns an invalid cell id guaranteed to be larger than any
+  // valid cell id.  Useful for creating indexes.
+  static constexpr S2CellId Sentinel() { return S2CellId(~uint64_t(0)); }
+
+  // Return the cell corresponding to a given S2 cube face.
+  static S2CellId FromFace(int face);
+
+  // Return a cell given its face (range 0..5), Hilbert curve position within
+  // that face (an unsigned integer with S2CellId::kPosBits bits), and level
+  // (range 0..kMaxLevel).  The given position will be modified to correspond
+  // to the Hilbert curve position at the center of the returned cell.  This
+  // is a static function rather than a constructor in order to indicate what
+  // the arguments represent.
+  static S2CellId FromFacePosLevel(int face, uint64_t pos, int level);
+
+  // Return the direction vector corresponding to the center of the given
+  // cell.  The vector returned by ToPointRaw is not necessarily unit length.
+  // This method returns the same result as S2Cell::GetCenter().
+  //
+  // The maximum directional error in ToPoint() (compared to the exact
+  // mathematical result) is 1.5 * DBL_EPSILON radians, and the maximum length
+  // error is 2 * DBL_EPSILON (the same as Normalize).
+  S2Point ToPoint() const { return ToPointRaw().Normalize(); }
+  S2Point ToPointRaw() const;
+
+  // Return the center of the cell in (s,t) coordinates (see s2.h).
+  R2Point GetCenterST() const;
+
+  // Return the edge length of this cell in (s,t)-space.
+  double GetSizeST() const;
+
+  // Return the edge length in (s,t)-space of cells at the given level.
+  static double GetSizeST(int level);
+
+  // Return the bounds of this cell in (s,t)-space.
+  R2Rect GetBoundST() const;
+
+  // Return the center of the cell in (u,v) coordinates (see s2.h).  Note that
+  // the center of the cell is defined as the point at which it is recursively
+  // subdivided into four children; in general, it is not at the midpoint of
+  // the (u,v) rectangle covered by the cell.
+  R2Point GetCenterUV() const;
+
+  // Return the bounds of this cell in (u,v)-space.
+  R2Rect GetBoundUV() const;
+
+  // Expand a rectangle in (u,v)-space so that it contains all points within
+  // the given distance of the boundary, and return the smallest such
+  // rectangle.  If the distance is negative, then instead shrink this
+  // rectangle so that it excludes all points within the given absolute
+  // distance of the boundary.
+  //
+  // Distances are measured *on the sphere*, not in (u,v)-space.  For example,
+  // you can use this method to expand the (u,v)-bound of an S2CellId so that
+  // it contains all points within 5km of the original cell.  You can then
+  // test whether a point lies within the expanded bounds like this:
+  //
+  //   R2Point uv;
+  //   if (S2::FaceXYZtoUV(face, point, &uv) && bound.Contains(uv)) { ... }
+  //
+  // Limitations:
+  //
+  //  - Because the rectangle is drawn on one of the six cube-face planes
+  //    (i.e., {x,y,z} = +/-1), it can cover at most one hemisphere.  This
+  //    limits the maximum amount that a rectangle can be expanded.  For
+  //    example, S2CellId bounds can be expanded safely by at most 45 degrees
+  //    (about 5000 km on the Earth's surface).
+  //
+  //  - The implementation is not exact for negative distances.  The resulting
+  //    rectangle will exclude all points within the given distance of the
+  //    boundary but may be slightly smaller than necessary.
+  static R2Rect ExpandedByDistanceUV(R2Rect const& uv, S1Angle distance);
+
+  // Return the (face, si, ti) coordinates of the center of the cell.  Note
+  // that although (si,ti) coordinates span the range [0,2**31] in general,
+  // the cell center coordinates are always in the range [1,2**31-1] and
+  // therefore can be represented using a signed 32-bit integer.
+  int GetCenterSiTi(int* psi, int* pti) const;
+
+  // Return the S2LatLng corresponding to the center of the given cell.
+  S2LatLng ToLatLng() const;
+
+  // The 64-bit unique identifier for this cell.
+  uint64_t id() const { return id_; }
+
+  // Return true if id() represents a valid cell.
+  bool is_valid() const;
+
+  // Which cube face this cell belongs to, in the range 0..5.
+  int face() const;
+
+  // The position of the cell center along the Hilbert curve over this face,
+  // in the range 0..(2**kPosBits-1).
+  uint64_t pos() const;
+
+  // Return the subdivision level of the cell (range 0..kMaxLevel).
+  int level() const;
+
+  // Return the edge length of this cell in (i,j)-space.
+  int GetSizeIJ() const;
+
+  // Like the above, but return the size of cells at the given level.
+  static int GetSizeIJ(int level);
+
+  // Return true if this is a leaf cell (more efficient than checking
+  // whether level() == kMaxLevel).
+  bool is_leaf() const;
+
+  // Return true if this is a top-level face cell (more efficient than
+  // checking whether level() == 0).
+  bool is_face() const;
+
+  // Return the child position (0..3) of this cell within its parent.
+  // REQUIRES: level() >= 1.
+  int child_position() const;
+
+  // Return the child position (0..3) of this cell's ancestor at the given
+  // level within its parent.  For example, child_position(1) returns the
+  // position of this cell's level-1 ancestor within its top-level face cell.
+  // REQUIRES: 1 <= level <= this->level().
+  int child_position(int level) const;
+
+  // These methods return the range of cell ids that are contained within this
+  // cell (including itself).  The range is *inclusive* (i.e. test using >=
+  // and <=) and the return values of both methods are valid leaf cell ids.
+  // In other words, a.contains(b) if and only if
+  //
+  //     (b >= a.range_min() && b <= a.range_max())
+  //
+  // If you want to iterate through all the descendants of this cell at a
+  // particular level, use child_begin(level) and child_end(level) instead.
+  // Also see maximum_tile(), which can be used to iterate through a range of
+  // cells using S2CellIds at different levels that are as large as possible.
+  //
+  // If you need to convert the range to a semi-open interval [min, limit)
+  // (e.g., in order to use a key-value store that only supports semi-open
+  // range queries), do not attempt to define "limit" as range_max.next().
+  // The problem is that leaf S2CellIds are 2 units apart, so the semi-open
+  // interval [min, limit) includes an additional value (range_max.id() + 1)
+  // which is happens to be a valid S2CellId about one-third of the time and
+  // is *never* contained by this cell.  (It always correpsonds to a cell that
+  // is larger than this one.)  You can define "limit" as (range_max.id() + 1)
+  // if necessary (which is not always a valid S2CellId but can still be used
+  // with FromToken/ToToken), or you can convert range_max() to the key space
+  // of your key-value store and define "limit" as Successor(key).
+  //
+  // Note that Sentinel().range_min() == Sentinel.range_max() == Sentinel().
+  S2CellId range_min() const;
+  S2CellId range_max() const;
+
+  // Return true if the given cell is contained within this one.
+  bool contains(S2CellId other) const;
+
+  // Return true if the given cell intersects this one.
+  bool intersects(S2CellId other) const;
+
+  // Return the cell at the previous level or at the given level (which must
+  // be less than or equal to the current level).
+  S2CellId parent() const;
+  S2CellId parent(int level) const;
+
+  // Return the immediate child of this cell at the given traversal order
+  // position (in the range 0 to 3).  This cell must not be a leaf cell.
+  S2CellId child(int position) const;
+
+  // Iterator-style methods for traversing the immediate children of a cell or
+  // all of the children at a given level (greater than or equal to the current
+  // level).  Note that the end value is exclusive, just like standard STL
+  // iterators, and may not even be a valid cell id.  You should iterate using
+  // code like this:
+  //
+  //   for(S2CellId c = id.child_begin(); c != id.child_end(); c = c.next())
+  //     ...
+  //
+  // The convention for advancing the iterator is "c = c.next()" rather
+  // than "++c" to avoid possible confusion with incrementing the
+  // underlying 64-bit cell id.
+  S2CellId child_begin() const;
+  S2CellId child_begin(int level) const;
+  S2CellId child_end() const;
+  S2CellId child_end(int level) const;
+
+  // Return the next/previous cell at the same level along the Hilbert curve.
+  // Works correctly when advancing from one face to the next, but
+  // does *not* wrap around from the last face to the first or vice versa.
+  S2CellId next() const;
+  S2CellId prev() const;
+
+  // This method advances or retreats the indicated number of steps along the
+  // Hilbert curve at the current level, and returns the new position.  The
+  // position is never advanced past End() or before Begin().
+  S2CellId advance(int64_t steps) const;
+
+  // Returns the number of steps that this cell is from Begin(level()). The
+  // return value is always non-negative.
+  int64_t distance_from_begin() const;
+
+  // Like next() and prev(), but these methods wrap around from the last face
+  // to the first and vice versa.  They should *not* be used for iteration in
+  // conjunction with child_begin(), child_end(), Begin(), or End().  The
+  // input must be a valid cell id.
+  S2CellId next_wrap() const;
+  S2CellId prev_wrap() const;
+
+  // This method advances or retreats the indicated number of steps along the
+  // Hilbert curve at the current level, and returns the new position.  The
+  // position wraps between the first and last faces as necessary.  The input
+  // must be a valid cell id.
+  S2CellId advance_wrap(int64_t steps) const;
+
+  // Return the largest cell with the same range_min() and such that
+  // range_max() < limit.range_min().  Returns "limit" if no such cell exists.
+  // This method can be used to generate a small set of S2CellIds that covers
+  // a given range (a "tiling").  This example shows how to generate a tiling
+  // for a semi-open range of leaf cells [start, limit):
+  //
+  //   for (S2CellId id = start.maximum_tile(limit);
+  //        id != limit; id = id.next().maximum_tile(limit)) { ... }
+  //
+  // Note that in general the cells in the tiling will be of different sizes;
+  // they gradually get larger (near the middle of the range) and then
+  // gradually get smaller (as "limit" is approached).
+  S2CellId maximum_tile(S2CellId limit) const;
+
+  // Return the level of the "lowest common ancestor" of this cell and
+  // "other".  Note that because of the way that cell levels are numbered,
+  // this is actually the *highest* level of any shared ancestor.  Return -1
+  // if the two cells do not have any common ancestor (i.e., they are from
+  // different faces).
+  int GetCommonAncestorLevel(S2CellId other) const;
+
+  // Iterator-style methods for traversing all the cells along the Hilbert
+  // curve at a given level (across all 6 faces of the cube).  Note that the
+  // end value is exclusive (just like standard STL iterators), and is not a
+  // valid cell id.
+  static S2CellId Begin(int level);
+  static S2CellId End(int level);
+
+  // Methods to encode and decode cell ids to compact text strings suitable
+  // for display or indexing.  Cells at lower levels (i.e. larger cells) are
+  // encoded into fewer characters.  The maximum token length is 16.
+  //
+  // ToToken() returns a std::string by value for convenience; the compiler
+  // does this without intermediate copying in most cases.
+  //
+  // These methods guarantee that FromToken(ToToken(x)) == x even when
+  // "x" is an invalid cell id.  All tokens are alphanumeric strings.
+  // FromToken() returns S2CellId::None() for malformed inputs.
+  std::string ToToken() const;
+  static S2CellId FromToken(const char* token, size_t length);
+  static S2CellId FromToken(std::string const& token);
+
+  // Creates a debug human readable std::string. Used for << and available for
+  // direct usage as well.
+  std::string ToString() const;
+
+  // Return the four cells that are adjacent across the cell's four edges.
+  // Neighbors are returned in the order defined by S2Cell::GetEdge.  All
+  // neighbors are guaranteed to be distinct.
+  void GetEdgeNeighbors(S2CellId neighbors[4]) const;
+
+  // Return the neighbors of closest vertex to this cell at the given level,
+  // by appending them to "output".  Normally there are four neighbors, but
+  // the closest vertex may only have three neighbors if it is one of the 8
+  // cube vertices.
+  //
+  // Requires: level < this->level(), so that we can determine which vertex is
+  // closest (in particular, level == kMaxLevel is not allowed).
+  void AppendVertexNeighbors(int level, std::vector<S2CellId>* output) const;
+
+  // Append all neighbors of this cell at the given level to "output".  Two
+  // cells X and Y are neighbors if their boundaries intersect but their
+  // interiors do not.  In particular, two cells that intersect at a single
+  // point are neighbors.  Note that for cells adjacent to a face vertex, the
+  // same neighbor may be appended more than once.
+  //
+  // REQUIRES: nbr_level >= this->level().
+  void AppendAllNeighbors(int nbr_level, std::vector<S2CellId>* output) const;
+
+  /////////////////////////////////////////////////////////////////////
+  // Low-level methods.
+
+  // Return a leaf cell given its cube face (range 0..5) and
+  // i- and j-coordinates (see s2.h).
+  static S2CellId FromFaceIJ(int face, int i, int j);
+
+  // Return the (face, i, j) coordinates for the leaf cell corresponding to
+  // this cell id.  Since cells are represented by the Hilbert curve position
+  // at the center of the cell, the returned (i,j) for non-leaf cells will be
+  // a leaf cell adjacent to the cell center.  If "orientation" is non-nullptr,
+  // also return the Hilbert curve orientation for the current cell.
+  int ToFaceIJOrientation(int* pi, int* pj, int* orientation) const;
+
+  // Return the lowest-numbered bit that is on for this cell id, which is
+  // equal to (uint64_t(1) << (2 * (kMaxLevel - level))).  So for example,
+  // a.lsb() <= b.lsb() if and only if a.level() >= b.level(), but the
+  // first test is more efficient.
+  uint64_t lsb() const { return id_ & -id_; }
+
+  // Return the lowest-numbered bit that is on for cells at the given level.
+  static uint64_t lsb_for_level(int level) {
+    return uint64_t(1) << (2 * (kMaxLevel - level));
+  }
+
+  // Return the bound in (u,v)-space for the cell at the given level containing
+  // the leaf cell with the given (i,j)-coordinates.
+  static R2Rect IJLevelToBoundUV(int ij[2], int level);
+
+  // When S2CellId is used as a key in one of the btree container types
+  // (util/btree), indicate that linear rather than binary search should be
+  // used.  This is much faster when the comparison function is cheap.
+  typedef std::true_type goog_btree_prefer_linear_node_search;
+
+ private:
+  // This is the offset required to wrap around from the beginning of the
+  // Hilbert curve to the end or vice versa; see next_wrap() and prev_wrap().
+  static uint64_t const kWrapOffset = uint64_t(kNumFaces) << kPosBits;
+
+  // Given a face and a point (i,j) where either i or j is outside the valid
+  // range [0..kMaxSize-1], this function first determines which neighboring
+  // face "contains" (i,j), and then returns the leaf cell on that face which
+  // is adjacent to the given face and whose distance from (i,j) is minimal.
+  static S2CellId FromFaceIJWrap(int face, int i, int j);
+
+  // Inline helper function that calls FromFaceIJ if "same_face" is true,
+  // or FromFaceIJWrap if "same_face" is false.
+  static S2CellId FromFaceIJSame(int face, int i, int j, bool same_face);
+
+  uint64_t id_;
+} ATTRIBUTE_PACKED;  // Necessary so that structures containing S2CellId's can
+                     // be ATTRIBUTE_PACKED.
+
+inline bool operator==(S2CellId x, S2CellId y) {
+  return x.id() == y.id();
+}
+
+inline bool operator!=(S2CellId x, S2CellId y) {
+  return x.id() != y.id();
+}
+
+inline bool operator<(S2CellId x, S2CellId y) {
+  return x.id() < y.id();
+}
+
+inline bool operator>(S2CellId x, S2CellId y) {
+  return x.id() > y.id();
+}
+
+inline bool operator<=(S2CellId x, S2CellId y) {
+  return x.id() <= y.id();
+}
+
+inline bool operator>=(S2CellId x, S2CellId y) {
+  return x.id() >= y.id();
+}
+
+inline S2CellId S2CellId::FromFace(int face) {
+  return S2CellId((static_cast<uint64_t>(face) << kPosBits) + lsb_for_level(0));
+}
+
+inline S2CellId S2CellId::FromFacePosLevel(int face, uint64_t pos, int level) {
+  S2CellId cell((static_cast<uint64_t>(face) << kPosBits) + (pos | 1));
+  return cell.parent(level);
+}
+
+inline int S2CellId::GetCenterSiTi(int* psi, int* pti) const {
+  // First we compute the discrete (i,j) coordinates of a leaf cell contained
+  // within the given cell.  Given that cells are represented by the Hilbert
+  // curve position corresponding at their center, it turns out that the cell
+  // returned by ToFaceIJOrientation is always one of two leaf cells closest
+  // to the center of the cell (unless the given cell is a leaf cell itself,
+  // in which case there is only one possibility).
+  //
+  // Given a cell of size s >= 2 (i.e. not a leaf cell), and letting (imin,
+  // jmin) be the coordinates of its lower left-hand corner, the leaf cell
+  // returned by ToFaceIJOrientation() is either (imin + s/2, jmin + s/2)
+  // (imin + s/2 - 1, jmin + s/2 - 1).  The first case is the one we want.
+  // We can distinguish these two cases by looking at the low bit of "i" or
+  // "j".  In the second case the low bit is one, unless s == 2 (i.e. the
+  // level just above leaf cells) in which case the low bit is zero.
+  //
+  // In the code below, the expression ((i ^ (int(id_) >> 2)) & 1) is true
+  // if we are in the second case described above.
+  int i, j;
+  int face = ToFaceIJOrientation(&i, &j, nullptr);
+  int delta = is_leaf() ? 1 : ((i ^ (static_cast<int>(id_) >> 2)) & 1) ? 2 : 0;
+
+  // Note that (2 * {i,j} + delta) will never overflow a 32-bit integer.
+  *psi = 2 * i + delta;
+  *pti = 2 * j + delta;
+  return face;
+}
+
+inline bool S2CellId::is_valid() const {
+  return (face() < kNumFaces &&
+          (lsb() & static_cast<uint64_t>(0x1555555555555555)));
+}
+
+inline int S2CellId::face() const {
+  return id_ >> kPosBits;
+}
+
+inline uint64_t S2CellId::pos() const {
+  return id_ & (~uint64_t(0) >> kFaceBits);
+}
+
+inline int S2CellId::level() const {
+  // A special case for leaf cells is not worthwhile.
+  return kMaxLevel - (Bits::FindLSBSetNonZero64(id_) >> 1);
+}
+
+inline int S2CellId::GetSizeIJ() const {
+  return GetSizeIJ(level());
+}
+
+inline double S2CellId::GetSizeST() const {
+  return GetSizeST(level());
+}
+
+inline int S2CellId::GetSizeIJ(int level) {
+  return 1 << (kMaxLevel - level);
+}
+
+inline double S2CellId::GetSizeST(int level) {
+  return S2::IJtoSTMin(GetSizeIJ(level));
+}
+
+inline bool S2CellId::is_leaf() const {
+  return int(id_) & 1;
+}
+
+inline bool S2CellId::is_face() const {
+  return (id_ & (lsb_for_level(0) - 1)) == 0;
+}
+
+inline int S2CellId::child_position() const {
+  // No need for a special implementation; the compiler optimizes this well.
+  return child_position(level());
+}
+
+inline int S2CellId::child_position(int level) const {
+  DCHECK(is_valid());
+  DCHECK_GE(level, 1);
+  DCHECK_LE(level, this->level());
+  return static_cast<int>(id_ >> (2 * (kMaxLevel - level) + 1)) & 3;
+}
+
+inline S2CellId S2CellId::range_min() const {
+  return S2CellId(id_ - (lsb() - 1));
+}
+
+inline S2CellId S2CellId::range_max() const {
+  return S2CellId(id_ + (lsb() - 1));
+}
+
+inline bool S2CellId::contains(S2CellId other) const {
+  DCHECK(is_valid());
+  DCHECK(other.is_valid());
+  return other >= range_min() && other <= range_max();
+}
+
+inline bool S2CellId::intersects(S2CellId other) const {
+  DCHECK(is_valid());
+  DCHECK(other.is_valid());
+  return other.range_min() <= range_max() && other.range_max() >= range_min();
+}
+
+inline S2CellId S2CellId::parent(int level) const {
+  DCHECK(is_valid());
+  DCHECK_GE(level, 0);
+  DCHECK_LE(level, this->level());
+  uint64_t new_lsb = lsb_for_level(level);
+  return S2CellId((id_ & -new_lsb) | new_lsb);
+}
+
+inline S2CellId S2CellId::parent() const {
+  DCHECK(is_valid());
+  DCHECK(!is_face());
+  uint64_t new_lsb = lsb() << 2;
+  return S2CellId((id_ & -new_lsb) | new_lsb);
+}
+
+inline S2CellId S2CellId::child(int position) const {
+  DCHECK(is_valid());
+  DCHECK(!is_leaf());
+  // To change the level, we need to move the least-significant bit two
+  // positions downward.  We do this by subtracting (4 * new_lsb) and adding
+  // new_lsb.  Then to advance to the given child cell, we add
+  // (2 * position * new_lsb).
+  uint64_t new_lsb = lsb() >> 2;
+  return S2CellId(id_ + (2 * position + 1 - 4) * new_lsb);
+}
+
+inline S2CellId S2CellId::child_begin() const {
+  DCHECK(is_valid());
+  DCHECK(!is_leaf());
+  uint64_t old_lsb = lsb();
+  return S2CellId(id_ - old_lsb + (old_lsb >> 2));
+}
+
+inline S2CellId S2CellId::child_begin(int level) const {
+  DCHECK(is_valid());
+  DCHECK_GE(level, this->level());
+  DCHECK_LE(level, kMaxLevel);
+  return S2CellId(id_ - lsb() + lsb_for_level(level));
+}
+
+inline S2CellId S2CellId::child_end() const {
+  DCHECK(is_valid());
+  DCHECK(!is_leaf());
+  uint64_t old_lsb = lsb();
+  return S2CellId(id_ + old_lsb + (old_lsb >> 2));
+}
+
+inline S2CellId S2CellId::child_end(int level) const {
+  DCHECK(is_valid());
+  DCHECK_GE(level, this->level());
+  DCHECK_LE(level, kMaxLevel);
+  return S2CellId(id_ + lsb() + lsb_for_level(level));
+}
+
+inline S2CellId S2CellId::next() const {
+  return S2CellId(id_ + (lsb() << 1));
+}
+
+inline S2CellId S2CellId::prev() const {
+  return S2CellId(id_ - (lsb() << 1));
+}
+
+inline S2CellId S2CellId::next_wrap() const {
+  DCHECK(is_valid());
+  S2CellId n = next();
+  if (n.id_ < kWrapOffset)
+    return n;
+  return S2CellId(n.id_ - kWrapOffset);
+}
+
+inline S2CellId S2CellId::prev_wrap() const {
+  DCHECK(is_valid());
+  S2CellId p = prev();
+  if (p.id_ < kWrapOffset)
+    return p;
+  return S2CellId(p.id_ + kWrapOffset);
+}
+
+inline S2CellId S2CellId::Begin(int level) {
+  return FromFace(0).child_begin(level);
+}
+
+inline S2CellId S2CellId::End(int level) {
+  return FromFace(5).child_end(level);
+}
+
+std::ostream& operator<<(std::ostream& os, S2CellId id);
+
+// Hasher for S2CellId.
+// Example use: std::unordered_map<S2CellId, int, S2CellIdHash>.
+struct S2CellIdHash {
+  size_t operator()(S2CellId id) const {
+    return std::hash<uint64_t>()(id.id());
+  }
+};
+
+#endif  // S2_S2CELLID_H_
diff --git a/third_party/s2cellid/src/s2/s2coords-internal.h b/third_party/s2cellid/src/s2/s2coords-internal.h
new file mode 100644
index 0000000..b3bf4a2
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s2coords-internal.h
@@ -0,0 +1,71 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_S2COORDS_INTERNAL_H_
+#define S2_S2COORDS_INTERNAL_H_
+// Author: ericv@google.com (Eric Veach)
+
+namespace S2 {
+namespace internal {
+
+// The canonical Hilbert traversal order looks like an inverted 'U':
+// the subcells are visited in the order (0,0), (0,1), (1,1), (1,0).
+// The following tables encode the traversal order for various
+// orientations of the Hilbert curve (axes swapped and/or directions
+// of the axes reversed).
+
+// Together these flags define a cell orientation.  If 'kSwapMask'
+// is true, then canonical traversal order is flipped around the
+// diagonal (i.e. i and j are swapped with each other).  If
+// 'kInvertMask' is true, then the traversal order is rotated by 180
+// degrees (i.e. the bits of i and j are inverted, or equivalently,
+// the axis directions are reversed).
+int constexpr kSwapMask = 0x01;
+int constexpr kInvertMask = 0x02;
+
+// kIJtoPos[orientation][ij] -> pos
+//
+// Given a cell orientation and the (i,j)-index of a subcell (0=(0,0),
+// 1=(0,1), 2=(1,0), 3=(1,1)), return the order in which this subcell is
+// visited by the Hilbert curve (a position in the range [0..3]).
+extern int const kIJtoPos[4][4];
+
+// kPosToIJ[orientation][pos] -> ij
+//
+// Return the (i,j) index of the subcell at the given position 'pos' in the
+// Hilbert curve traversal order with the given orientation.  This is the
+// inverse of the previous table:
+//
+//   kPosToIJ[r][kIJtoPos[r][ij]] == ij
+extern int const kPosToIJ[4][4];
+
+// kPosToOrientation[pos] -> orientation_modifier
+//
+// Return a modifier indicating how the orientation of the child subcell
+// with the given traversal position [0..3] is related to the orientation
+// of the parent cell.  The modifier should be XOR-ed with the parent
+// orientation to obtain the curve orientation in the child.
+extern int const kPosToOrientation[4];
+
+// The U,V,W axes for each face.
+extern double const kFaceUVWAxes[6][3][3];
+
+// The precomputed neighbors of each face (see GetUVWFace).
+extern int const kFaceUVWFaces[6][3][2];
+
+}  // namespace internal
+}  // namespace S2
+
+#endif  // S2_S2COORDS_INTERNAL_H_
diff --git a/third_party/s2cellid/src/s2/s2coords.cc b/third_party/s2cellid/src/s2/s2coords.cc
new file mode 100644
index 0000000..ce9bccfb9
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s2coords.cc
@@ -0,0 +1,117 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2coords.h"
+
+#include "s2/util/bits/bits.h"
+
+namespace S2 {
+
+namespace internal {
+
+// Define the "extern" constants in s2coords-internal.h.
+
+static_assert(kSwapMask == 0x01 && kInvertMask == 0x02, "masks changed");
+
+// kIJtoPos[orientation][ij] -> pos
+int const kIJtoPos[4][4] = {
+    // (0,0) (0,1) (1,0) (1,1)
+    {0, 1, 3, 2},  // canonical order
+    {0, 3, 1, 2},  // axes swapped
+    {2, 3, 1, 0},  // bits inverted
+    {2, 1, 3, 0},  // swapped & inverted
+};
+
+// kPosToIJ[orientation][pos] -> ij
+int const kPosToIJ[4][4] = {
+    // 0  1  2  3
+    {0, 1, 3, 2},  // canonical order:    (0,0), (0,1), (1,1), (1,0)
+    {0, 2, 3, 1},  // axes swapped:       (0,0), (1,0), (1,1), (0,1)
+    {3, 2, 0, 1},  // bits inverted:      (1,1), (1,0), (0,0), (0,1)
+    {3, 1, 0, 2},  // swapped & inverted: (1,1), (0,1), (0,0), (1,0)
+};
+
+// kPosToOrientation[pos] -> orientation_modifier
+int const kPosToOrientation[4] = {
+    kSwapMask, 0, 0, kInvertMask + kSwapMask,
+};
+
+int const kFaceUVWFaces[6][3][2] = {
+    {{4, 1}, {5, 2}, {3, 0}}, {{0, 3}, {5, 2}, {4, 1}},
+    {{0, 3}, {1, 4}, {5, 2}}, {{2, 5}, {1, 4}, {0, 3}},
+    {{2, 5}, {3, 0}, {1, 4}}, {{4, 1}, {3, 0}, {2, 5}}};
+
+double const kFaceUVWAxes[6][3][3] = {
+    {{0, 1, 0}, {0, 0, 1}, {1, 0, 0}},   {{-1, 0, 0}, {0, 0, 1}, {0, 1, 0}},
+    {{-1, 0, 0}, {0, -1, 0}, {0, 0, 1}}, {{0, 0, -1}, {0, -1, 0}, {-1, 0, 0}},
+    {{0, 0, -1}, {1, 0, 0}, {0, -1, 0}}, {{0, 1, 0}, {1, 0, 0}, {0, 0, -1}}};
+
+}  // namespace internal
+
+S2Point FaceXYZtoUVW(int face, S2Point const& p) {
+  // The result coordinates are simply the dot products of P with the (u,v,w)
+  // axes for the given face (see kFaceUVWAxes).
+  switch (face) {
+    case 0:
+      return S2Point(p.y(), p.z(), p.x());
+    case 1:
+      return S2Point(-p.x(), p.z(), p.y());
+    case 2:
+      return S2Point(-p.x(), -p.y(), p.z());
+    case 3:
+      return S2Point(-p.z(), -p.y(), -p.x());
+    case 4:
+      return S2Point(-p.z(), p.x(), -p.y());
+    default:
+      return S2Point(p.y(), p.x(), -p.z());
+  }
+}
+
+int XYZtoFaceSiTi(S2Point const& p,
+                  int* face,
+                  unsigned int* si,
+                  unsigned int* ti) {
+  double u, v;
+  *face = XYZtoFaceUV(p, &u, &v);
+  *si = STtoSiTi(UVtoST(u));
+  *ti = STtoSiTi(UVtoST(v));
+  // If the levels corresponding to si,ti are not equal, then p is not a cell
+  // center.  The si,ti values 0 and kMaxSiTi need to be handled specially
+  // because they do not correspond to cell centers at any valid level; they
+  // are mapped to level -1 by the code below.
+  int level = kMaxCellLevel - Bits::FindLSBSetNonZero(*si | kMaxSiTi);
+  if (level < 0 ||
+      level != kMaxCellLevel - Bits::FindLSBSetNonZero(*ti | kMaxSiTi)) {
+    return -1;
+  }
+  DCHECK_LE(level, kMaxCellLevel);
+  // In infinite precision, this test could be changed to ST == SiTi. However,
+  // due to rounding errors, UVtoST(XYZtoFaceUV(FaceUVtoXYZ(STtoUV(...)))) is
+  // not idempotent. On the other hand, center_raw is computed exactly the same
+  // way p was originally computed (if it is indeed the center of an S2Cell):
+  // the comparison can be exact.
+  S2Point center = FaceSiTitoXYZ(*face, *si, *ti).Normalize();
+  return p == center ? level : -1;
+}
+
+S2Point FaceSiTitoXYZ(int face, unsigned int si, unsigned int ti) {
+  double u = STtoUV(SiTitoST(si));
+  double v = STtoUV(SiTitoST(ti));
+  return FaceUVtoXYZ(face, u, v);
+}
+
+}  // namespace S2
diff --git a/third_party/s2cellid/src/s2/s2coords.h b/third_party/s2cellid/src/s2/s2coords.h
new file mode 100644
index 0000000..5b0961b
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s2coords.h
@@ -0,0 +1,500 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+//
+// This file contains documentation of the various coordinate systems used
+// throughout the library.  Most importantly, S2 defines a framework for
+// decomposing the unit sphere into a hierarchy of "cells".  Each cell is a
+// quadrilateral bounded by four geodesics.  The top level of the hierarchy is
+// obtained by projecting the six faces of a cube onto the unit sphere, and
+// lower levels are obtained by subdividing each cell into four children
+// recursively.  Cells are numbered such that sequentially increasing cells
+// follow a continuous space-filling curve over the entire sphere.  The
+// transformation is designed to make the cells at each level fairly uniform
+// in size.
+//
+//
+////////////////////////// S2Cell Decomposition /////////////////////////
+//
+// The following methods define the cube-to-sphere projection used by
+// the S2Cell decomposition.
+//
+// In the process of converting a latitude-longitude pair to a 64-bit cell
+// id, the following coordinate systems are used:
+//
+//  (id)
+//    An S2CellId is a 64-bit encoding of a face and a Hilbert curve position
+//    on that face.  The Hilbert curve position implicitly encodes both the
+//    position of a cell and its subdivision level (see s2cellid.h).
+//
+//  (face, i, j)
+//    Leaf-cell coordinates.  "i" and "j" are integers in the range
+//    [0,(2**30)-1] that identify a particular leaf cell on the given face.
+//    The (i, j) coordinate system is right-handed on each face, and the
+//    faces are oriented such that Hilbert curves connect continuously from
+//    one face to the next.
+//
+//  (face, s, t)
+//    Cell-space coordinates.  "s" and "t" are real numbers in the range
+//    [0,1] that identify a point on the given face.  For example, the point
+//    (s, t) = (0.5, 0.5) corresponds to the center of the top-level face
+//    cell.  This point is also a vertex of exactly four cells at each
+//    subdivision level greater than zero.
+//
+//  (face, si, ti)
+//    Discrete cell-space coordinates.  These are obtained by multiplying
+//    "s" and "t" by 2**31 and rounding to the nearest unsigned integer.
+//    Discrete coordinates lie in the range [0,2**31].  This coordinate
+//    system can represent the edge and center positions of all cells with
+//    no loss of precision (including non-leaf cells).  In binary, each
+//    coordinate of a level-k cell center ends with a 1 followed by
+//    (30 - k) 0s.  The coordinates of its edges end with (at least)
+//    (31 - k) 0s.
+//
+//  (face, u, v)
+//    Cube-space coordinates in the range [-1,1].  To make the cells at each
+//    level more uniform in size after they are projected onto the sphere,
+//    we apply a nonlinear transformation of the form u=f(s), v=f(t).
+//    The (u, v) coordinates after this transformation give the actual
+//    coordinates on the cube face (modulo some 90 degree rotations) before
+//    it is projected onto the unit sphere.
+//
+//  (face, u, v, w)
+//    Per-face coordinate frame.  This is an extension of the (face, u, v)
+//    cube-space coordinates that adds a third axis "w" in the direction of
+//    the face normal.  It is always a right-handed 3D coordinate system.
+//    Cube-space coordinates can be converted to this frame by setting w=1,
+//    while (u,v,w) coordinates can be projected onto the cube face by
+//    dividing by w, i.e. (face, u/w, v/w).
+//
+//  (x, y, z)
+//    Direction vector (S2Point).  Direction vectors are not necessarily unit
+//    length, and are often chosen to be points on the biunit cube
+//    [-1,+1]x[-1,+1]x[-1,+1].  They can be be normalized to obtain the
+//    corresponding point on the unit sphere.
+//
+//  (lat, lng)
+//    Latitude and longitude (S2LatLng).  Latitudes must be between -90 and
+//    90 degrees inclusive, and longitudes must be between -180 and 180
+//    degrees inclusive.
+//
+// Note that the (i, j), (s, t), (si, ti), and (u, v) coordinate systems are
+// right-handed on all six faces.
+
+#ifndef S2_S2COORDS_H_
+#define S2_S2COORDS_H_
+
+#include <algorithm>
+#include <cmath>
+
+#include "base/logging.h"
+#include "s2/r2.h"
+#include "s2/s2coords-internal.h"
+#include "s2/s2point.h"
+#include "s2/util/math/mathutil.h"
+
+// S2 is a namespace for constants and simple utility functions that are used
+// throughout the S2 library.  The name "S2" is derived from the mathematical
+// symbol for the two-dimensional unit sphere (note that the "2" refers to the
+// dimension of the surface, not the space it is embedded in).
+namespace S2 {
+
+// This is the number of levels needed to specify a leaf cell.  This
+// constant is defined here so that the S2::Metric class and the conversion
+// functions below can be implemented without including s2cellid.h.  Please
+// see s2cellid.h for other useful constants and conversion functions.
+int const kMaxCellLevel = 30;
+
+// The maximum index of a valid leaf cell plus one.  The range of valid leaf
+// cell indices is [0..kLimitIJ-1].
+int const kLimitIJ = 1 << kMaxCellLevel;  // == S2CellId::kMaxSize
+
+// The maximum value of an si- or ti-coordinate.  The range of valid (si,ti)
+// values is [0..kMaxSiTi].
+unsigned int const kMaxSiTi = 1U << (kMaxCellLevel + 1);
+
+// Convert an s- or t-value to the corresponding u- or v-value.  This is
+// a non-linear transformation from [-1,1] to [-1,1] that attempts to
+// make the cell sizes more uniform.
+double STtoUV(double s);
+
+// The inverse of the STtoUV transformation.  Note that it is not always
+// true that UVtoST(STtoUV(x)) == x due to numerical errors.
+double UVtoST(double u);
+
+// Convert the i- or j-index of a leaf cell to the minimum corresponding s-
+// or t-value contained by that cell.  The argument must be in the range
+// [0..2**30], i.e. up to one position beyond the normal range of valid leaf
+// cell indices.
+double IJtoSTMin(int i);
+
+// Return the i- or j-index of the leaf cell containing the given
+// s- or t-value.  If the argument is outside the range spanned by valid
+// leaf cell indices, return the index of the closest valid leaf cell (i.e.,
+// return values are clamped to the range of valid leaf cell indices).
+int STtoIJ(double s);
+
+// Convert an si- or ti-value to the corresponding s- or t-value.
+double SiTitoST(unsigned int si);
+
+// Return the si- or ti-coordinate that is nearest to the given s- or
+// t-value.  The result may be outside the range of valid (si,ti)-values.
+unsigned int STtoSiTi(double s);
+
+// Convert (face, u, v) coordinates to a direction vector (not
+// necessarily unit length).
+S2Point FaceUVtoXYZ(int face, double u, double v);
+S2Point FaceUVtoXYZ(int face, R2Point const& uv);
+
+// If the dot product of p with the given face normal is positive,
+// set the corresponding u and v values (which may lie outside the range
+// [-1,1]) and return true.  Otherwise return false.
+bool FaceXYZtoUV(int face, S2Point const& p, double* pu, double* pv);
+bool FaceXYZtoUV(int face, S2Point const& p, R2Point* puv);
+
+// Given a *valid* face for the given point p (meaning that dot product
+// of p with the face normal is positive), return the corresponding
+// u and v values (which may lie outside the range [-1,1]).
+void ValidFaceXYZtoUV(int face, S2Point const& p, double* pu, double* pv);
+void ValidFaceXYZtoUV(int face, S2Point const& p, R2Point* puv);
+
+// Transform the given point P to the (u,v,w) coordinate frame of the given
+// face (where the w-axis represents the face normal).
+S2Point FaceXYZtoUVW(int face, S2Point const& p);
+
+// Return the face containing the given direction vector.  (For points on
+// the boundary between faces, the result is arbitrary but repeatable.)
+int GetFace(S2Point const& p);
+
+// Convert a direction vector (not necessarily unit length) to
+// (face, u, v) coordinates.
+int XYZtoFaceUV(S2Point const& p, double* pu, double* pv);
+int XYZtoFaceUV(S2Point const& p, R2Point* puv);
+
+// Convert a direction vector (not necessarily unit length) to
+// (face, si, ti) coordinates and, if p is exactly equal to the center of a
+// cell, return the level of this cell (-1 otherwise).
+int XYZtoFaceSiTi(S2Point const& p,
+                  int* face,
+                  unsigned int* si,
+                  unsigned int* ti);
+
+// Convert (face, si, ti) coordinates to a direction vector (not necessarily
+// unit length).
+S2Point FaceSiTitoXYZ(int face, unsigned int si, unsigned int ti);
+
+// Return the right-handed normal (not necessarily unit length) for an
+// edge in the direction of the positive v-axis at the given u-value on
+// the given face.  (This vector is perpendicular to the plane through
+// the sphere origin that contains the given edge.)
+S2Point GetUNorm(int face, double u);
+
+// Return the right-handed normal (not necessarily unit length) for an
+// edge in the direction of the positive u-axis at the given v-value on
+// the given face.
+S2Point GetVNorm(int face, double v);
+
+// Return the unit-length normal, u-axis, or v-axis for the given face.
+S2Point GetNorm(int face);
+S2Point GetUAxis(int face);
+S2Point GetVAxis(int face);
+
+// Return the given axis of the given face (u=0, v=1, w=2).
+S2Point GetUVWAxis(int face, int axis);
+
+// With respect to the (u,v,w) coordinate system of a given face, return the
+// face that lies in the given direction (negative=0, positive=1) of the
+// given axis (u=0, v=1, w=2).  For example, GetUVWFace(4, 0, 1) returns the
+// face that is adjacent to face 4 in the positive u-axis direction.
+int GetUVWFace(int face, int axis, int direction);
+
+//////////////////   Implementation details follow   ////////////////////
+
+// We have implemented three different projections from cell-space (s,t) to
+// cube-space (u,v): linear, quadratic, and tangent.  They have the following
+// tradeoffs:
+//
+//   Linear - This is the fastest transformation, but also produces the least
+//   uniform cell sizes.  Cell areas vary by a factor of about 5.2, with the
+//   largest cells at the center of each face and the smallest cells in
+//   the corners.
+//
+//   Tangent - Transforming the coordinates via atan() makes the cell sizes
+//   more uniform.  The areas vary by a maximum ratio of 1.4 as opposed to a
+//   maximum ratio of 5.2.  However, each call to atan() is about as expensive
+//   as all of the other calculations combined when converting from points to
+//   cell ids, i.e. it reduces performance by a factor of 3.
+//
+//   Quadratic - This is an approximation of the tangent projection that
+//   is much faster and produces cells that are almost as uniform in size.
+//   It is about 3 times faster than the tangent projection for converting
+//   cell ids to points or vice versa.  Cell areas vary by a maximum ratio of
+//   about 2.1.
+//
+// Here is a table comparing the cell uniformity using each projection.  "Area
+// ratio" is the maximum ratio over all subdivision levels of the largest cell
+// area to the smallest cell area at that level, "edge ratio" is the maximum
+// ratio of the longest edge of any cell to the shortest edge of any cell at
+// the same level, and "diag ratio" is the ratio of the longest diagonal of
+// any cell to the shortest diagonal of any cell at the same level.  "ToPoint"
+// and "FromPoint" are the times in microseconds required to convert cell ids
+// to and from points (unit vectors) respectively.  "ToPointRaw" is the time
+// to convert to a non-unit-length vector, which is all that is needed for
+// some purposes.
+//
+//               Area    Edge    Diag   ToPointRaw  ToPoint  FromPoint
+//              Ratio   Ratio   Ratio             (microseconds)
+// -------------------------------------------------------------------
+// Linear:      5.200   2.117   2.959      0.020     0.087     0.085
+// Tangent:     1.414   1.414   1.704      0.237     0.299     0.258
+// Quadratic:   2.082   1.802   1.932      0.033     0.096     0.108
+//
+// The worst-case cell aspect ratios are about the same with all three
+// projections.  The maximum ratio of the longest edge to the shortest edge
+// within the same cell is about 1.4 and the maximum ratio of the diagonals
+// within the same cell is about 1.7.
+//
+// This data was produced using s2cell_test and s2cellid_test.
+
+#define S2_LINEAR_PROJECTION 0
+#define S2_TAN_PROJECTION 1
+#define S2_QUADRATIC_PROJECTION 2
+
+#define S2_PROJECTION S2_QUADRATIC_PROJECTION
+
+#if S2_PROJECTION == S2_LINEAR_PROJECTION
+
+inline double STtoUV(double s) {
+  return 2 * s - 1;
+}
+
+inline double UVtoST(double u) {
+  return 0.5 * (u + 1);
+}
+
+#elif S2_PROJECTION == S2_TAN_PROJECTION
+
+inline double STtoUV(double s) {
+  // Unfortunately, tan(M_PI_4) is slightly less than 1.0.  This isn't due to
+  // a flaw in the implementation of tan(), it's because the derivative of
+  // tan(x) at x=pi/4 is 2, and it happens that the two adjacent floating
+  // point numbers on either side of the infinite-precision value of pi/4 have
+  // tangents that are slightly below and slightly above 1.0 when rounded to
+  // the nearest double-precision result.
+
+  s = std::tan(M_PI_2 * s - M_PI_4);
+  return s + (1.0 / (GG_LONGLONG(1) << 53)) * s;
+}
+
+inline double UVtoST(double u) {
+  volatile double a = std::atan(u);
+  return (2 * M_1_PI) * (a + M_PI_4);
+}
+
+#elif S2_PROJECTION == S2_QUADRATIC_PROJECTION
+
+inline double STtoUV(double s) {
+  if (s >= 0.5)
+    return (1 / 3.) * (4 * s * s - 1);
+  else
+    return (1 / 3.) * (1 - 4 * (1 - s) * (1 - s));
+}
+
+inline double UVtoST(double u) {
+  if (u >= 0)
+    return 0.5 * std::sqrt(1 + 3 * u);
+  else
+    return 1 - 0.5 * std::sqrt(1 - 3 * u);
+}
+
+#else
+
+#error Unknown value for S2_PROJECTION
+
+#endif
+
+inline double IJtoSTMin(int i) {
+  DCHECK(i >= 0 && i <= kLimitIJ);
+  return (1.0 / kLimitIJ) * i;
+}
+
+inline int STtoIJ(double s) {
+  return std::max(
+      0, std::min(kLimitIJ - 1, MathUtil::FastIntRound(kLimitIJ * s - 0.5)));
+}
+
+inline double SiTitoST(unsigned int si) {
+  DCHECK(si >= 0 && si <= kMaxSiTi);
+  return (1.0 / kMaxSiTi) * si;
+}
+
+inline unsigned int STtoSiTi(double s) {
+  // kMaxSiTi == 2^31, so the result doesn't fit in an int32_t when s == 1.
+  return static_cast<unsigned int>(MathUtil::FastInt64Round(s * kMaxSiTi));
+}
+
+inline S2Point FaceUVtoXYZ(int face, double u, double v) {
+  switch (face) {
+    case 0:
+      return S2Point(1, u, v);
+    case 1:
+      return S2Point(-u, 1, v);
+    case 2:
+      return S2Point(-u, -v, 1);
+    case 3:
+      return S2Point(-1, -v, -u);
+    case 4:
+      return S2Point(v, -1, -u);
+    default:
+      return S2Point(v, u, -1);
+  }
+}
+
+inline S2Point FaceUVtoXYZ(int face, R2Point const& uv) {
+  return FaceUVtoXYZ(face, uv[0], uv[1]);
+}
+
+inline void ValidFaceXYZtoUV(int face,
+                             S2Point const& p,
+                             double* pu,
+                             double* pv) {
+  DCHECK_GT(p.DotProd(GetNorm(face)), 0);
+  switch (face) {
+    case 0:
+      *pu = p[1] / p[0];
+      *pv = p[2] / p[0];
+      break;
+    case 1:
+      *pu = -p[0] / p[1];
+      *pv = p[2] / p[1];
+      break;
+    case 2:
+      *pu = -p[0] / p[2];
+      *pv = -p[1] / p[2];
+      break;
+    case 3:
+      *pu = p[2] / p[0];
+      *pv = p[1] / p[0];
+      break;
+    case 4:
+      *pu = p[2] / p[1];
+      *pv = -p[0] / p[1];
+      break;
+    default:
+      *pu = -p[1] / p[2];
+      *pv = -p[0] / p[2];
+      break;
+  }
+}
+
+inline void ValidFaceXYZtoUV(int face, S2Point const& p, R2Point* puv) {
+  ValidFaceXYZtoUV(face, p, &(*puv)[0], &(*puv)[1]);
+}
+
+inline int GetFace(S2Point const& p) {
+  int face = p.LargestAbsComponent();
+  if (p[face] < 0)
+    face += 3;
+  return face;
+}
+
+inline int XYZtoFaceUV(S2Point const& p, double* pu, double* pv) {
+  int face = GetFace(p);
+  ValidFaceXYZtoUV(face, p, pu, pv);
+  return face;
+}
+
+inline int XYZtoFaceUV(S2Point const& p, R2Point* puv) {
+  return XYZtoFaceUV(p, &(*puv)[0], &(*puv)[1]);
+}
+
+inline bool FaceXYZtoUV(int face, S2Point const& p, double* pu, double* pv) {
+  if (face < 3) {
+    if (p[face] <= 0)
+      return false;
+  } else {
+    if (p[face - 3] >= 0)
+      return false;
+  }
+  ValidFaceXYZtoUV(face, p, pu, pv);
+  return true;
+}
+
+inline bool FaceXYZtoUV(int face, S2Point const& p, R2Point* puv) {
+  return FaceXYZtoUV(face, p, &(*puv)[0], &(*puv)[1]);
+}
+
+inline S2Point GetUNorm(int face, double u) {
+  switch (face) {
+    case 0:
+      return S2Point(u, -1, 0);
+    case 1:
+      return S2Point(1, u, 0);
+    case 2:
+      return S2Point(1, 0, u);
+    case 3:
+      return S2Point(-u, 0, 1);
+    case 4:
+      return S2Point(0, -u, 1);
+    default:
+      return S2Point(0, -1, -u);
+  }
+}
+
+inline S2Point GetVNorm(int face, double v) {
+  switch (face) {
+    case 0:
+      return S2Point(-v, 0, 1);
+    case 1:
+      return S2Point(0, -v, 1);
+    case 2:
+      return S2Point(0, -1, -v);
+    case 3:
+      return S2Point(v, -1, 0);
+    case 4:
+      return S2Point(1, v, 0);
+    default:
+      return S2Point(1, 0, v);
+  }
+}
+
+inline S2Point GetNorm(int face) {
+  return GetUVWAxis(face, 2);
+}
+
+inline S2Point GetUAxis(int face) {
+  return GetUVWAxis(face, 0);
+}
+
+inline S2Point GetVAxis(int face) {
+  return GetUVWAxis(face, 1);
+}
+
+inline S2Point GetUVWAxis(int face, int axis) {
+  double const* p = internal::kFaceUVWAxes[face][axis];
+  return S2Point(p[0], p[1], p[2]);
+}
+
+inline int GetUVWFace(int face, int axis, int direction) {
+  DCHECK(face >= 0 && face <= 5);
+  DCHECK(axis >= 0 && axis <= 2);
+  DCHECK(direction >= 0 && direction <= 1);
+  return internal::kFaceUVWFaces[face][axis][direction];
+}
+
+}  // namespace S2
+
+#endif  // S2_S2COORDS_H_
diff --git a/third_party/s2cellid/src/s2/s2latlng.cc b/third_party/s2cellid/src/s2/s2latlng.cc
new file mode 100644
index 0000000..338e039d
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s2latlng.cc
@@ -0,0 +1,89 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#include "s2/s2latlng.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+
+using std::max;
+using std::min;
+
+S2LatLng S2LatLng::Normalized() const {
+  // remainder(x, 2 * M_PI) reduces its argument to the range [-M_PI, M_PI]
+  // inclusive, which is what we want here.
+  return S2LatLng(max(-M_PI_2, min(M_PI_2, lat().radians())),
+                  remainder(lng().radians(), 2 * M_PI));
+}
+
+S2Point S2LatLng::ToPoint() const {
+  DLOG_IF(ERROR, !is_valid())
+      << "Invalid S2LatLng in S2LatLng::ToPoint: " << *this;
+  double phi = lat().radians();
+  double theta = lng().radians();
+  double cosphi = cos(phi);
+  return S2Point(cos(theta) * cosphi, sin(theta) * cosphi, sin(phi));
+}
+
+S2LatLng::S2LatLng(S2Point const& p)
+    : coords_(Latitude(p).radians(), Longitude(p).radians()) {
+  // The latitude and longitude are already normalized.
+  DLOG_IF(ERROR, !is_valid()) << "Invalid S2LatLng in constructor: " << *this;
+}
+
+S1Angle S2LatLng::GetDistance(S2LatLng const& o) const {
+  // This implements the Haversine formula, which is numerically stable for
+  // small distances but only gets about 8 digits of precision for very large
+  // distances (e.g. antipodal points).  Note that 8 digits is still accurate
+  // to within about 10cm for a sphere the size of the Earth.
+  //
+  // This could be fixed with another sin() and cos() below, but at that point
+  // you might as well just convert both arguments to S2Points and compute the
+  // distance that way (which gives about 15 digits of accuracy for all
+  // distances).
+
+  DLOG_IF(ERROR, !is_valid())
+      << "Invalid S2LatLng in S2LatLng::GetDistance: " << *this;
+
+  DLOG_IF(ERROR, !o.is_valid())
+      << "Invalid S2LatLng in S2LatLng::GetDistance: " << o;
+
+  double lat1 = lat().radians();
+  double lat2 = o.lat().radians();
+  double lng1 = lng().radians();
+  double lng2 = o.lng().radians();
+  double dlat = sin(0.5 * (lat2 - lat1));
+  double dlng = sin(0.5 * (lng2 - lng1));
+  double x = dlat * dlat + dlng * dlng * cos(lat1) * cos(lat2);
+  return S1Angle::Radians(2 * asin(sqrt(min(1.0, x))));
+}
+
+std::string S2LatLng::ToStringInDegrees() const {
+  S2LatLng pt = Normalized();
+  return base::StringPrintf("%f,%f", pt.lat().degrees(), pt.lng().degrees());
+}
+
+void S2LatLng::ToStringInDegrees(std::string* s) const {
+  *s = ToStringInDegrees();
+}
+
+std::ostream& operator<<(std::ostream& os, S2LatLng const& ll) {
+  return os << "[" << ll.lat() << ", " << ll.lng() << "]";
+}
diff --git a/third_party/s2cellid/src/s2/s2latlng.h b/third_party/s2cellid/src/s2/s2latlng.h
new file mode 100644
index 0000000..3ac9ddf
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s2latlng.h
@@ -0,0 +1,209 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2LATLNG_H_
+#define S2_S2LATLNG_H_
+
+#include <cmath>
+#include <iosfwd>
+#include <ostream>
+#include <string>
+
+#include "s2/_fpcontractoff.h"
+#include "s2/r2.h"
+#include "s2/s1angle.h"
+#include "s2/util/math/vector.h"
+
+// This class represents a point on the unit sphere as a pair
+// of latitude-longitude coordinates.  Like the rest of the "geometry"
+// package, the intent is to represent spherical geometry as a mathematical
+// abstraction, so functions that are specifically related to the Earth's
+// geometry (e.g. easting/northing conversions) should be put elsewhere.
+//
+// This class is intended to be copied by value as desired.  It uses
+// the default copy constructor and assignment operator.
+class S2LatLng {
+ public:
+  // Constructor.  The latitude and longitude are allowed to be outside
+  // the is_valid() range.  However, note that most methods that accept
+  // S2LatLngs expect them to be normalized (see Normalized() below).
+  S2LatLng(S1Angle lat, S1Angle lng);
+
+  // The default constructor sets the latitude and longitude to zero.  This is
+  // mainly useful when declaring arrays, STL containers, etc.
+  S2LatLng();
+
+  // Convert a direction vector (not necessarily unit length) to an S2LatLng.
+  explicit S2LatLng(S2Point const& p);
+
+  // Returns an S2LatLng for which is_valid() will return false.
+  static S2LatLng Invalid();
+
+  // Convenience functions -- shorter than calling S1Angle::Radians(), etc.
+  static S2LatLng FromRadians(double lat_radians, double lng_radians);
+  static S2LatLng FromDegrees(double lat_degrees, double lng_degrees);
+  static S2LatLng FromE5(int32_t lat_e5, int32_t lng_e5);
+  static S2LatLng FromE6(int32_t lat_e6, int32_t lng_e6);
+  static S2LatLng FromE7(int32_t lat_e7, int32_t lng_e7);
+
+  // Convenience functions -- to use when args have been fixed32s in protos.
+  //
+  // The arguments are static_cast into int32_t, so very large unsigned values
+  // are treated as negative numbers.
+  static S2LatLng FromUnsignedE6(uint32_t lat_e6, uint32_t lng_e6);
+  static S2LatLng FromUnsignedE7(uint32_t lat_e7, uint32_t lng_e7);
+
+  // Methods to compute the latitude and longitude of a point separately.
+  static S1Angle Latitude(S2Point const& p);
+  static S1Angle Longitude(S2Point const& p);
+
+  // Accessor methods.
+  S1Angle lat() const { return S1Angle::Radians(coords_[0]); }
+  S1Angle lng() const { return S1Angle::Radians(coords_[1]); }
+  R2Point const& coords() const { return coords_; }
+
+  // Return true if the latitude is between -90 and 90 degrees inclusive
+  // and the longitude is between -180 and 180 degrees inclusive.
+  bool is_valid() const;
+
+  // Clamps the latitude to the range [-90, 90] degrees, and adds or subtracts
+  // a multiple of 360 degrees to the longitude if necessary to reduce it to
+  // the range [-180, 180].
+  S2LatLng Normalized() const;
+
+  // Convert a normalized S2LatLng to the equivalent unit-length vector.
+  // The maximum error in the result is 1.5 * DBL_EPSILON.  (This does not
+  // include the error of converting degrees, E5, E6, or E7 to radians.)
+  S2Point ToPoint() const;
+
+  // Return the distance (measured along the surface of the sphere) to the
+  // given S2LatLng.  This is mathematically equivalent to:
+  //
+  //   S1Angle(ToPoint(), o.ToPoint())
+  //
+  // but this implementation is slightly more efficient.  Both S2LatLngs
+  // must be normalized.
+  S1Angle GetDistance(S2LatLng const& o) const;
+
+  // Simple arithmetic operations for manipulating latitude-longitude pairs.
+  // The results are not normalized (see Normalized()).
+  friend S2LatLng operator+(S2LatLng const& a, S2LatLng const& b);
+  friend S2LatLng operator-(S2LatLng const& a, S2LatLng const& b);
+  friend S2LatLng operator*(double m, S2LatLng const& a);
+  friend S2LatLng operator*(S2LatLng const& a, double m);
+
+  bool operator==(S2LatLng const& o) const { return coords_ == o.coords_; }
+  bool operator!=(S2LatLng const& o) const { return coords_ != o.coords_; }
+  bool operator<(S2LatLng const& o) const { return coords_ < o.coords_; }
+  bool operator>(S2LatLng const& o) const { return coords_ > o.coords_; }
+  bool operator<=(S2LatLng const& o) const { return coords_ <= o.coords_; }
+  bool operator>=(S2LatLng const& o) const { return coords_ >= o.coords_; }
+
+  bool ApproxEquals(S2LatLng const& o,
+                    S1Angle max_error = S1Angle::Radians(1e-15)) const {
+    return coords_.aequal(o.coords_, max_error.radians());
+  }
+
+  // Export the latitude and longitude in degrees, separated by a comma.
+  // e.g. "94.518000,150.300000"
+  std::string ToStringInDegrees() const;
+  void ToStringInDegrees(std::string* s) const;
+
+ private:
+  // Internal constructor.
+  explicit S2LatLng(R2Point const& coords) : coords_(coords) {}
+
+  // This is internal to avoid ambiguity about which units are expected.
+  S2LatLng(double lat_radians, double lng_radians)
+      : coords_(lat_radians, lng_radians) {}
+
+  R2Point coords_;
+};
+
+inline S2LatLng::S2LatLng(S1Angle lat, S1Angle lng)
+    : coords_(lat.radians(), lng.radians()) {}
+
+inline S2LatLng::S2LatLng() : coords_(0, 0) {}
+
+inline S2LatLng S2LatLng::FromRadians(double lat_radians, double lng_radians) {
+  return S2LatLng(lat_radians, lng_radians);
+}
+
+inline S2LatLng S2LatLng::FromDegrees(double lat_degrees, double lng_degrees) {
+  return S2LatLng(S1Angle::Degrees(lat_degrees), S1Angle::Degrees(lng_degrees));
+}
+
+inline S2LatLng S2LatLng::FromE5(int32_t lat_e5, int32_t lng_e5) {
+  return S2LatLng(S1Angle::E5(lat_e5), S1Angle::E5(lng_e5));
+}
+
+inline S2LatLng S2LatLng::FromE6(int32_t lat_e6, int32_t lng_e6) {
+  return S2LatLng(S1Angle::E6(lat_e6), S1Angle::E6(lng_e6));
+}
+
+inline S2LatLng S2LatLng::FromE7(int32_t lat_e7, int32_t lng_e7) {
+  return S2LatLng(S1Angle::E7(lat_e7), S1Angle::E7(lng_e7));
+}
+
+inline S2LatLng S2LatLng::FromUnsignedE6(uint32_t lat_e6, uint32_t lng_e6) {
+  return S2LatLng(S1Angle::UnsignedE6(lat_e6), S1Angle::UnsignedE6(lng_e6));
+}
+
+inline S2LatLng S2LatLng::FromUnsignedE7(uint32_t lat_e7, uint32_t lng_e7) {
+  return S2LatLng(S1Angle::UnsignedE7(lat_e7), S1Angle::UnsignedE7(lng_e7));
+}
+
+inline S2LatLng S2LatLng::Invalid() {
+  // These coordinates are outside the bounds allowed by is_valid().
+  return S2LatLng(M_PI, 2 * M_PI);
+}
+
+inline S1Angle S2LatLng::Latitude(S2Point const& p) {
+  // We use atan2 rather than asin because the input vector is not necessarily
+  // unit length, and atan2 is much more accurate than asin near the poles.
+  return S1Angle::Radians(atan2(p[2], sqrt(p[0] * p[0] + p[1] * p[1])));
+}
+
+inline S1Angle S2LatLng::Longitude(S2Point const& p) {
+  // Note that atan2(0, 0) is defined to be zero.
+  return S1Angle::Radians(atan2(p[1], p[0]));
+}
+
+inline bool S2LatLng::is_valid() const {
+  return (std::fabs(lat().radians()) <= M_PI_2 &&
+          std::fabs(lng().radians()) <= M_PI);
+}
+
+inline S2LatLng operator+(S2LatLng const& a, S2LatLng const& b) {
+  return S2LatLng(a.coords_ + b.coords_);
+}
+
+inline S2LatLng operator-(S2LatLng const& a, S2LatLng const& b) {
+  return S2LatLng(a.coords_ - b.coords_);
+}
+
+inline S2LatLng operator*(double m, S2LatLng const& a) {
+  return S2LatLng(m * a.coords_);
+}
+
+inline S2LatLng operator*(S2LatLng const& a, double m) {
+  return S2LatLng(m * a.coords_);
+}
+
+std::ostream& operator<<(std::ostream& os, S2LatLng const& ll);
+
+#endif  // S2_S2LATLNG_H_
diff --git a/third_party/s2cellid/src/s2/s2point.h b/third_party/s2cellid/src/s2/s2point.h
new file mode 100644
index 0000000..88a061b8
--- /dev/null
+++ b/third_party/s2cellid/src/s2/s2point.h
@@ -0,0 +1,31 @@
+// Copyright 2005 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Author: ericv@google.com (Eric Veach)
+
+#ifndef S2_S2POINT_H_
+#define S2_S2POINT_H_
+
+#include "s2/_fpcontractoff.h"
+#include "s2/util/math/vector.h"  // IWYU pragma: export
+
+// An S2Point represents a point on the unit sphere as a 3D vector.  Usually
+// points are normalized to be unit length, but some methods do not require
+// this.  See util/math/vector.h for the methods available.  Among other
+// things, there are overloaded operators that make it convenient to write
+// arithmetic expressions (e.g. (1-x)*p1 + x*p2).
+using S2Point = Vector3_d;
+
+#endif  // S2_S2POINT_H_
diff --git a/third_party/s2cellid/src/s2/util/bits/bits.h b/third_party/s2cellid/src/s2/util/bits/bits.h
new file mode 100644
index 0000000..5a36d52
--- /dev/null
+++ b/third_party/s2cellid/src/s2/util/bits/bits.h
@@ -0,0 +1,217 @@
+// Copyright 2002 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef S2_UTIL_BITS_BITS_H_
+#define S2_UTIL_BITS_BITS_H_
+
+//
+// Various bit-twiddling functions, all of which are static members of the Bits
+// class (making it effectively a namespace). Operands are unsigned integers.
+// Munging bits in _signed_ integers is fraught with peril! For example,
+// -5 << n has undefined behavior (for some values of n).
+//
+// Bits provide the following:
+//
+//   * Log2(Floor|Ceiling)(NonZero)?.* - The NonZero variants have undefined
+//     behavior if argument is 0.
+//
+// The only other thing is BitPattern, which is a trait class template (not in
+// Bits) containing a few bit patterns (which vary based on value of template
+// parameter).
+
+#if defined(__i386__) || defined(__x86_64__)
+#include <x86intrin.h>
+#endif
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+class Bits {
+ public:
+  // Return floor(log2(n)) for positive integer n.  Returns -1 iff n == 0.
+  static int Log2Floor(uint32_t n);
+  static int Log2Floor64(uint64_t n);
+
+  // Potentially faster version of Log2Floor() that returns an
+  // undefined value if n == 0
+  static int Log2FloorNonZero(uint32_t n);
+  static int Log2FloorNonZero64(uint64_t n);
+
+  // Return the first set least / most significant bit, 0-indexed.  Returns an
+  // undefined value if n == 0.  FindLSBSetNonZero() is similar to ffs() except
+  // that it's 0-indexed, while FindMSBSetNonZero() is the same as
+  // Log2FloorNonZero().
+  static int FindLSBSetNonZero(uint32_t n);
+  static int FindLSBSetNonZero64(uint64_t n);
+  static int FindMSBSetNonZero(uint32_t n) { return Log2FloorNonZero(n); }
+  static int FindMSBSetNonZero64(uint64_t n) { return Log2FloorNonZero64(n); }
+
+ private:
+  // Portable implementations.
+  static int Log2Floor_Portable(uint32_t n);
+  static int Log2Floor64_Portable(uint64_t n);
+  static int Log2FloorNonZero_Portable(uint32_t n);
+  static int Log2FloorNonZero64_Portable(uint64_t n);
+  static int FindLSBSetNonZero_Portable(uint32_t n);
+  static int FindLSBSetNonZero64_Portable(uint64_t n);
+
+  Bits(Bits const&) = delete;
+  void operator=(Bits const&) = delete;
+};
+
+// ------------------------------------------------------------------------
+// Implementation details follow
+// ------------------------------------------------------------------------
+
+#if defined(__GNUC__)
+
+inline int Bits::Log2Floor(uint32_t n) {
+  return n == 0 ? -1 : 31 ^ __builtin_clz(n);
+}
+
+inline int Bits::Log2FloorNonZero(uint32_t n) {
+  return 31 ^ __builtin_clz(n);
+}
+
+inline int Bits::FindLSBSetNonZero(uint32_t n) {
+  return __builtin_ctz(n);
+}
+
+inline int Bits::Log2Floor64(uint64_t n) {
+  return n == 0 ? -1 : 63 ^ __builtin_clzll(n);
+}
+
+inline int Bits::Log2FloorNonZero64(uint64_t n) {
+  return 63 ^ __builtin_clzll(n);
+}
+
+inline int Bits::FindLSBSetNonZero64(uint64_t n) {
+  return __builtin_ctzll(n);
+}
+
+#elif defined(_MSC_VER)
+
+inline int Bits::FindLSBSetNonZero(uint64_t n) {
+  return Bits::FindLSBSetNonZero_Portable(n);
+}
+
+inline int Bits::FindLSBSetNonZero64(uint64_t n) {
+  return Bits::FindLSBSetNonZero64_Portable(n);
+}
+
+inline int Bits::Log2FloorNonZero(uint32_t n) {
+#ifdef _M_IX86
+  _asm {
+    bsr ebx, n
+    mov n, ebx
+  }
+  return n;
+#else
+  return Bits::Log2FloorNonZero_Portable(n);
+#endif
+}
+
+inline int Bits::Log2Floor(uint32_t n) {
+#ifdef _M_IX86
+  _asm {
+    xor ebx, ebx
+    mov eax, n
+    and eax, eax
+    jz return_ebx
+    bsr ebx, eax
+return_ebx:
+    mov n, ebx
+  }
+  return n;
+#else
+  return Bits::Log2Floor_Portable(n);
+#endif
+}
+
+inline int Bits::Log2Floor64(uint64_t n) {
+  return Bits::Log2Floor64_Portable(n);
+}
+
+inline int Bits::Log2FloorNonZero64(uint64_t n) {
+  return Bits::Log2FloorNonZero64_Portable(n);
+}
+
+#else  // !__GNUC__ && !_MSC_VER
+
+inline int Bits::Log2Floor(uint32_t n) {
+  return Bits::Log2Floor_Portable(n);
+}
+
+inline int Bits::Log2FloorNonZero(uint32_t n) {
+  return Bits::Log2FloorNonZero_Portable(n);
+}
+
+inline int Bits::FindLSBSetNonZero(uint32_t n) {
+  return Bits::FindLSBSetNonZero_Portable(n);
+}
+
+inline int Bits::Log2Floor64(uint64_t n) {
+  return Bits::Log2Floor64_Portable(n);
+}
+
+inline int Bits::Log2FloorNonZero64(uint64_t n) {
+  return Bits::Log2FloorNonZero64_Portable(n);
+}
+
+inline int Bits::FindLSBSetNonZero64(uint64_t n) {
+  return Bits::FindLSBSetNonZero64_Portable(n);
+}
+
+#endif
+
+inline int Bits::Log2FloorNonZero_Portable(uint32_t n) {
+  // Just use the common routine
+  return Log2Floor(n);
+}
+
+// Log2Floor64() is defined in terms of Log2Floor32(), Log2FloorNonZero32()
+inline int Bits::Log2Floor64_Portable(uint64_t n) {
+  const uint32_t topbits = static_cast<uint32_t>(n >> 32);
+  if (topbits == 0) {
+    // Top bits are zero, so scan in bottom bits
+    return Log2Floor(static_cast<uint32_t>(n));
+  } else {
+    return 32 + Log2FloorNonZero(topbits);
+  }
+}
+
+// Log2FloorNonZero64() is defined in terms of Log2FloorNonZero32()
+inline int Bits::Log2FloorNonZero64_Portable(uint64_t n) {
+  const uint32_t topbits = static_cast<uint32_t>(n >> 32);
+  if (topbits == 0) {
+    // Top bits are zero, so scan in bottom bits
+    return Log2FloorNonZero(static_cast<uint32_t>(n));
+  } else {
+    return 32 + Log2FloorNonZero(topbits);
+  }
+}
+
+// FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero()
+inline int Bits::FindLSBSetNonZero64_Portable(uint64_t n) {
+  const uint32_t bottombits = static_cast<uint32_t>(n);
+  if (bottombits == 0) {
+    // Bottom bits are zero, so scan in top bits
+    return 32 + FindLSBSetNonZero(static_cast<uint32_t>(n >> 32));
+  } else {
+    return FindLSBSetNonZero(bottombits);
+  }
+}
+
+#endif  // S2_UTIL_BITS_BITS_H_
diff --git a/third_party/s2cellid/src/s2/util/math/mathutil.cc b/third_party/s2cellid/src/s2/util/math/mathutil.cc
new file mode 100644
index 0000000..66b38cc4
--- /dev/null
+++ b/third_party/s2cellid/src/s2/util/math/mathutil.cc
@@ -0,0 +1,74 @@
+// Copyright 2008 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "s2/util/math/mathutil.h"
+
+#include <cmath>
+#include <cstdlib>
+
+namespace {
+// Returns the sign of x:
+//   -1 if x < 0,
+//   +1 if x > 0,
+//    0 if x = 0.
+template <class T>
+inline T sgn(const T x) {
+  return (x == 0 ? 0 : (x < 0 ? -1 : 1));
+}
+}  // namespace
+
+bool MathUtil::RealRootsForCubic(long double const a,
+                                 long double const b,
+                                 long double const c,
+                                 long double* const r1,
+                                 long double* const r2,
+                                 long double* const r3) {
+  // According to Numerical Recipes (pp. 184-5), what
+  // follows is an arrangement of computations to
+  // compute the roots of a cubic that minimizes
+  // roundoff error (as pointed out by A.J. Glassman).
+
+  long double const a_squared = a * a, a_third = a / 3.0, b_tripled = 3.0 * b;
+  long double const Q = (a_squared - b_tripled) / 9.0;
+  long double const R =
+      (2.0 * a_squared * a - 3.0 * a * b_tripled + 27.0 * c) / 54.0;
+
+  long double const R_squared = R * R;
+  long double const Q_cubed = Q * Q * Q;
+
+  if (R_squared < Q_cubed) {
+    long double const root_Q = sqrt(Q);
+    long double const two_pi_third = 2.0 * M_PI / 3.0;
+    long double const theta_third = acos(R / sqrt(Q_cubed)) / 3.0;
+    long double const minus_two_root_Q = -2.0 * root_Q;
+
+    *r1 = minus_two_root_Q * cos(theta_third) - a_third;
+    *r2 = minus_two_root_Q * cos(theta_third + two_pi_third) - a_third;
+    *r3 = minus_two_root_Q * cos(theta_third - two_pi_third) - a_third;
+
+    return true;
+  }
+
+  long double const A =
+      -sgn(R) * pow(std::abs(R) + sqrt(R_squared - Q_cubed), 1.0 / 3.0L);
+
+  if (A != 0.0) {  // in which case, B from NR is zero
+    *r1 = A + Q / A - a_third;
+    return false;
+  }
+
+  *r1 = *r2 = *r3 = -a_third;
+  return true;
+}
diff --git a/third_party/s2cellid/src/s2/util/math/mathutil.h b/third_party/s2cellid/src/s2/util/math/mathutil.h
new file mode 100644
index 0000000..f383566c
--- /dev/null
+++ b/third_party/s2cellid/src/s2/util/math/mathutil.h
@@ -0,0 +1,183 @@
+// Copyright 2001 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// This class is intended to contain a collection of useful (static)
+// mathematical functions, properly coded (by consulting numerical
+// recipes or another authoritative source first).
+
+#ifndef S2_UTIL_MATH_MATHUTIL_H_
+#define S2_UTIL_MATH_MATHUTIL_H_
+
+#include <type_traits>
+
+class MathUtil {
+ public:
+  // Solves for the real roots of x^3+ax^2+bx+c=0, returns true iff
+  // all three are real, in which case the roots are stored (in any
+  // order) in r1, r2, r3; otherwise, exactly one real root exists and
+  // it is stored in r1.
+  static bool RealRootsForCubic(long double a,
+                                long double b,
+                                long double c,
+                                long double* r1,
+                                long double* r2,
+                                long double* r3);
+
+  // --------------------------------------------------------------------
+  // Round
+  //   This function rounds a floating-point number to an integer. It
+  //   works for positive or negative numbers.
+  //
+  //   Values that are halfway between two integers may be rounded up or
+  //   down, for example Round<int>(0.5) == 0 and Round<int>(1.5) == 2.
+  //   This allows the function to be implemented efficiently on Intel
+  //   processors (see the template specializations at the bottom of this
+  //   file).  You should not use this function if you care about which
+  //   way such half-integers are rounded.
+  //
+  //   Example usage:
+  //     double y, z;
+  //     int x = Round<int>(y + 3.7);
+  //     int64_t b = Round<int64_t>(0.3 * z);
+  //
+  //   Note that the floating-point template parameter is typically inferred
+  //   from the argument type, i.e. there is no need to specify it explicitly.
+  // --------------------------------------------------------------------
+  template <class IntOut, class FloatIn>
+  static IntOut Round(FloatIn x) {
+    static_assert(!std::is_integral<FloatIn>::value, "FloatIn is integer");
+    static_assert(std::is_integral<IntOut>::value, "IntOut is not integer");
+
+    // We don't use sgn(x) below because there is no need to distinguish the
+    // (x == 0) case.  Also note that there are specialized faster versions
+    // of this function for Intel processors at the bottom of this file.
+    return static_cast<IntOut>(x < 0 ? (x - 0.5) : (x + 0.5));
+  }
+
+  // --------------------------------------------------------------------
+  // FastIntRound, FastInt64Round
+  //   Fast routines for converting floating-point numbers to integers.
+  //
+  //   These routines are approximately 6 times faster than the default
+  //   implementation of Round<int> on Intel processors (12 times faster on
+  //   the Pentium 3).  They are also more than 5 times faster than simply
+  //   casting a "double" to an "int" using static_cast<int>.  This is
+  //   because casts are defined to truncate towards zero, which on Intel
+  //   processors requires changing the rounding mode and flushing the
+  //   floating-point pipeline (unless programs are compiled specifically
+  //   for the Pentium 4, which has a new instruction to avoid this).
+  //
+  //   Numbers that are halfway between two integers may be rounded up or
+  //   down.  This is because the conversion is done using the default
+  //   rounding mode, which rounds towards the closest even number in case
+  //   of ties.  So for example, FastIntRound(0.5) == 0, but
+  //   FastIntRound(1.5) == 2.  These functions should only be used with
+  //   applications that don't care about which way such half-integers are
+  //   rounded.
+  //
+  //   There are template specializations of Round() which call these
+  //   functions (for "int" and "int64_t" only), but it's safer to call them
+  //   directly.
+  //
+  //   This functions are equivalent to lrint() and llrint() as defined in
+  //   the ISO C99 standard.  Unfortunately this standard does not seem to
+  //   widely adopted yet and these functions are not available by default.
+  //   --------------------------------------------------------------------
+
+  static int32_t FastIntRound(double x) {
+// This function is not templatized because gcc doesn't seem to be able
+// to deal with inline assembly code in templatized functions, and there
+// is no advantage to passing an argument type of "float" on Intel
+// architectures anyway.
+
+#if defined __GNUC__ && (defined __i386__ || defined __SSE2__)
+#if defined __SSE2__
+    // SSE2.
+    int32_t result;
+    __asm__ __volatile__("cvtsd2si %1, %0"
+                         : "=r"(result)  // Output operand is a register
+                         : "x"(x));      // Input operand is an xmm register
+    return result;
+#elif defined __i386__
+    // FPU stack.  Adapted from /usr/include/bits/mathinline.h.
+    int32_t result;
+    __asm__ __volatile__("fistpl %0"
+                         : "=m"(result)  // Output operand is a memory location
+                         : "t"(x)        // Input operand is top of FP stack
+                         : "st");        // Clobbers (pops) top of FP stack
+    return result;
+#endif  // if defined __x86_64__ || ...
+#else
+    return Round<int32_t, double>(x);
+#endif  // if defined __GNUC__ && ...
+  }
+
+  static int64_t FastInt64Round(double x) {
+#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
+#if defined __x86_64__
+    // SSE2.
+    int64_t result;
+    __asm__ __volatile__("cvtsd2si %1, %0"
+                         : "=r"(result)  // Output operand is a register
+                         : "x"(x));      // Input operand is an xmm register
+    return result;
+#elif defined __i386__
+    // There is no CVTSD2SI in i386 to produce a 64 bit int, even with SSE2.
+    // FPU stack.  Adapted from /usr/include/bits/mathinline.h.
+    int64_t result;
+    __asm__ __volatile__("fistpll %0"
+                         : "=m"(result)  // Output operand is a memory location
+                         : "t"(x)        // Input operand is top of FP stack
+                         : "st");        // Clobbers (pops) top of FP stack
+    return result;
+#endif  // if defined __i386__
+#else
+    return Round<int64_t, double>(x);
+#endif  // if defined __GNUC__ && ...
+  }
+};
+
+// ========================================================================= //
+
+#if (defined __i386__ || defined __x86_64__) && defined __GNUC__
+
+// We define template specializations of Round() to get the more efficient
+// Intel versions when possible.  Note that gcc does not currently support
+// partial specialization of templatized functions.
+
+template <>
+inline int32_t MathUtil::Round<int32_t, double>(double x) {
+  return FastIntRound(x);
+}
+
+template <>
+inline int32_t MathUtil::Round<int32_t, float>(float x) {
+  return FastIntRound(x);
+}
+
+template <>
+inline int64_t MathUtil::Round<int64_t, double>(double x) {
+  return FastInt64Round(x);
+}
+
+template <>
+inline int64_t MathUtil::Round<int64_t, float>(float x) {
+  return FastInt64Round(x);
+}
+
+#endif
+
+#endif  // S2_UTIL_MATH_MATHUTIL_H_
diff --git a/third_party/s2cellid/src/s2/util/math/vector.h b/third_party/s2cellid/src/s2/util/math/vector.h
new file mode 100644
index 0000000..0b0587cc
--- /dev/null
+++ b/third_party/s2cellid/src/s2/util/math/vector.h
@@ -0,0 +1,584 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// Simple classes to handle vectors in 2D, 3D, and 4D.
+//
+// Maintainers: Please be mindful of extreme degradations in unoptimized
+// performance here.
+#ifndef S2_UTIL_MATH_VECTOR_H_
+#define S2_UTIL_MATH_VECTOR_H_
+
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <iosfwd>
+#include <iostream>  // NOLINT(readability/streams)
+#include <limits>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+template <typename T>
+class Vector2;
+template <typename T>
+class Vector3;
+template <typename T>
+class Vector4;
+
+namespace util {
+namespace math {
+namespace internal_vector {
+
+// See http://en.cppreference.com/w/cpp/utility/integer_sequence
+// Implemented here to avoid a dependency.
+// TODO(user): Reuse std::index_sequence or gtl::IndexSequence.
+template <std::size_t... Is>
+struct IdxSeq {};
+template <std::size_t M, std::size_t... Is>
+struct MkIdxSeq : MkIdxSeq<M - 1, M - 1, Is...> {};
+template <std::size_t... Is>
+struct MkIdxSeq<0, Is...> {
+  using type = IdxSeq<Is...>;
+};
+
+// CRTP base class for all Vector templates.
+template <template <typename> class VecTemplate, typename T, std::size_t N>
+class BasicVector {
+  using D = VecTemplate<T>;
+
+ protected:
+  // FloatType is the type returned by Norm() and Angle().  These methods are
+  // special because they return floating-point values even when VType is an
+  // integer.
+  typedef typename std::conditional<std::is_integral<T>::value, double, T>::type
+      FloatType;
+
+  using IdxSeqN = typename MkIdxSeq<N>::type;
+
+  template <std::size_t I, typename F, typename... As>
+  static auto Reduce(F f, As*... as) -> decltype(f(as[I]...)) {
+    return f(as[I]...);
+  }
+
+  template <typename R = D, std::size_t... Is, typename F, typename... As>
+  static R GenerateEach(IdxSeq<Is...>, F f, As*... as) {
+    return R(Reduce<Is>(f, as...)...);
+  }
+
+  // Generate<R>(f,a,b,...) returns an R(...), where the constructor arguments
+  // are created as a transform. R(f(a[0],b[0],...), f(a[1],b[1],...), ...),
+  // and with a,b,...  all optional.
+  template <typename R = D, typename F, typename... As>
+  static R Generate(F f, As&&... as) {
+    return GenerateEach<R>(IdxSeqN(), f, std::forward<As>(as).Data()...);
+  }
+
+ public:
+  enum { SIZE = N };
+  static int Size() { return SIZE; }
+
+  void Clear() { AsD() = D(); }
+
+  T& operator[](int b) {
+    DCHECK_GE(b, 0);
+    DCHECK_LT(b, SIZE);
+    return static_cast<D&>(*this).Data()[b];
+  }
+  T operator[](int b) const {
+    DCHECK_GE(b, 0);
+    DCHECK_LT(b, SIZE);
+    return static_cast<const D&>(*this).Data()[b];
+  }
+
+  // TODO(user): Relationals should be nonmembers.
+  bool operator==(const D& b) const {
+    const T* ap = static_cast<const D&>(*this).Data();
+    return std::equal(ap, ap + this->Size(), b.Data());
+  }
+  bool operator!=(const D& b) const { return !(AsD() == b); }
+  bool operator<(const D& b) const {
+    const T* ap = static_cast<const D&>(*this).Data();
+    const T* bp = b.Data();
+    return std::lexicographical_compare(ap, ap + this->Size(), bp,
+                                        bp + b.Size());
+  }
+  bool operator>(const D& b) const { return b < AsD(); }
+  bool operator<=(const D& b) const { return !(AsD() > b); }
+  bool operator>=(const D& b) const { return !(AsD() < b); }
+
+  D& operator+=(const D& b) {
+    Idx::PlusEq(static_cast<D&>(*this).Data(), b.Data(), IdxSeqN{});
+    return static_cast<D&>(*this);
+  }
+
+  D& operator-=(const D& b) {
+    Idx::MinusEq(static_cast<D&>(*this).Data(), b.Data(), IdxSeqN{});
+    return static_cast<D&>(*this);
+  }
+
+  D& operator*=(T k) {
+    Idx::MulEq(static_cast<D&>(*this).Data(), k, IdxSeqN{});
+    return static_cast<D&>(*this);
+  }
+
+  D& operator/=(T k) {
+    Idx::DivEq(static_cast<D&>(*this).Data(), k, IdxSeqN{});
+    return static_cast<D&>(*this);
+  }
+
+  D operator+(const D& b) const { return D(AsD()) += b; }
+  D operator-(const D& b) const { return D(AsD()) -= b; }
+  D operator*(T k) const { return D(AsD()) *= k; }
+  D operator/(T k) const { return D(AsD()) /= k; }
+
+  friend D operator-(const D& a) {
+    return Generate([](const T& x) { return -x; }, a);
+  }
+
+  // Convert from another vector type
+  template <typename T2>
+  static D Cast(const VecTemplate<T2>& b) {
+    return Generate([](const T2& x) { return static_cast<T>(x); }, b);
+  }
+
+  // multiply two vectors component by component
+  D MulComponents(const D& b) const {
+    return Generate([](const T& x, const T& y) { return x * y; }, AsD(), b);
+  }
+  // divide two vectors component by component
+  D DivComponents(const D& b) const {
+    return Generate([](const T& x, const T& y) { return x / y; }, AsD(), b);
+  }
+
+  // Element-wise max.  {max(a[0],b[0]), max(a[1],b[1]), ...}
+  friend D Max(const D& a, const D& b) {
+    return Generate([](const T& x, const T& y) { return std::max(x, y); }, a,
+                    b);
+  }
+
+  // Element-wise min.  {min(a[0],b[0]), min(a[1],b[1]), ...}
+  friend D Min(const D& a, const D& b) {
+    return Generate([](const T& x, const T& y) { return std::min(x, y); }, a,
+                    b);
+  }
+
+  T DotProd(const D& b) const {
+    return Idx::Dot(static_cast<T>(0), static_cast<const D&>(*this).Data(),
+                    b.Data(), IdxSeqN{});
+  }
+
+  // Squared Euclidean norm (the dot product with itself).
+  T Norm2() const { return DotProd(AsD()); }
+
+  // Euclidean norm. For integer T, correct only if Norm2 does not overflow.
+  FloatType Norm() const {
+    using std::sqrt;
+    return sqrt(Norm2());
+  }
+
+  // Normalized vector if the norm is nonzero. Not for integer types.
+  D Normalize() const {
+    static_assert(!std::is_integral<T>::value, "must be floating point");
+    T n = Norm();
+    if (n != T(0.0)) {
+      n = T(1.0) / n;
+    }
+    return D(AsD()) *= n;
+  }
+
+  // Compose a vector from the sqrt of each component.
+  D Sqrt() const {
+    return Generate(
+        [](const T& x) {
+          using std::sqrt;
+          return sqrt(x);
+        },
+        AsD());
+  }
+
+  // Take the floor of each component.
+  D Floor() const {
+    return Generate([](const T& x) { return floor(x); }, AsD());
+  }
+
+  // Take the ceil of each component.
+  D Ceil() const {
+    return Generate([](const T& x) { return ceil(x); }, AsD());
+  }
+
+  // Round of each component.
+  D FRound() const {
+    return Generate([](const T& x) { return rint(x); }, AsD());
+  }
+
+  // Round of each component and return an integer vector.
+  VecTemplate<int> IRound() const {
+    return Generate<VecTemplate<int>>([](const T& x) { return lrint(x); },
+                                      AsD());
+  }
+
+  // True if any of the components is not a number.
+  bool IsNaN() const {
+    bool r = false;
+    const T* ap = AsD().Data();
+    for (int i = 0; i < SIZE; ++i)
+      r = r || isnan(ap[i]);
+    return r;
+  }
+
+  // A Vector populated with all NaN values.
+  static D NaN() {
+    return Generate([] { return std::numeric_limits<T>::quiet_NaN(); });
+  }
+
+  friend std::ostream& operator<<(std::ostream& out, const D& v) {
+    out << "[";
+    const char* sep = "";
+    for (int i = 0; i < SIZE; ++i) {
+      out << sep;
+      Print(out, v[i]);
+      sep = ", ";
+    }
+    return out << "]";
+  }
+
+  // These are only public for technical reasons (see cl/121145822).
+  template <typename K>
+  D MulScalarInternal(const K& k) const {
+    return Generate([k](const T& x) { return k * x; }, AsD());
+  }
+  template <typename K>
+  D DivScalarInternal(const K& k) const {
+    return Generate([k](const T& x) { return k / x; }, AsD());
+  }
+
+ private:
+  const D& AsD() const { return static_cast<const D&>(*this); }
+  D& AsD() { return static_cast<D&>(*this); }
+
+  // ostream << uint8_t prints the ASCII character, which is not useful.
+  // Cast to int so that numbers will be printed instead.
+  template <typename U>
+  static void Print(std::ostream& out, const U& v) {
+    out << v;
+  }
+  static void Print(std::ostream& out, uint8_t v) {
+    out << static_cast<int>(v);
+  }
+
+  // For loops can be force-unrolled with indexed recursion.
+  // The helpers to facilitate this are grouped here.
+  struct Idx {
+    static T Dot(T sum, const T* a, const T* b, IdxSeq<>) { return sum; }
+    template <std::size_t I, std::size_t... Is>
+    static T Dot(T sum, const T* a, const T* b, IdxSeq<I, Is...>) {
+      return Dot(sum + a[I] * b[I], a, b, IdxSeq<Is...>{});
+    }
+
+    static void PlusEq(T* a, const T* b, IdxSeq<>) {}
+    template <std::size_t I, std::size_t... Is>
+    static void PlusEq(T* a, const T* b, IdxSeq<I, Is...>) {
+      a[I] += b[I];
+      PlusEq(a, b, IdxSeq<Is...>{});
+    }
+
+    static void MinusEq(T* a, const T* b, IdxSeq<>) {}
+    template <std::size_t I, std::size_t... Is>
+    static void MinusEq(T* a, const T* b, IdxSeq<I, Is...>) {
+      a[I] -= b[I];
+      MinusEq(a, b, IdxSeq<Is...>{});
+    }
+
+    static void MulEq(T* a, T b, IdxSeq<>) {}
+    template <std::size_t I, std::size_t... Is>
+    static void MulEq(T* a, T b, IdxSeq<I, Is...>) {
+      a[I] *= b;
+      MulEq(a, b, IdxSeq<Is...>{});
+    }
+
+    static void DivEq(T* a, T b, IdxSeq<>) {}
+    template <std::size_t I, std::size_t... Is>
+    static void DivEq(T* a, T b, IdxSeq<I, Is...>) {
+      a[I] /= b;
+      DivEq(a, b, IdxSeq<Is...>{});
+    }
+  };
+};
+
+// These templates must be defined outside of BasicVector so that the
+// template specialization match algorithm must deduce 'a'. See the review
+// of cl/119944115.
+template <typename K,
+          template <typename> class VT2,
+          typename T2,
+          std::size_t N2>
+VT2<T2> operator*(const K& k, const BasicVector<VT2, T2, N2>& a) {
+  return a.MulScalarInternal(k);
+}
+template <typename K,
+          template <typename> class VT2,
+          typename T2,
+          std::size_t N2>
+VT2<T2> operator/(const K& k, const BasicVector<VT2, T2, N2>& a) {
+  return a.DivScalarInternal(k);
+}
+
+}  // namespace internal_vector
+}  // namespace math
+}  // namespace util
+
+// ======================================================================
+template <typename T>
+class Vector2 : public util::math::internal_vector::BasicVector<Vector2, T, 2> {
+ private:
+  using Base = util::math::internal_vector::BasicVector<::Vector2, T, 2>;
+  using VType = T;
+
+ public:
+  typedef VType BaseType;
+  using FloatType = typename Base::FloatType;
+  using Base::SIZE;
+
+  Vector2() : c_() {}
+  Vector2(T x, T y) {
+    c_[0] = x;
+    c_[1] = y;
+  }
+  explicit Vector2(const Vector3<T>& b) : Vector2(b.x(), b.y()) {}
+  explicit Vector2(const Vector4<T>& b) : Vector2(b.x(), b.y()) {}
+
+  T* Data() { return c_; }
+  const T* Data() const { return c_; }
+
+  void x(T v) { c_[0] = v; }
+  void y(T v) { c_[1] = v; }
+  T x() const { return c_[0]; }
+  T y() const { return c_[1]; }
+
+  bool aequal(const Vector2& vb, FloatType margin) const {
+    using std::fabs;
+    return (fabs(c_[0] - vb.c_[0]) < margin) &&
+           (fabs(c_[1] - vb.c_[1]) < margin);
+  }
+
+  void Set(T x, T y) { *this = Vector2(x, y); }
+
+  // Cross product.  Be aware that if T is an integer type, the high bits
+  // of the result are silently discarded.
+  T CrossProd(const Vector2& vb) const {
+    return c_[0] * vb.c_[1] - c_[1] * vb.c_[0];
+  }
+
+  // return the angle between "this" and v in radians
+  FloatType Angle(const Vector2& v) const {
+    using std::atan2;
+    return atan2(CrossProd(v), this->DotProd(v));
+  }
+
+  // return a vector orthogonal to the current one
+  // with the same norm and counterclockwise to it
+  Vector2 Ortho() const { return Vector2(-c_[1], c_[0]); }
+
+  // TODO(user): unify Fabs/Abs between all Vector classes.
+  Vector2 Fabs() const {
+    using std::fabs;
+    return Vector2(fabs(c_[0]), fabs(c_[1]));
+  }
+  Vector2 Abs() const {
+    static_assert(std::is_integral<VType>::value, "use Fabs for float_types");
+    static_assert(static_cast<VType>(-1) == -1, "type must be signed");
+    static_assert(sizeof(c_[0]) <= sizeof(int), "Abs truncates to int");
+    return Vector2(abs(c_[0]), abs(c_[1]));
+  }
+
+ private:
+  VType c_[SIZE];
+};
+
+template <typename T>
+class Vector3 : public util::math::internal_vector::BasicVector<Vector3, T, 3> {
+ private:
+  using Base = util::math::internal_vector::BasicVector<::Vector3, T, 3>;
+  using VType = T;
+
+ public:
+  typedef VType BaseType;
+  using FloatType = typename Base::FloatType;
+  using Base::SIZE;
+
+  Vector3() : c_() {}
+  Vector3(T x, T y, T z) {
+    c_[0] = x;
+    c_[1] = y;
+    c_[2] = z;
+  }
+  Vector3(const Vector2<T>& b, T z) : Vector3(b.x(), b.y(), z) {}
+  explicit Vector3(const Vector4<T>& b) : Vector3(b.x(), b.y(), b.z()) {}
+
+  T* Data() { return c_; }
+  const T* Data() const { return c_; }
+
+  void x(const T& v) { c_[0] = v; }
+  void y(const T& v) { c_[1] = v; }
+  void z(const T& v) { c_[2] = v; }
+  T x() const { return c_[0]; }
+  T y() const { return c_[1]; }
+  T z() const { return c_[2]; }
+
+  bool aequal(const Vector3& vb, FloatType margin) const {
+    using std::abs;
+    return (abs(c_[0] - vb.c_[0]) < margin) &&
+           (abs(c_[1] - vb.c_[1]) < margin) && (abs(c_[2] - vb.c_[2]) < margin);
+  }
+
+  void Set(T x, T y, T z) { *this = Vector3(x, y, z); }
+
+  // Cross product.  Be aware that if VType is an integer type, the high bits
+  // of the result are silently discarded.
+  Vector3 CrossProd(const Vector3& vb) const {
+    return Vector3(c_[1] * vb.c_[2] - c_[2] * vb.c_[1],
+                   c_[2] * vb.c_[0] - c_[0] * vb.c_[2],
+                   c_[0] * vb.c_[1] - c_[1] * vb.c_[0]);
+  }
+
+  // Returns a unit vector orthogonal to this one.
+  Vector3 Ortho() const {
+    int k = LargestAbsComponent() - 1;
+    if (k < 0)
+      k = 2;
+    Vector3 temp;
+    temp[k] = T(1);
+    return CrossProd(temp).Normalize();
+  }
+
+  // return the angle between two vectors in radians
+  FloatType Angle(const Vector3& va) const {
+    using std::atan2;
+    return atan2(CrossProd(va).Norm(), this->DotProd(va));
+  }
+
+  Vector3 Fabs() const { return Abs(); }
+
+  Vector3 Abs() const {
+    static_assert(
+        !std::is_integral<VType>::value || static_cast<VType>(-1) == -1,
+        "type must be signed");
+    using std::abs;
+    return Vector3(abs(c_[0]), abs(c_[1]), abs(c_[2]));
+  }
+
+  // return the index of the largest component (fabs)
+  int LargestAbsComponent() const {
+    Vector3 temp = Abs();
+    return temp[0] > temp[1] ? temp[0] > temp[2] ? 0 : 2
+                             : temp[1] > temp[2] ? 1 : 2;
+  }
+
+  // return the index of the smallest, median ,largest component of the vector
+  Vector3<int> ComponentOrder() const {
+    using std::swap;
+    Vector3<int> temp(0, 1, 2);
+    if (c_[temp[0]] > c_[temp[1]])
+      swap(temp[0], temp[1]);
+    if (c_[temp[1]] > c_[temp[2]])
+      swap(temp[1], temp[2]);
+    if (c_[temp[0]] > c_[temp[1]])
+      swap(temp[0], temp[1]);
+    return temp;
+  }
+
+ private:
+  VType c_[SIZE];
+};
+
+template <typename T>
+class Vector4 : public util::math::internal_vector::BasicVector<Vector4, T, 4> {
+ private:
+  using Base = util::math::internal_vector::BasicVector<::Vector4, T, 4>;
+  using VType = T;
+
+ public:
+  typedef VType BaseType;
+  using FloatType = typename Base::FloatType;
+  using Base::SIZE;
+
+  Vector4() : c_() {}
+  Vector4(T x, T y, T z, T w) {
+    c_[0] = x;
+    c_[1] = y;
+    c_[2] = z;
+    c_[3] = w;
+  }
+
+  Vector4(const Vector2<T>& b, T z, T w) : Vector4(b.x(), b.y(), z, w) {}
+  Vector4(const Vector2<T>& a, const Vector2<T>& b)
+      : Vector4(a.x(), a.y(), b.x(), b.y()) {}
+  Vector4(const Vector3<T>& b, T w) : Vector4(b.x(), b.y(), b.z(), w) {}
+
+  T* Data() { return c_; }
+  const T* Data() const { return c_; }
+
+  bool aequal(const Vector4& vb, FloatType margin) const {
+    using std::fabs;
+    return (fabs(c_[0] - vb.c_[0]) < margin) &&
+           (fabs(c_[1] - vb.c_[1]) < margin) &&
+           (fabs(c_[2] - vb.c_[2]) < margin) &&
+           (fabs(c_[3] - vb.c_[3]) < margin);
+  }
+
+  void x(const T& v) { c_[0] = v; }
+  void y(const T& v) { c_[1] = v; }
+  void z(const T& v) { c_[2] = v; }
+  void w(const T& v) { c_[3] = v; }
+  T x() const { return c_[0]; }
+  T y() const { return c_[1]; }
+  T z() const { return c_[2]; }
+  T w() const { return c_[3]; }
+
+  void Set(T x, T y, T z, T w) { *this = Vector4(x, y, z, w); }
+
+  Vector4 Fabs() const {
+    using std::fabs;
+    return Vector4(fabs(c_[0]), fabs(c_[1]), fabs(c_[2]), fabs(c_[3]));
+  }
+
+  Vector4 Abs() const {
+    static_assert(std::is_integral<VType>::value, "use Fabs for float types");
+    static_assert(static_cast<VType>(-1) == -1, "type must be signed");
+    static_assert(sizeof(c_[0]) <= sizeof(int), "Abs truncates to int");
+    return Vector4(abs(c_[0]), abs(c_[1]), abs(c_[2]), abs(c_[3]));
+  }
+
+ private:
+  VType c_[SIZE];
+};
+
+typedef Vector2<uint8_t> Vector2_b;
+typedef Vector2<int> Vector2_i;
+typedef Vector2<float> Vector2_f;
+typedef Vector2<double> Vector2_d;
+
+typedef Vector3<uint8_t> Vector3_b;
+typedef Vector3<int> Vector3_i;
+typedef Vector3<float> Vector3_f;
+typedef Vector3<double> Vector3_d;
+
+typedef Vector4<uint8_t> Vector4_b;
+typedef Vector4<int> Vector4_i;
+typedef Vector4<float> Vector4_f;
+typedef Vector4<double> Vector4_d;
+
+#endif  // S2_UTIL_MATH_VECTOR_H_
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index df009da..ba581ef2 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -27,7 +27,7 @@
 # Do NOT CHANGE this if you don't know what you're doing -- see
 # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
-CLANG_REVISION = '313222'
+CLANG_REVISION = '313786'
 
 use_head_revision = 'LLVM_FORCE_HEAD_REVISION' in os.environ
 if use_head_revision:
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 86b77885..cfafc0c 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -38874,6 +38874,15 @@
   <int value="3" label="AUTO_UPDATES_ONLY"/>
 </enum>
 
+<enum name="UpgradeNotificationStage">
+  <int value="0" label="None"/>
+  <int value="1" label="Low"/>
+  <int value="2" label="Elevated"/>
+  <int value="3" label="High"/>
+  <int value="4" label="Severe"/>
+  <int value="5" label="Critical"/>
+</enum>
+
 <enum name="URLRequestAnnotationType">
 <!-- Generated from tools/traffic_annotation/summary/annotations.xml.
 Called by update_traffic_annotation_histograms.py.-->
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e7221de..be7559d9 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -28668,12 +28668,27 @@
 
 <histogram name="interstitial.ssl.did_user_revoke_decisions"
     enum="BooleanRevoked">
+  <obsolete>
+    Deprecated September 13 2017.
+  </obsolete>
   <owner>jww@chromium.org</owner>
   <summary>
     Specifies when a user enters the page info menu whether or not the user
     pressed the SSL decisions revoke button. This can only by done if the user
     is in the &quot;Remember Certificate Error Decisions&quot; experiment. This
-    is logged when the page info UI is closed.
+    is logged when the page info UI is closed. Replaced by
+    .did_user_revoke_decisions2 to keep data separate after changing
+    functionality to only log when button was visible.
+  </summary>
+</histogram>
+
+<histogram name="interstitial.ssl.did_user_revoke_decisions2"
+    enum="BooleanRevoked">
+  <owner>carlosil@chromium.org</owner>
+  <summary>
+    Specifies when a user enters the page info menu whether or not the user
+    pressed the SSL decisions revoke button. This is logged when the page info
+    UI is closed, only if the button was visible in the page info.
   </summary>
 </histogram>
 
@@ -54804,6 +54819,17 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.AboutBlankPasswordSubmission"
+    enum="BooleanMainFrame">
+  <owner>alexmos@chromium.org</owner>
+  <owner>battre@chromium.org</owner>
+  <summary>
+    Records attempts to submit a password on a form in an about:blank frame,
+    indicating whether this attempt is for a main frame or subframe.  Recorded
+    once per form submission.
+  </summary>
+</histogram>
+
 <histogram name="PasswordManager.AcceptedSaveUpdateSubmissionIndicatorEvent"
     enum="SubmissionIndicatorEvent">
   <owner>dvadym@chromium.org</owner>
@@ -86425,6 +86451,14 @@
   </summary>
 </histogram>
 
+<histogram name="UpgradeDetector.NotificationStage"
+    enum="UpgradeNotificationStage">
+  <owner>spqchan@chromium.com</owner>
+  <summary>
+    Tracks the upgrade notification stage. This is recorded with every UMA log.
+  </summary>
+</histogram>
+
 <histogram name="Uptime.ChromeExecToLoginPromptVisibleAfterLogout" units="ms">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>kouhei@chromium.org</owner>
diff --git a/tools/perf/chromium.perf.fyi.extras.json b/tools/perf/chromium.perf.fyi.extras.json
index a29457f..790a07c 100644
--- a/tools/perf/chromium.perf.fyi.extras.json
+++ b/tools/perf/chromium.perf.fyi.extras.json
@@ -39,7 +39,8 @@
           "--output-format=histograms",
           "--output-format=json-test-results",
           "--browser=release",
-          "--xvfb"
+          "--xvfb",
+          "--also-run-disabled-tests"
         ],
         "isolate_name": "telemetry_perf_tests",
         "name": "dummy_benchmark.histogram_benchmark_1",
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index f9c5e11..4c004d18 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -2275,13 +2275,17 @@
 }
 
 STDMETHODIMP AXPlatformNodeWin::get_nSelections(LONG* n_selections) {
+  WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTIONS);
   COM_OBJECT_VALIDATE_1_ARG(n_selections);
-  int sel_start = GetIntAttribute(AX_ATTR_TEXT_SEL_START);
-  int sel_end = GetIntAttribute(AX_ATTR_TEXT_SEL_END);
-  if (sel_start != sel_end)
+  AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes);
+
+  *n_selections = 0;
+  int selection_start, selection_end;
+  GetSelectionOffsets(&selection_start, &selection_end);
+  if (selection_start >= 0 && selection_end >= 0 &&
+      selection_start != selection_end) {
     *n_selections = 1;
-  else
-    *n_selections = 0;
+  }
   return S_OK;
 }
 
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index 05f2aa9..afa8066 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -2652,4 +2652,47 @@
   EXPECT_EQ(2, end_offset);
 }
 
-}  // namespace ui
+TEST_F(AXPlatformNodeWinTest, TestIAccessibleTextTextFieldGetNSelectionsZero) {
+  Init(BuildTextField());
+
+  ScopedComPtr<IAccessible2> ia2_text_field =
+      ToIAccessible2(GetRootIAccessible());
+  ScopedComPtr<IAccessibleText> text_field;
+  ia2_text_field.CopyTo(text_field.GetAddressOf());
+  ASSERT_NE(nullptr, text_field.Get());
+
+  LONG selections;
+  EXPECT_HRESULT_SUCCEEDED(text_field->get_nSelections(&selections));
+  EXPECT_EQ(0, selections);
+}
+
+TEST_F(AXPlatformNodeWinTest,
+       TestIAccessibleTextContentEditableGetNSelectionsZero) {
+  Init(BuildContentEditable());
+
+  ScopedComPtr<IAccessible2> ia2_text_field =
+      ToIAccessible2(GetRootIAccessible());
+  ScopedComPtr<IAccessibleText> text_field;
+  ia2_text_field.CopyTo(text_field.GetAddressOf());
+  ASSERT_NE(nullptr, text_field.Get());
+
+  LONG selections;
+  EXPECT_HRESULT_SUCCEEDED(text_field->get_nSelections(&selections));
+  EXPECT_EQ(0, selections);
+}
+
+TEST_F(AXPlatformNodeWinTest,
+       TestIAccessibleTextContentEditableGetNSelections) {
+  Init(BuildContentEditableWithSelectionRange(1, 2));
+  ScopedComPtr<IAccessible2> ia2_text_field =
+      ToIAccessible2(GetRootIAccessible());
+  ScopedComPtr<IAccessibleText> text_field;
+  ia2_text_field.CopyTo(text_field.GetAddressOf());
+  ASSERT_NE(nullptr, text_field.Get());
+
+  LONG selections;
+  EXPECT_HRESULT_SUCCEEDED(text_field->get_nSelections(&selections));
+  EXPECT_EQ(1, selections);
+}
+
+}  // namespace ui
\ No newline at end of file
diff --git a/ui/display/manager/display_manager_utilities.cc b/ui/display/manager/display_manager_utilities.cc
index 7f5737c..511dde6 100644
--- a/ui/display/manager/display_manager_utilities.cc
+++ b/ui/display/manager/display_manager_utilities.cc
@@ -227,8 +227,13 @@
   int rb = std::min(a_bounds.bottom(), b_bounds.bottom());
 
   DisplayPlacement::Position position;
-  if ((rb - ry) == 0) {
+  if (rb == ry) {
     // top bottom
+    if (rr <= rx) {
+      // Top and bottom align, but no edges are shared.
+      return false;
+    }
+
     if (a_bounds.bottom() == b_bounds.y()) {
       position = DisplayPlacement::BOTTOM;
     } else if (a_bounds.y() == b_bounds.bottom()) {
@@ -236,16 +241,22 @@
     } else {
       return false;
     }
-  } else {
+  } else if (rr == rx) {
     // left right
+    if (rb <= ry) {
+      // Left and right align, but no edges are shared.
+      return false;
+    }
+
     if (a_bounds.right() == b_bounds.x()) {
       position = DisplayPlacement::RIGHT;
     } else if (a_bounds.x() == b_bounds.right()) {
       position = DisplayPlacement::LEFT;
     } else {
-      DCHECK_NE(rr, rx);
       return false;
     }
+  } else {
+    return false;
   }
 
   switch (position) {
diff --git a/ui/display/manager/display_manager_utilities_unittest.cc b/ui/display/manager/display_manager_utilities_unittest.cc
index f31ab79a..ca57909d 100644
--- a/ui/display/manager/display_manager_utilities_unittest.cc
+++ b/ui/display/manager/display_manager_utilities_unittest.cc
@@ -110,4 +110,53 @@
   }
 }
 
+TEST(DisplayUtilitiesTest, ComputeBoundary) {
+  // Two displays with their top and bottom align but share no edges.
+  // +----+
+  // |    |
+  // +----+  +----+
+  //         |    |
+  //         +----+
+  Display display_1(1, gfx::Rect(0, 0, 500, 300));
+  Display display_2(2, gfx::Rect(759, 300, 133, 182));
+  gfx::Rect edge_1;
+  gfx::Rect edge_2;
+  EXPECT_FALSE(ComputeBoundary(display_1, display_2, &edge_1, &edge_2));
+
+  // Two displays with their left and right align but share no edges.
+  // +----+
+  // |    |
+  // +----+
+  //
+  //      +----+
+  //      |    |
+  //      +----+
+  display_1.set_bounds(gfx::Rect(0, 0, 500, 300));
+  display_2.set_bounds(gfx::Rect(500, 500, 240, 300));
+  EXPECT_FALSE(ComputeBoundary(display_1, display_2, &edge_1, &edge_2));
+
+  // Special case: all edges align but no edges are shared.
+  // +----+
+  // |    |
+  // +----+----+
+  //      |    |
+  //      +----+
+  display_1.set_bounds(gfx::Rect(0, 0, 500, 300));
+  display_2.set_bounds(gfx::Rect(500, 300, 500, 300));
+  EXPECT_FALSE(ComputeBoundary(display_1, display_2, &edge_1, &edge_2));
+
+  // Test normal cases.
+  display_1.set_bounds(gfx::Rect(740, 0, 150, 300));
+  display_2.set_bounds(gfx::Rect(759, 300, 133, 182));
+  EXPECT_TRUE(ComputeBoundary(display_1, display_2, &edge_1, &edge_2));
+  EXPECT_EQ("759,299 131x1", edge_1.ToString());
+  EXPECT_EQ("759,300 131x1", edge_2.ToString());
+
+  display_1.set_bounds(gfx::Rect(0, 0, 400, 400));
+  display_2.set_bounds(gfx::Rect(400, 150, 400, 400));
+  EXPECT_TRUE(ComputeBoundary(display_1, display_2, &edge_1, &edge_2));
+  EXPECT_EQ("399,150 1x250", edge_1.ToString());
+  EXPECT_EQ("400,150 1x250", edge_2.ToString());
+}
+
 }  // namespace display
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index f9ec7b1..f9d774f 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -1104,28 +1104,65 @@
   ]
 }
 
-if (is_mac) {
-  test("macviews_interactive_ui_tests") {
-    sources = [
+# This target is added as a dependency of browser interactive_ui_tests. It must
+# be source_set, otherwise the linker will drop the tests as dead code.
+source_set("views_interactive_ui_tests") {
+  testonly = true
+
+  sources = [
+    "widget/widget_interactive_uitest.cc",
+  ]
+
+  deps = [
+    ":test_support",
+    ":views",
+    "//base",
+    "//base/test:test_support",
+    "//mojo/edk/system",
+    "//skia",
+    "//testing/gtest",
+    "//ui/base:test_support",
+    "//ui/compositor",
+    "//ui/events:test_support",
+    "//ui/gl:test_support",
+    "//ui/resources",
+    "//ui/resources:ui_test_pak",
+    "//ui/strings",
+  ]
+
+  if (is_win) {
+    sources += [ "accessibility/ax_system_caret_win_interactive_uitest.cc" ]
+  }
+
+  if (is_mac) {
+    sources += [
       "cocoa/bridged_native_widget_interactive_uitest.mm",
-      "run_all_unittests_main.cc",
       "widget/native_widget_mac_interactive_uitest.mm",
     ]
-    deps = [
-      ":test_support",
-      ":views",
-      "//base",
-      "//base/test:test_support",
-      "//mojo/edk/system",
-      "//skia",
-      "//testing/gtest",
-      "//ui/base:test_support",
-      "//ui/compositor",
-      "//ui/events:test_support",
-      "//ui/gl:test_support",
-      "//ui/resources",
-      "//ui/resources:ui_test_pak",
-      "//ui/strings",
+  }
+
+  if (use_aura) {
+    sources += [
+      "corewm/desktop_capture_controller_unittest.cc",
+      "widget/native_widget_aura_interactive_uitest.cc",
     ]
+    deps += [
+      "//ui/aura",
+      "//ui/aura:test_support",
+      "//ui/wm",
+      "//ui/wm/public",
+    ]
+  }
+
+  if (use_x11) {
+    sources += [
+      "widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc",
+      "widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc",
+    ]
+    deps += [ "//ui/events/platform/x11:x11" ]
+  }
+
+  if (is_chromeos) {
+    sources -= [ "corewm/desktop_capture_controller_unittest.cc" ]
   }
 }
diff --git a/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm b/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
index a959ac0f..9e328b92 100644
--- a/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
+++ b/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm
@@ -15,6 +15,7 @@
 #include "ui/base/test/ui_controls.h"
 #import "ui/base/test/windowed_nsnotification_observer.h"
 #import "ui/events/test/cocoa_test_event_utils.h"
+#include "ui/views/test/views_interactive_ui_test_base.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/widget/native_widget_mac.h"
 #include "ui/views/window/native_frame_view.h"
@@ -41,14 +42,11 @@
 
 class BridgedNativeWidgetUITest : public test::WidgetTest {
  public:
-  BridgedNativeWidgetUITest() {
-    // TODO(tapted): Remove this when these are absorbed into Chrome's
-    // interactive_ui_tests target. See http://crbug.com/403679.
-    ui_controls::EnableUIControls();
-  }
+  BridgedNativeWidgetUITest() = default;
 
   // testing::Test:
   void SetUp() override {
+    ViewsInteractiveUITestBase::InteractiveSetUp();
     WidgetTest::SetUp();
     Widget::InitParams init_params =
         CreateParams(Widget::InitParams::TYPE_WINDOW);
@@ -72,6 +70,9 @@
 
  protected:
   std::unique_ptr<Widget> widget_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetUITest);
 };
 
 // Tests for correct fullscreen tracking, regardless of whether it is initiated
diff --git a/ui/views/paint_info.cc b/ui/views/paint_info.cc
index fd74959..5330215 100644
--- a/ui/views/paint_info.cc
+++ b/ui/views/paint_info.cc
@@ -20,21 +20,6 @@
   return PaintInfo(parent_paint_info, bounds, parent_size, scale_type);
 }
 
-// static
-PaintInfo PaintInfo::ClonePaintInfo(const PaintInfo& parent_paint_info) {
-  return PaintInfo(parent_paint_info,
-                   ui::PaintContext::CLONE_WITHOUT_INVALIDATION);
-}
-
-PaintInfo::PaintInfo(const PaintInfo& other,
-                     ui::PaintContext::CloneWithoutInvalidation c)
-    : paint_recording_scale_x_(other.paint_recording_scale_x_),
-      paint_recording_scale_y_(other.paint_recording_scale_y_),
-      paint_recording_bounds_(other.paint_recording_bounds_),
-      offset_from_parent_(other.offset_from_parent_),
-      context_(other.context(), c),
-      root_context_(nullptr) {}
-
 PaintInfo::~PaintInfo() {}
 
 bool PaintInfo::IsPixelCanvas() const {
diff --git a/ui/views/paint_info.h b/ui/views/paint_info.h
index 7e38d37..7b99dba 100644
--- a/ui/views/paint_info.h
+++ b/ui/views/paint_info.h
@@ -45,10 +45,6 @@
                                         const gfx::Size& parent_size,
                                         ScaleType scale_type);
 
-  // Clones a given paint info, |other|, without the invalidation from its
-  // PaintContext.
-  static PaintInfo ClonePaintInfo(const PaintInfo& other);
-
   PaintInfo(const PaintInfo& other);
   ~PaintInfo();
 
@@ -87,8 +83,6 @@
             const gfx::Rect& bounds,
             const gfx::Size& parent_size,
             ScaleType scale_type);
-  PaintInfo(const PaintInfo& other,
-            ui::PaintContext::CloneWithoutInvalidation c);
 
   // Scales the |child_bounds| to its recording bounds based on the
   // |context.device_scale_factor()|. The recording bounds are snapped to the
diff --git a/ui/views/test/views_interactive_ui_test_base.cc b/ui/views/test/views_interactive_ui_test_base.cc
index f318b15..bf27bae 100644
--- a/ui/views/test/views_interactive_ui_test_base.cc
+++ b/ui/views/test/views_interactive_ui_test_base.cc
@@ -16,7 +16,8 @@
 
 ViewsInteractiveUITestBase::~ViewsInteractiveUITestBase() {}
 
-void ViewsInteractiveUITestBase::SetUp() {
+// static
+void ViewsInteractiveUITestBase::InteractiveSetUp() {
   // Mojo is initialized here similar to how each browser test case initializes
   // Mojo when starting. This only works because each interactive_ui_test runs
   // in a new process.
@@ -27,7 +28,10 @@
   base::FilePath ui_test_pak_path;
   ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
   ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
+}
 
+void ViewsInteractiveUITestBase::SetUp() {
+  InteractiveSetUp();
   ViewsTestBase::SetUp();
 }
 
diff --git a/ui/views/test/views_interactive_ui_test_base.h b/ui/views/test/views_interactive_ui_test_base.h
index 22f7aee..3c3e0e2 100644
--- a/ui/views/test/views_interactive_ui_test_base.h
+++ b/ui/views/test/views_interactive_ui_test_base.h
@@ -15,6 +15,9 @@
   ViewsInteractiveUITestBase();
   ~ViewsInteractiveUITestBase() override;
 
+  static void InteractiveSetUp();
+
+  // ViewsTestBase:
   void SetUp() override;
 
  private:
diff --git a/ui/views/widget/native_widget_mac_interactive_uitest.mm b/ui/views/widget/native_widget_mac_interactive_uitest.mm
index 4e8cf2b6..faa01ce5 100644
--- a/ui/views/widget/native_widget_mac_interactive_uitest.mm
+++ b/ui/views/widget/native_widget_mac_interactive_uitest.mm
@@ -14,6 +14,7 @@
 #import "ui/events/test/cocoa_test_event_utils.h"
 #include "ui/views/bubble/bubble_dialog_delegate.h"
 #include "ui/views/test/test_widget_observer.h"
+#include "ui/views/test/views_interactive_ui_test_base.h"
 #include "ui/views/test/widget_test.h"
 
 namespace views {
@@ -27,11 +28,12 @@
  public:
   class Observer;
 
-  NativeWidgetMacInteractiveUITest()
-      : activationCount_(0), deactivationCount_(0) {
-    // TODO(tapted): Remove this when these are absorbed into Chrome's
-    // interactive_ui_tests target. See http://crbug.com/403679.
-    ui_controls::EnableUIControls();
+  NativeWidgetMacInteractiveUITest() = default;
+
+  // WidgetTest:
+  void SetUp() override {
+    ViewsInteractiveUITestBase::InteractiveSetUp();
+    WidgetTest::SetUp();
   }
 
   Widget* MakeWidget() {
@@ -41,8 +43,8 @@
 
  protected:
   std::unique_ptr<Observer> observer_;
-  int activationCount_;
-  int deactivationCount_;
+  int activation_count_ = 0;
+  int deactivation_count_ = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacInteractiveUITest);
@@ -55,9 +57,9 @@
 
   void OnWidgetActivationChanged(Widget* widget, bool active) override {
     if (active)
-      parent_->activationCount_++;
+      parent_->activation_count_++;
     else
-      parent_->deactivationCount_++;
+      parent_->deactivation_count_++;
   }
 
  private:
@@ -72,37 +74,44 @@
   observer_.reset(new Observer(this, widget));
 
   EXPECT_FALSE(widget->IsActive());
-  EXPECT_EQ(0, activationCount_);
-  widget->Show();
+  EXPECT_EQ(0, activation_count_);
+  {
+    WidgetActivationWaiter wait_for_first_active(widget, true);
+    widget->Show();
+    wait_for_first_active.Wait();
+  }
   EXPECT_TRUE(widget->IsActive());
-  RunPendingMessages();
   EXPECT_TRUE([widget->GetNativeWindow() isKeyWindow]);
-  EXPECT_EQ(1, activationCount_);
-  EXPECT_EQ(0, deactivationCount_);
+  EXPECT_EQ(1, activation_count_);
+  EXPECT_EQ(0, deactivation_count_);
 
   // Now check that losing and gaining key status due events outside of Widget
   // works correctly.
   Widget* widget2 = MakeWidget();  // Note: not observed.
-  EXPECT_EQ(0, deactivationCount_);
-  widget2->Show();
-  EXPECT_EQ(1, deactivationCount_);
-
-  RunPendingMessages();
+  EXPECT_EQ(0, deactivation_count_);
+  {
+    WidgetActivationWaiter wait_for_deactivate(widget, false);
+    widget2->Show();
+    wait_for_deactivate.Wait();
+  }
+  EXPECT_EQ(1, deactivation_count_);
   EXPECT_FALSE(widget->IsActive());
-  EXPECT_EQ(1, deactivationCount_);
-  EXPECT_EQ(1, activationCount_);
+  EXPECT_EQ(1, activation_count_);
 
-  [widget->GetNativeWindow() makeKeyAndOrderFront:nil];
-  RunPendingMessages();
+  {
+    WidgetActivationWaiter wait_for_external_activate(widget, true);
+    [widget->GetNativeWindow() makeKeyAndOrderFront:nil];
+    wait_for_external_activate.Wait();
+  }
   EXPECT_TRUE(widget->IsActive());
-  EXPECT_EQ(1, deactivationCount_);
-  EXPECT_EQ(2, activationCount_);
+  EXPECT_EQ(1, deactivation_count_);
+  EXPECT_EQ(2, activation_count_);
 
   widget2->CloseNow();
   widget->CloseNow();
 
-  EXPECT_EQ(1, deactivationCount_);
-  EXPECT_EQ(2, activationCount_);
+  EXPECT_EQ(1, deactivation_count_);
+  EXPECT_EQ(2, activation_count_);
 }
 
 // Test that ShowInactive does not take keyWindow status.
diff --git a/ui/webui/resources/polymer_resources.grdp b/ui/webui/resources/polymer_resources.grdp
index ce7085b..49921f7e 100644
--- a/ui/webui/resources/polymer_resources.grdp
+++ b/ui/webui/resources/polymer_resources.grdp
@@ -437,12 +437,6 @@
   <structure name="IDR_POLYMER_1_0_PAPER_CHECKBOX_PAPER_CHECKBOX_HTML"
              file="../../../third_party/polymer/v1_0/components-chromium/paper-checkbox/paper-checkbox.html"
              type="chrome_html" />
-  <structure name="IDR_POLYMER_1_0_PAPER_DRAWER_PANEL_PAPER_DRAWER_PANEL_EXTRACTED_JS"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-drawer-panel/paper-drawer-panel-extracted.js"
-             type="chrome_html" />
-  <structure name="IDR_POLYMER_1_0_PAPER_DRAWER_PANEL_PAPER_DRAWER_PANEL_HTML"
-             file="../../../third_party/polymer/v1_0/components-chromium/paper-drawer-panel/paper-drawer-panel.html"
-             type="chrome_html" />
   <structure name="IDR_POLYMER_1_0_PAPER_FAB_PAPER_FAB_EXTRACTED_JS"
              file="../../../third_party/polymer/v1_0/components-chromium/paper-fab/paper-fab-extracted.js"
              type="chrome_html" />