| Index: third_party/WebKit/Source/modules/media_controls/MediaControlsOrientationLockDelegate.cpp
|
| diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsOrientationLockDelegate.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsOrientationLockDelegate.cpp
|
| index b6f4ab8ff80bd3d25fff272a1db1151047b8a84e..03ab1310b9f332678b20a8ba090b856abbedd8f6 100644
|
| --- a/third_party/WebKit/Source/modules/media_controls/MediaControlsOrientationLockDelegate.cpp
|
| +++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsOrientationLockDelegate.cpp
|
| @@ -5,13 +5,33 @@
|
| #include "modules/media_controls/MediaControlsOrientationLockDelegate.h"
|
|
|
| #include "core/events/Event.h"
|
| +#include "core/frame/LocalDOMWindow.h"
|
| +#include "core/frame/Screen.h"
|
| #include "core/frame/ScreenOrientationController.h"
|
| #include "core/html/HTMLVideoElement.h"
|
| #include "core/page/ChromeClient.h"
|
| +#include "modules/device_orientation/DeviceOrientationData.h"
|
| +#include "modules/device_orientation/DeviceOrientationEvent.h"
|
| +#include "modules/screen_orientation/ScreenOrientation.h"
|
| +#include "modules/screen_orientation/ScreenScreenOrientation.h"
|
| #include "platform/Histogram.h"
|
| +#include "platform/RuntimeEnabledFeatures.h"
|
| +#include "platform/wtf/Functional.h"
|
| +#include "platform/wtf/MathExtras.h"
|
| #include "public/platform/WebScreenInfo.h"
|
| #include "public/platform/modules/screen_orientation/WebLockOrientationCallback.h"
|
|
|
| +#if OS(ANDROID)
|
| +#include "platform/mojo/MojoHelper.h"
|
| +#include "public/platform/Platform.h"
|
| +#include "services/device/public/interfaces/constants.mojom-blink.h"
|
| +#include "services/service_manager/public/cpp/connector.h"
|
| +#endif // OS(ANDROID)
|
| +
|
| +#undef atan2 // to use std::atan2 instead of wtf_atan2
|
| +#undef fmod // to use std::fmod instead of wtf_fmod
|
| +#include <cmath>
|
| +
|
| namespace blink {
|
|
|
| namespace {
|
| @@ -119,15 +139,17 @@ void MediaControlsOrientationLockDelegate::MaybeLockOrientation() {
|
| return;
|
| }
|
|
|
| - WebScreenOrientationLockType orientation_lock = ComputeOrientationLock();
|
| - controller->lock(orientation_lock,
|
| + locked_orientation_ = ComputeOrientationLock();
|
| + DCHECK_NE(locked_orientation_, kWebScreenOrientationLockDefault);
|
| + controller->lock(locked_orientation_,
|
| WTF::WrapUnique(new DummyScreenOrientationCallback));
|
| - should_unlock_orientation_ = true;
|
|
|
| - if (orientation_lock == kWebScreenOrientationLockLandscape)
|
| + if (locked_orientation_ == kWebScreenOrientationLockLandscape)
|
| RecordLockResult(LockResultMetrics::kLandscape);
|
| else
|
| RecordLockResult(LockResultMetrics::kPortrait);
|
| +
|
| + MaybeListenToDeviceOrientation();
|
| }
|
|
|
| void MediaControlsOrientationLockDelegate::MaybeUnlockOrientation() {
|
| @@ -135,11 +157,68 @@ void MediaControlsOrientationLockDelegate::MaybeUnlockOrientation() {
|
|
|
| state_ = State::kPendingFullscreen;
|
|
|
| - if (!should_unlock_orientation_)
|
| + if (locked_orientation_ == kWebScreenOrientationLockDefault /* unlocked */)
|
| return;
|
|
|
| + monitor_.reset(); // Cancel any GotIsAutoRotateEnabledByUser Mojo callback.
|
| + if (LocalDOMWindow* dom_window = GetDocument().domWindow()) {
|
| + dom_window->removeEventListener(EventTypeNames::deviceorientation, this,
|
| + false);
|
| + }
|
| +
|
| ScreenOrientationController::From(*GetDocument().GetFrame())->unlock();
|
| - should_unlock_orientation_ = false;
|
| + locked_orientation_ = kWebScreenOrientationLockDefault /* unlocked */;
|
| +}
|
| +
|
| +void MediaControlsOrientationLockDelegate::MaybeListenToDeviceOrientation() {
|
| + DCHECK_EQ(state_, State::kMaybeLockedFullscreen);
|
| + DCHECK_NE(locked_orientation_, kWebScreenOrientationLockDefault);
|
| +
|
| + // If the rotate-to-fullscreen feature is also enabled, then start listening
|
| + // to deviceorientation events so the orientation can be unlocked once the
|
| + // user rotates the device to match the video's orientation (allowing the user
|
| + // to then exit fullscreen by rotating their device back to the opposite
|
| + // orientation). Otherwise, don't listen for deviceorientation events and just
|
| + // hold the orientation lock until the user exits fullscreen (which prevents
|
| + // the user rotating to the wrong fullscreen orientation).
|
| + if (!RuntimeEnabledFeatures::videoRotateToFullscreenEnabled())
|
| + return;
|
| +
|
| +// Check whether the user locked screen orientation at the OS level.
|
| +#if OS(ANDROID)
|
| + DCHECK(!monitor_.is_bound());
|
| + Platform::Current()->GetConnector()->BindInterface(
|
| + device::mojom::blink::kServiceName, mojo::MakeRequest(&monitor_));
|
| + monitor_->IsAutoRotateEnabledByUser(ConvertToBaseCallback(WTF::Bind(
|
| + &MediaControlsOrientationLockDelegate::GotIsAutoRotateEnabledByUser,
|
| + WrapPersistent(this))));
|
| +#else
|
| + GotIsAutoRotateEnabledByUser(true); // Assume always enabled on other OSes.
|
| +#endif // OS(ANDROID)
|
| +}
|
| +
|
| +void MediaControlsOrientationLockDelegate::GotIsAutoRotateEnabledByUser(
|
| + bool enabled) {
|
| + monitor_.reset();
|
| +
|
| + if (!enabled) {
|
| + // Since the user has locked their screen orientation, prevent
|
| + // MediaControlsRotateToFullscreenDelegate from exiting fullscreen by not
|
| + // listening for deviceorientation events and instead continuing to hold the
|
| + // orientation lock until the user exits fullscreen. This enables users to
|
| + // watch videos in bed with their head facing sideways (which requires a
|
| + // landscape screen orientation when the device is portrait and vice versa).
|
| + // TODO(johnme): Ideally we would start listening for deviceorientation
|
| + // events and allow rotating to exit if a user enables screen auto rotation
|
| + // after we have locked to landscape. That would require listening for
|
| + // changes to the auto rotate setting, rather than only checking it once.
|
| + return;
|
| + }
|
| +
|
| + if (LocalDOMWindow* dom_window = GetDocument().domWindow()) {
|
| + dom_window->addEventListener(EventTypeNames::deviceorientation, this,
|
| + false);
|
| + }
|
| }
|
|
|
| HTMLVideoElement& MediaControlsOrientationLockDelegate::VideoElement() const {
|
| @@ -173,6 +252,12 @@ void MediaControlsOrientationLockDelegate::handleEvent(
|
| return;
|
| }
|
|
|
| + if (event->type() == EventTypeNames::deviceorientation) {
|
| + MaybeUnlockIfDeviceOrientationMatchesVideo(ToDeviceOrientationEvent(event));
|
| +
|
| + return;
|
| + }
|
| +
|
| NOTREACHED();
|
| }
|
|
|
| @@ -212,6 +297,95 @@ MediaControlsOrientationLockDelegate::ComputeOrientationLock() const {
|
| return kWebScreenOrientationLockLandscape;
|
| }
|
|
|
| +void MediaControlsOrientationLockDelegate::
|
| + MaybeUnlockIfDeviceOrientationMatchesVideo(DeviceOrientationEvent* event) {
|
| + DCHECK_EQ(state_, State::kMaybeLockedFullscreen);
|
| + DCHECK_NE(locked_orientation_, kWebScreenOrientationLockDefault);
|
| +
|
| + LocalDOMWindow* dom_window = GetDocument().domWindow();
|
| + if (!dom_window)
|
| + return;
|
| +
|
| + if (!event->Orientation()->CanProvideBeta() ||
|
| + !event->Orientation()->CanProvideGamma()) {
|
| + return;
|
| + }
|
| + double beta = event->Orientation()->Beta();
|
| + double gamma = event->Orientation()->Gamma();
|
| +
|
| + // Calculate the projection of the up vector (normal to the earth's surface)
|
| + // onto the device's screen in its natural orientation. (x,y) will lie within
|
| + // the unit circle centered on (0,0), e.g. if the top of the device is
|
| + // pointing upwards (x,y) will be (0,-1).
|
| + double x = -std::sin(deg2rad(gamma)) * std::cos(deg2rad(beta));
|
| + double y = -std::sin(deg2rad(beta));
|
| +
|
| + // Convert (x,y) to polar coordinates: 0 <= device_orientation_angle < 360 and
|
| + // 0 <= r <= 1, such that device_orientation_angle is the clockwise angle in
|
| + // degrees between the current physical orientation of the device and the
|
| + // natural physical orientation of the device (ignoring the screen
|
| + // orientation). Thus snapping device_orientation_angle to the nearest
|
| + // multiple of 90 gives the value screen.orientation.angle would have if the
|
| + // screen orientation was allowed to rotate freely to match the device
|
| + // orientation. Note that we want device_orientation_angle==0 when the top of
|
| + // the device is pointing upwards, but atan2's zero angle points to the right,
|
| + // so we pass y=x and x=-y to atan2 to rotate by 90 degrees.
|
| + double r = std::sqrt(x * x + y * y);
|
| + double device_orientation_angle =
|
| + std::fmod(rad2deg(std::atan2(/* y= */ x, /* x= */ -y)) + 360, 360);
|
| +
|
| + constexpr double kMinElevationAngle = 24; // degrees from horizontal plane
|
| + if (r < std::sin(deg2rad(kMinElevationAngle)))
|
| + return; // Device is too flat to reliably determine orientation.
|
| +
|
| + // device_orientation_angle snapped to nearest multiple of 90.
|
| + int device_orientation_angle90 =
|
| + std::lround(device_orientation_angle / 90) * 90;
|
| +
|
| + if (std::abs(device_orientation_angle - device_orientation_angle90) > 23) {
|
| + // Device is diagonal (within 44 degree hysteresis zone).
|
| + return;
|
| + }
|
| +
|
| + // screen.orientation.angle is the standardized replacement for
|
| + // window.orientation. They are equal, except -90 was replaced by 270.
|
| + int screen_orientation_angle =
|
| + ScreenScreenOrientation::orientation(nullptr /* ScriptState */,
|
| + *dom_window->screen())
|
| + ->angle();
|
| +
|
| + // This is equivalent to screen.orientation.type.startsWith('landscape').
|
| + bool screen_orientation_is_landscape =
|
| + dom_window->screen()->width() > dom_window->screen()->height();
|
| +
|
| + // The natural orientation of the device could either be portrait (almost
|
| + // all phones, and some tablets like Nexus 7) or landscape (other tablets
|
| + // like Pixel C). Detect this by comparing angle to orientation.
|
| + // TODO(johnme): This might get confused on square screens.
|
| + bool screen_orientation_is_natural_or_flipped_natural =
|
| + screen_orientation_angle % 180 == 0;
|
| + bool natural_orientation_is_landscape =
|
| + screen_orientation_is_landscape ==
|
| + screen_orientation_is_natural_or_flipped_natural;
|
| +
|
| + bool natural_orientation_matches_video =
|
| + natural_orientation_is_landscape ==
|
| + (locked_orientation_ == kWebScreenOrientationLockLandscape);
|
| +
|
| + // If natural_orientation_matches_video, then 0 and 180 match video, otherwise
|
| + // 90 and 270 match video.
|
| + bool device_orientation_matches_video =
|
| + (device_orientation_angle90 % 180) ==
|
| + (natural_orientation_matches_video ? 0 : 90);
|
| +
|
| + if (!device_orientation_matches_video)
|
| + return;
|
| +
|
| + // Job done: the user rotated their device to match the orientation of the
|
| + // video that we locked to, so now we can unlock (and stop listening).
|
| + MaybeUnlockOrientation();
|
| +}
|
| +
|
| DEFINE_TRACE(MediaControlsOrientationLockDelegate) {
|
| EventListener::Trace(visitor);
|
| visitor->Trace(video_element_);
|
|
|