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 "Remember Certificate Error Decisions" 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" />