blob: 2e930dcef556e344ef2ffa75b874c027ab840148 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/media_controls/media_controls_orientation_lock_delegate.h"
#include <memory>
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
#include "services/device/public/mojom/screen_orientation.mojom-blink.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_size.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
#include "third_party/blink/renderer/core/frame/frame_view.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/screen_orientation_controller.h"
#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
#include "third_party/blink/renderer/core/html/media/html_audio_element.h"
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/loader/empty_clients.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/modules/device_orientation/device_orientation_controller.h"
#include "third_party/blink/renderer/modules/device_orientation/device_orientation_data.h"
#include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
#include "third_party/blink/renderer/modules/screen_orientation/screen_orientation_controller_impl.h"
#include "third_party/blink/renderer/modules/screen_orientation/web_lock_orientation_callback.h"
#include "third_party/blink/renderer/platform/geometry/int_rect.h"
#include "third_party/blink/renderer/platform/layout_test_support.h"
#include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
using testing::_;
using testing::AtLeast;
using testing::Return;
namespace blink {
namespace {
// WebLockOrientationCallback implementation that will not react to a success
// nor a failure.
class DummyScreenOrientationCallback final : public WebLockOrientationCallback {
public:
void OnSuccess() override {}
void OnError(WebLockOrientationError) override {}
};
class MockWebMediaPlayerForOrientationLockDelegate final
: public EmptyWebMediaPlayer {
public:
bool HasVideo() const override { return true; }
MOCK_CONST_METHOD0(NaturalSize, WebSize());
};
class MockScreenOrientation final
: public device::mojom::blink::ScreenOrientation {
public:
MockScreenOrientation() : binding_(this) {}
// device::mojom::blink::ScreenOrientation overrides:
void LockOrientation(WebScreenOrientationLockType type,
LockOrientationCallback callback) override {
std::move(callback).Run(device::mojom::ScreenOrientationLockResult::
SCREEN_ORIENTATION_LOCK_RESULT_SUCCESS);
LockOrientation(type);
}
void BindRequest(
device::mojom::blink::ScreenOrientationAssociatedRequest request) {
binding_.Bind(std::move(request));
}
void Close() { binding_.Close(); }
MOCK_METHOD0(UnlockOrientation, void());
MOCK_METHOD1(LockOrientation, void(WebScreenOrientationLockType));
private:
mojo::AssociatedBinding<device::mojom::blink::ScreenOrientation> binding_;
};
void DidEnterFullscreen(Document* document) {
DCHECK(document);
Fullscreen::DidEnterFullscreen(*document);
document->ServiceScriptedAnimations(WTF::CurrentTimeTicks());
}
void DidExitFullscreen(Document* document) {
DCHECK(document);
Fullscreen::DidExitFullscreen(*document);
document->ServiceScriptedAnimations(WTF::CurrentTimeTicks());
}
class MockChromeClientForOrientationLockDelegate final
: public EmptyChromeClient {
public:
// ChromeClient overrides:
void InstallSupplements(LocalFrame& frame) override {
EmptyChromeClient::InstallSupplements(frame);
ScreenOrientationControllerImpl::ProvideTo(frame);
device::mojom::blink::ScreenOrientationAssociatedPtr screen_orientation;
ScreenOrientationClient().BindRequest(
mojo::MakeRequestAssociatedWithDedicatedPipe(&screen_orientation));
ScreenOrientationControllerImpl::From(frame)
->SetScreenOrientationAssociatedPtrForTests(
std::move(screen_orientation));
}
// The real ChromeClient::EnterFullscreen/ExitFullscreen implementation is
// async due to IPC, emulate that by posting tasks:
void EnterFullscreen(LocalFrame& frame, const FullscreenOptions*) override {
Thread::Current()->GetTaskRunner()->PostTask(
FROM_HERE,
WTF::Bind(DidEnterFullscreen, WrapPersistent(frame.GetDocument())));
}
void ExitFullscreen(LocalFrame& frame) override {
Thread::Current()->GetTaskRunner()->PostTask(
FROM_HERE,
WTF::Bind(DidExitFullscreen, WrapPersistent(frame.GetDocument())));
}
MOCK_CONST_METHOD0(GetScreenInfo, WebScreenInfo());
MockScreenOrientation& ScreenOrientationClient() {
return mock_screen_orientation_;
}
private:
MockScreenOrientation mock_screen_orientation_;
};
class StubLocalFrameClientForOrientationLockDelegate final
: public EmptyLocalFrameClient {
public:
static StubLocalFrameClientForOrientationLockDelegate* Create() {
return new StubLocalFrameClientForOrientationLockDelegate;
}
std::unique_ptr<WebMediaPlayer> CreateWebMediaPlayer(
HTMLMediaElement&,
const WebMediaPlayerSource&,
WebMediaPlayerClient*,
WebLayerTreeView*) override {
return std::make_unique<MockWebMediaPlayerForOrientationLockDelegate>();
}
};
} // anonymous namespace
class MediaControlsOrientationLockDelegateTest
: public PageTestBase,
private ScopedVideoFullscreenOrientationLockForTest,
private ScopedVideoRotateToFullscreenForTest {
public:
MediaControlsOrientationLockDelegateTest()
: ScopedVideoFullscreenOrientationLockForTest(true),
ScopedVideoRotateToFullscreenForTest(false) {}
MediaControlsOrientationLockDelegateTest(bool video_rotate_to_fullscreen)
: ScopedVideoFullscreenOrientationLockForTest(true),
ScopedVideoRotateToFullscreenForTest(video_rotate_to_fullscreen) {}
protected:
using DeviceOrientationType =
MediaControlsOrientationLockDelegate::DeviceOrientationType;
static constexpr TimeDelta GetUnlockDelay() {
return MediaControlsOrientationLockDelegate::kLockToAnyDelay;
}
void SetUp() override {
chrome_client_ = new MockChromeClientForOrientationLockDelegate();
Page::PageClients clients;
FillWithEmptyClients(clients);
clients.chrome_client = chrome_client_.Get();
SetupPageWithClients(
&clients, StubLocalFrameClientForOrientationLockDelegate::Create());
previous_orientation_event_value_ =
RuntimeEnabledFeatures::OrientationEventEnabled();
GetDocument().write("<body><video></body>");
video_ = ToHTMLVideoElement(*GetDocument().QuerySelector("video"));
}
void TearDown() override {
testing::Mock::VerifyAndClear(&ScreenOrientationClient());
ScreenOrientationClient().Close();
}
static bool HasDelegate(const MediaControls& media_controls) {
return !!static_cast<const MediaControlsImpl*>(&media_controls)
->orientation_lock_delegate_;
}
void SimulateEnterFullscreen() {
std::unique_ptr<UserGestureIndicator> gesture =
LocalFrame::NotifyUserActivation(GetDocument().GetFrame());
Fullscreen::RequestFullscreen(Video());
test::RunPendingTasks();
}
void SimulateExitFullscreen() {
Fullscreen::ExitFullscreen(GetDocument());
test::RunPendingTasks();
}
void SimulateOrientationLock() {
ScreenOrientationController* controller =
ScreenOrientationController::From(*GetDocument().GetFrame());
controller->lock(kWebScreenOrientationLockLandscape,
std::make_unique<DummyScreenOrientationCallback>());
EXPECT_TRUE(controller->MaybeHasActiveLock());
}
void SimulateVideoReadyState(HTMLMediaElement::ReadyState state) {
Video().SetReadyState(state);
}
void SimulateVideoNetworkState(HTMLMediaElement::NetworkState state) {
Video().SetNetworkState(state);
}
MediaControlsImpl* MediaControls() const {
return static_cast<MediaControlsImpl*>(video_->GetMediaControls());
}
void CheckStatePendingFullscreen() const {
EXPECT_EQ(MediaControlsOrientationLockDelegate::State::kPendingFullscreen,
MediaControls()->orientation_lock_delegate_->state_);
}
void CheckStatePendingMetadata() const {
EXPECT_EQ(MediaControlsOrientationLockDelegate::State::kPendingMetadata,
MediaControls()->orientation_lock_delegate_->state_);
}
void CheckStateMaybeLockedFullscreen() const {
EXPECT_EQ(
MediaControlsOrientationLockDelegate::State::kMaybeLockedFullscreen,
MediaControls()->orientation_lock_delegate_->state_);
}
bool DelegateWillUnlockFullscreen() const {
return DelegateOrientationLock() !=
kWebScreenOrientationLockDefault /* unlocked */;
}
WebScreenOrientationLockType DelegateOrientationLock() const {
return MediaControls()->orientation_lock_delegate_->locked_orientation_;
}
WebScreenOrientationLockType ComputeOrientationLock() const {
return MediaControls()
->orientation_lock_delegate_->ComputeOrientationLock();
}
MockChromeClientForOrientationLockDelegate& ChromeClient() const {
return *chrome_client_;
}
HTMLVideoElement& Video() const { return *video_; }
MockScreenOrientation& ScreenOrientationClient() const {
return ChromeClient().ScreenOrientationClient();
}
MockWebMediaPlayerForOrientationLockDelegate& MockWebMediaPlayer() const {
return *static_cast<MockWebMediaPlayerForOrientationLockDelegate*>(
Video().GetWebMediaPlayer());
}
private:
friend class MediaControlsOrientationLockAndRotateToFullscreenDelegateTest;
bool previous_orientation_event_value_;
Persistent<HTMLVideoElement> video_;
Persistent<MockChromeClientForOrientationLockDelegate> chrome_client_;
};
class MediaControlsOrientationLockAndRotateToFullscreenDelegateTest
: public MediaControlsOrientationLockDelegateTest,
private ScopedOrientationEventForTest {
public:
MediaControlsOrientationLockAndRotateToFullscreenDelegateTest()
: MediaControlsOrientationLockDelegateTest(true),
ScopedOrientationEventForTest(true) {}
protected:
enum DeviceNaturalOrientation { kNaturalIsPortrait, kNaturalIsLandscape };
void SetUp() override {
// Unset this to fix ScreenOrientationControllerImpl::ComputeOrientation.
// TODO(mlamouri): Refactor to avoid this (crbug.com/726817).
was_running_layout_test_ = LayoutTestSupport::IsRunningLayoutTest();
LayoutTestSupport::SetIsRunningLayoutTest(false);
MediaControlsOrientationLockDelegateTest::SetUp();
// Reset the <video> element now we've enabled the runtime feature.
video_->parentElement()->RemoveChild(video_);
video_ = HTMLVideoElement::Create(GetDocument());
video_->setAttribute(html_names::kControlsAttr, g_empty_atom);
// Most tests should call GetDocument().body()->AppendChild(&Video());
// This is not done automatically, so that tests control timing of `Attach`,
// which is important for MediaControlsRotateToFullscreenDelegate since
// that's when it reads the initial screen orientation.
}
void TearDown() override {
MediaControlsOrientationLockDelegateTest::TearDown();
LayoutTestSupport::SetIsRunningLayoutTest(was_running_layout_test_);
}
void SetIsAutoRotateEnabledByUser(bool enabled) {
MediaControls()
->orientation_lock_delegate_
->is_auto_rotate_enabled_by_user_override_for_testing_ = enabled;
}
WebRect ScreenRectFromAngle(uint16_t screen_orientation_angle) {
uint16_t portrait_angle_mod_180 = natural_orientation_is_portrait_ ? 0 : 90;
bool screen_rect_is_portrait =
screen_orientation_angle % 180 == portrait_angle_mod_180;
return screen_rect_is_portrait ? IntRect(0, 0, 1080, 1920)
: IntRect(0, 0, 1920, 1080);
}
void RotateDeviceTo(uint16_t new_device_orientation_angle) {
// Pick one of the many (beta,gamma) pairs that should map to each angle.
switch (new_device_orientation_angle) {
case 0:
RotateDeviceTo(90, 0);
break;
case 90:
RotateDeviceTo(0, -90);
break;
case 180:
RotateDeviceTo(-90, 0);
break;
case 270:
RotateDeviceTo(0, 90);
break;
default:
NOTREACHED();
break;
}
}
void RotateDeviceTo(double beta, double gamma) {
DeviceOrientationController::From(GetDocument())
.SetOverride(DeviceOrientationData::Create(0.0 /* alpha */, beta, gamma,
false /* absolute */));
test::RunPendingTasks();
}
// Calls must be wrapped in ASSERT_NO_FATAL_FAILURE.
void RotateScreenTo(WebScreenOrientationType screen_orientation_type,
uint16_t screen_orientation_angle) {
WebScreenInfo screen_info;
screen_info.orientation_type = screen_orientation_type;
screen_info.orientation_angle = screen_orientation_angle;
screen_info.rect = ScreenRectFromAngle(screen_orientation_angle);
ASSERT_TRUE(screen_info.orientation_type ==
ScreenOrientationControllerImpl::ComputeOrientation(
screen_info.rect, screen_info.orientation_angle));
testing::Mock::VerifyAndClearExpectations(&ChromeClient());
EXPECT_CALL(ChromeClient(), GetScreenInfo())
.Times(AtLeast(1))
.WillRepeatedly(Return(screen_info));
// Screen Orientation API
ScreenOrientationController::From(*GetDocument().GetFrame())
->NotifyOrientationChanged();
// Legacy window.orientation API
GetDocument().domWindow()->SendOrientationChangeEvent();
test::RunPendingTasks();
}
void InitVideo(int video_width, int video_height) {
// Set up the WebMediaPlayer instance.
GetDocument().body()->AppendChild(&Video());
Video().SetSrc("https://example.com");
test::RunPendingTasks();
SimulateVideoReadyState(HTMLMediaElement::kHaveMetadata);
// Set video size.
EXPECT_CALL(MockWebMediaPlayer(), NaturalSize())
.WillRepeatedly(Return(WebSize(video_width, video_height)));
// Dispatch an arbitrary Device Orientation event to satisfy
// MediaControlsRotateToFullscreenDelegate's requirement that the device
// supports the API and can provide beta and gamma values. The orientation
// will be ignored.
RotateDeviceTo(0);
}
void PlayVideo() {
{
std::unique_ptr<UserGestureIndicator> gesture =
LocalFrame::NotifyUserActivation(GetDocument().GetFrame());
Video().Play();
}
test::RunPendingTasks();
}
void UpdateVisibilityObserver() {
// Let IntersectionObserver update.
GetDocument().View()->UpdateAllLifecyclePhases();
test::RunPendingTasks();
}
DeviceOrientationType ComputeDeviceOrientation(
DeviceOrientationData* data) const {
return MediaControls()
->orientation_lock_delegate_->ComputeDeviceOrientation(data);
}
bool was_running_layout_test_ = false;
bool natural_orientation_is_portrait_ = true;
};
TEST_F(MediaControlsOrientationLockDelegateTest, DelegateRequiresFlag) {
// Flag on by default.
EXPECT_TRUE(HasDelegate(*Video().GetMediaControls()));
// Same with flag off.
ScopedVideoFullscreenOrientationLockForTest video_fullscreen_orientation_lock(
false);
HTMLVideoElement* video = HTMLVideoElement::Create(GetDocument());
GetDocument().body()->AppendChild(video);
EXPECT_FALSE(HasDelegate(*video->GetMediaControls()));
}
TEST_F(MediaControlsOrientationLockDelegateTest, DelegateRequiresVideo) {
HTMLAudioElement* audio = HTMLAudioElement::Create(GetDocument());
GetDocument().body()->AppendChild(audio);
EXPECT_FALSE(HasDelegate(*audio->GetMediaControls()));
}
TEST_F(MediaControlsOrientationLockDelegateTest, InitialState) {
CheckStatePendingFullscreen();
}
TEST_F(MediaControlsOrientationLockDelegateTest, EnterFullscreenNoMetadata) {
EXPECT_CALL(ScreenOrientationClient(), LockOrientation(_)).Times(0);
SimulateEnterFullscreen();
CheckStatePendingMetadata();
}
TEST_F(MediaControlsOrientationLockDelegateTest, LeaveFullscreenNoMetadata) {
EXPECT_CALL(ScreenOrientationClient(), LockOrientation(_)).Times(0);
EXPECT_CALL(ScreenOrientationClient(), UnlockOrientation()).Times(0);
SimulateEnterFullscreen();
// State set to PendingMetadata.
SimulateExitFullscreen();
CheckStatePendingFullscreen();
}
TEST_F(MediaControlsOrientationLockDelegateTest, EnterFullscreenWithMetadata) {
SimulateVideoReadyState(HTMLMediaElement::kHaveMetadata);
EXPECT_CALL(ScreenOrientationClient(), LockOrientation(_)).Times(1);
EXPECT_FALSE(DelegateWillUnlockFullscreen());
SimulateEnterFullscreen();
EXPECT_TRUE(DelegateWillUnlockFullscreen());
CheckStateMaybeLockedFullscreen();
}
TEST_F(MediaControlsOrientationLockDelegateTest, LeaveFullscreenWithMetadata) {
SimulateVideoReadyState(HTMLMediaElement::kHaveMetadata);
EXPECT_CALL(ScreenOrientationClient(), LockOrientation(_)).Times(1);
EXPECT_CALL(ScreenOrientationClient(), UnlockOrientation()).Times(1);
SimulateEnterFullscreen();
// State set to MaybeLockedFullscreen.
SimulateExitFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
CheckStatePendingFullscreen();
}
TEST_F(MediaControlsOrientationLockDelegateTest, EnterFullscreenAfterPageLock) {
SimulateVideoReadyState(HTMLMediaElement::kHaveMetadata);
SimulateOrientationLock();
test::RunPendingTasks();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
EXPECT_CALL(ScreenOrientationClient(), LockOrientation(_)).Times(0);
SimulateEnterFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
CheckStateMaybeLockedFullscreen();
}
TEST_F(MediaControlsOrientationLockDelegateTest, LeaveFullscreenAfterPageLock) {
SimulateVideoReadyState(HTMLMediaElement::kHaveMetadata);
SimulateOrientationLock();
test::RunPendingTasks();
EXPECT_CALL(ScreenOrientationClient(), LockOrientation(_)).Times(0);
EXPECT_CALL(ScreenOrientationClient(), UnlockOrientation()).Times(0);
SimulateEnterFullscreen();
// State set to MaybeLockedFullscreen.
SimulateExitFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
CheckStatePendingFullscreen();
}
TEST_F(MediaControlsOrientationLockDelegateTest,
ReceivedMetadataAfterExitingFullscreen) {
EXPECT_CALL(ScreenOrientationClient(), LockOrientation(_)).Times(1);
SimulateEnterFullscreen();
// State set to PendingMetadata.
// Set up the WebMediaPlayer instance.
Video().SetSrc("http://example.com");
test::RunPendingTasks();
SimulateVideoNetworkState(HTMLMediaElement::kNetworkIdle);
SimulateVideoReadyState(HTMLMediaElement::kHaveMetadata);
test::RunPendingTasks();
CheckStateMaybeLockedFullscreen();
}
TEST_F(MediaControlsOrientationLockDelegateTest, ReceivedMetadataLater) {
EXPECT_CALL(ScreenOrientationClient(), LockOrientation(_)).Times(0);
EXPECT_CALL(ScreenOrientationClient(), UnlockOrientation()).Times(0);
SimulateEnterFullscreen();
// State set to PendingMetadata.
SimulateExitFullscreen();
// Set up the WebMediaPlayer instance.
Video().SetSrc("http://example.com");
test::RunPendingTasks();
SimulateVideoNetworkState(HTMLMediaElement::kNetworkIdle);
SimulateVideoReadyState(HTMLMediaElement::kHaveMetadata);
test::RunPendingTasks();
CheckStatePendingFullscreen();
}
TEST_F(MediaControlsOrientationLockDelegateTest, ComputeOrientationLock) {
// Set up the WebMediaPlayer instance.
Video().SetSrc("http://example.com");
test::RunPendingTasks();
SimulateVideoNetworkState(HTMLMediaElement::kNetworkIdle);
SimulateVideoReadyState(HTMLMediaElement::kHaveMetadata);
EXPECT_CALL(MockWebMediaPlayer(), NaturalSize())
.Times(14) // Each `computeOrientationLock` calls the method twice.
.WillOnce(Return(WebSize(100, 50)))
.WillOnce(Return(WebSize(100, 50)))
.WillOnce(Return(WebSize(50, 100)))
.WillOnce(Return(WebSize(50, 100)))
.WillRepeatedly(Return(WebSize(100, 100)));
// 100x50
EXPECT_EQ(kWebScreenOrientationLockLandscape, ComputeOrientationLock());
// 50x100
EXPECT_EQ(kWebScreenOrientationLockPortrait, ComputeOrientationLock());
// 100x100 has more subtilities, it depends on the current screen orientation.
WebScreenInfo screen_info;
screen_info.orientation_type = kWebScreenOrientationUndefined;
EXPECT_CALL(ChromeClient(), GetScreenInfo())
.Times(1)
.WillOnce(Return(screen_info));
EXPECT_EQ(kWebScreenOrientationLockLandscape, ComputeOrientationLock());
screen_info.orientation_type = kWebScreenOrientationPortraitPrimary;
EXPECT_CALL(ChromeClient(), GetScreenInfo())
.Times(1)
.WillOnce(Return(screen_info));
EXPECT_EQ(kWebScreenOrientationLockPortrait, ComputeOrientationLock());
screen_info.orientation_type = kWebScreenOrientationPortraitPrimary;
EXPECT_CALL(ChromeClient(), GetScreenInfo())
.Times(1)
.WillOnce(Return(screen_info));
EXPECT_EQ(kWebScreenOrientationLockPortrait, ComputeOrientationLock());
screen_info.orientation_type = kWebScreenOrientationLandscapePrimary;
EXPECT_CALL(ChromeClient(), GetScreenInfo())
.Times(1)
.WillOnce(Return(screen_info));
EXPECT_EQ(kWebScreenOrientationLockLandscape, ComputeOrientationLock());
screen_info.orientation_type = kWebScreenOrientationLandscapeSecondary;
EXPECT_CALL(ChromeClient(), GetScreenInfo())
.Times(1)
.WillOnce(Return(screen_info));
EXPECT_EQ(kWebScreenOrientationLockLandscape, ComputeOrientationLock());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
ComputeDeviceOrientation) {
InitVideo(400, 400);
// Repeat test with natural_orientation_is_portrait_ = false then true.
for (int n_o_i_p = 0; n_o_i_p <= 1; n_o_i_p++) {
natural_orientation_is_portrait_ = static_cast<bool>(n_o_i_p);
SCOPED_TRACE(testing::Message() << "natural_orientation_is_portrait_="
<< natural_orientation_is_portrait_);
DeviceOrientationType natural_orientation =
natural_orientation_is_portrait_ ? DeviceOrientationType::kPortrait
: DeviceOrientationType::kLandscape;
DeviceOrientationType perpendicular_to_natural_orientation =
natural_orientation_is_portrait_ ? DeviceOrientationType::kLandscape
: DeviceOrientationType::kPortrait;
// There are four valid combinations of orientation type and orientation
// angle for a naturally portrait device, and they should all calculate the
// same device orientation (since this doesn't depend on the screen
// orientation, it only depends on whether the device is naturally portrait
// or naturally landscape). Similarly for a naturally landscape device.
for (int screen_angle = 0; screen_angle < 360; screen_angle += 90) {
SCOPED_TRACE(testing::Message() << "screen_angle=" << screen_angle);
WebScreenOrientationType screen_type = kWebScreenOrientationUndefined;
switch (screen_angle) {
case 0:
screen_type = natural_orientation_is_portrait_
? kWebScreenOrientationPortraitPrimary
: kWebScreenOrientationLandscapePrimary;
break;
case 90:
screen_type = natural_orientation_is_portrait_
? kWebScreenOrientationLandscapePrimary
: kWebScreenOrientationPortraitSecondary;
break;
case 180:
screen_type = natural_orientation_is_portrait_
? kWebScreenOrientationPortraitSecondary
: kWebScreenOrientationLandscapeSecondary;
break;
case 270:
screen_type = natural_orientation_is_portrait_
? kWebScreenOrientationLandscapeSecondary
: kWebScreenOrientationPortraitPrimary;
break;
}
ASSERT_NO_FATAL_FAILURE(RotateScreenTo(screen_type, screen_angle));
// Compass heading is irrelevant to this calculation.
double alpha = 0.0;
bool absolute = false;
// These beta and gamma values should all map to r < sin(24 degrees), so
// orientation == kFlat, irrespective of their device_orientation_angle.
EXPECT_EQ(DeviceOrientationType::kFlat, // face up
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 0. /* beta */, 0. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kFlat, // face down
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 180. /* beta */, 0. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kFlat, // face down
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -180. /* beta */, 0. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kFlat, // face up, angle=0
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 20. /* beta */, 0. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kFlat, // face up, angle=90
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 0. /* beta */, -20. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kFlat, // face up, angle=180
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -20. /* beta */, 0. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kFlat, // face up, angle=270
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 0. /* beta */, 20. /* gamma */, absolute)));
// These beta and gamma values should all map to r ~= 1 and
// device_orientation_angle % 90 ~= 45, hence orientation == kDiagonal.
EXPECT_EQ(DeviceOrientationType::kDiagonal, // angle=45
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 135. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kDiagonal, // angle=45
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 45. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kDiagonal, // angle=135
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -135. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kDiagonal, // angle=135
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -45. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kDiagonal, // angle=225
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -45. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kDiagonal, // angle=225
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -135. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kDiagonal, // angle=315
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 45. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(DeviceOrientationType::kDiagonal, // angle=315
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 135. /* beta */, -90. /* gamma */, absolute)));
// These beta and gamma values should all map to r ~= 1 and
// device_orientation_angle ~= 0, hence orientation == kPortrait.
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 90. /* beta */, 0. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 90. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 90. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 85. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 85. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 95. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 95. /* beta */, -90. /* gamma */, absolute)));
// These beta and gamma values should all map to r == 1 and
// device_orientation_angle == 90, hence orientation == kLandscape.
EXPECT_EQ(perpendicular_to_natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 0. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(perpendicular_to_natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 180. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(perpendicular_to_natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -180. /* beta */, 90. /* gamma */, absolute)));
// These beta and gamma values should all map to r ~= 1 and
// device_orientation_angle ~= 180, hence orientation == kPortrait.
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -90. /* beta */, 0. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -90. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -90. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -85. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -85. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -95. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -95. /* beta */, -90. /* gamma */, absolute)));
// These beta and gamma values should all map to r == 1 and
// device_orientation_angle == 270, hence orientation == kLandscape.
EXPECT_EQ(perpendicular_to_natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 0. /* beta */, 90. /* gamma */, absolute)));
EXPECT_EQ(perpendicular_to_natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, 180. /* beta */, -90. /* gamma */, absolute)));
EXPECT_EQ(perpendicular_to_natural_orientation,
ComputeDeviceOrientation(DeviceOrientationData::Create(
alpha, -180. /* beta */, -90. /* gamma */, absolute)));
}
}
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
PortraitInlineRotateToLandscapeFullscreen) {
// Naturally portrait device, initially portrait, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
PlayVideo();
UpdateVisibilityObserver();
// Initially inline, unlocked orientation.
ASSERT_FALSE(Video().IsFullscreen());
CheckStatePendingFullscreen();
ASSERT_FALSE(DelegateWillUnlockFullscreen());
// Simulate user rotating their device to landscape triggering a screen
// orientation change.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
// MediaControlsRotateToFullscreenDelegate should enter fullscreen, so
// MediaControlsOrientationLockDelegate should lock orientation to landscape
// (even though the screen is already landscape).
EXPECT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Device orientation events received by MediaControlsOrientationLockDelegate
// will confirm that the device is already landscape.
RotateDeviceTo(90 /* landscape primary */);
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsOrientationLockDelegate should lock to "any" orientation.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockAny, DelegateOrientationLock());
EXPECT_TRUE(DelegateWillUnlockFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
PortraitInlineButtonToPortraitLockedLandscapeFullscreen) {
// Naturally portrait device, initially portrait, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
// Initially inline, unlocked orientation.
ASSERT_FALSE(Video().IsFullscreen());
CheckStatePendingFullscreen();
ASSERT_FALSE(DelegateWillUnlockFullscreen());
// Simulate user clicking on media controls fullscreen button.
SimulateEnterFullscreen();
EXPECT_TRUE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should lock to landscape.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// This will trigger a screen orientation change to landscape.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
// Even though the device is still held in portrait.
RotateDeviceTo(0 /* portrait primary */);
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsOrientationLockDelegate should remain locked to landscape.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
PortraitLockedLandscapeFullscreenRotateToLandscapeFullscreen) {
// Naturally portrait device, initially portrait device orientation but locked
// to landscape screen orientation, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
// Initially fullscreen, locked orientation.
SimulateEnterFullscreen();
ASSERT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Simulate user rotating their device to landscape (matching the screen
// orientation lock).
RotateDeviceTo(90 /* landscape primary */);
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsOrientationLockDelegate should lock to "any" orientation.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockAny, DelegateOrientationLock());
EXPECT_TRUE(DelegateWillUnlockFullscreen());
EXPECT_TRUE(Video().IsFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
PortraitLockedLandscapeFullscreenBackToPortraitInline) {
// Naturally portrait device, initially portrait device orientation but locked
// to landscape screen orientation, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
// Initially fullscreen, locked orientation.
SimulateEnterFullscreen();
ASSERT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Simulate user clicking on media controls exit fullscreen button.
SimulateExitFullscreen();
EXPECT_FALSE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should unlock orientation.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
// Play the video and make it visible, just to make sure
// MediaControlsRotateToFullscreenDelegate doesn't react to the
// orientationchange event.
PlayVideo();
UpdateVisibilityObserver();
// Unlocking the orientation earlier will trigger a screen orientation change
// to portrait (since the device orientation was already portrait, even though
// the screen was locked to landscape).
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
// Video should remain inline, unlocked.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
EXPECT_FALSE(Video().IsFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
LandscapeInlineRotateToPortraitInline) {
// Naturally portrait device, initially landscape, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
PlayVideo();
UpdateVisibilityObserver();
// Initially inline, unlocked orientation.
ASSERT_FALSE(Video().IsFullscreen());
CheckStatePendingFullscreen();
ASSERT_FALSE(DelegateWillUnlockFullscreen());
// Simulate user rotating their device to portrait triggering a screen
// orientation change.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
test::RunDelayedTasks(GetUnlockDelay());
// Video should remain inline, unlocked.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
EXPECT_FALSE(Video().IsFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
LandscapeInlineButtonToLandscapeFullscreen) {
// Naturally portrait device, initially landscape, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
// Initially inline, unlocked orientation.
ASSERT_FALSE(Video().IsFullscreen());
CheckStatePendingFullscreen();
ASSERT_FALSE(DelegateWillUnlockFullscreen());
// Simulate user clicking on media controls fullscreen button.
SimulateEnterFullscreen();
EXPECT_TRUE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should lock to landscape (even though
// the screen is already landscape).
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Device orientation events received by MediaControlsOrientationLockDelegate
// will confirm that the device is already landscape.
RotateDeviceTo(90 /* landscape primary */);
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsOrientationLockDelegate should lock to "any" orientation.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockAny, DelegateOrientationLock());
EXPECT_TRUE(DelegateWillUnlockFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
LandscapeFullscreenRotateToPortraitInline) {
// Naturally portrait device, initially landscape, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
// Initially fullscreen, locked to "any" orientation.
SimulateEnterFullscreen();
RotateDeviceTo(90 /* landscape primary */);
test::RunDelayedTasks(GetUnlockDelay());
ASSERT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockAny, DelegateOrientationLock());
EXPECT_TRUE(DelegateWillUnlockFullscreen());
// Simulate user rotating their device to portrait triggering a screen
// orientation change.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsRotateToFullscreenDelegate should exit fullscreen.
EXPECT_FALSE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should unlock screen orientation.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
LandscapeFullscreenBackToLandscapeInline) {
// Naturally portrait device, initially landscape, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
// Initially fullscreen, locked to "any" orientation.
SimulateEnterFullscreen();
RotateDeviceTo(90 /* landscape primary */);
test::RunDelayedTasks(GetUnlockDelay());
ASSERT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockAny, DelegateOrientationLock());
EXPECT_TRUE(DelegateWillUnlockFullscreen());
// Simulate user clicking on media controls exit fullscreen button.
SimulateExitFullscreen();
EXPECT_FALSE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should unlock screen orientation.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
}
TEST_F(
MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
AutoRotateDisabledPortraitInlineButtonToPortraitLockedLandscapeFullscreen) {
// Naturally portrait device, initially portrait, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
InitVideo(640, 480);
// But this time the user has disabled auto rotate, e.g. locked to portrait.
SetIsAutoRotateEnabledByUser(false);
// Initially inline, unlocked orientation.
ASSERT_FALSE(Video().IsFullscreen());
CheckStatePendingFullscreen();
ASSERT_FALSE(DelegateWillUnlockFullscreen());
// Simulate user clicking on media controls fullscreen button.
SimulateEnterFullscreen();
EXPECT_TRUE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should lock to landscape.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// This will trigger a screen orientation change to landscape, since the app's
// lock overrides the user's orientation lock (at least on Android).
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
// Even though the device is still held in portrait.
RotateDeviceTo(0 /* portrait primary */);
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsOrientationLockDelegate should remain locked to landscape.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
}
TEST_F(
MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
AutoRotateDisabledPortraitLockedLandscapeFullscreenRotateToLandscapeLockedLandscapeFullscreen) {
// Naturally portrait device, initially portrait device orientation but locked
// to landscape screen orientation, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
// But this time the user has disabled auto rotate, e.g. locked to portrait
// (even though the app's landscape screen orientation lock overrides it).
SetIsAutoRotateEnabledByUser(false);
// Initially fullscreen, locked orientation.
SimulateEnterFullscreen();
ASSERT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Simulate user rotating their device to landscape (matching the screen
// orientation lock).
RotateDeviceTo(90 /* landscape primary */);
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsOrientationLockDelegate should remain locked to landscape even
// though the screen orientation is now landscape, since the user has disabled
// auto rotate, so unlocking now would cause the device to return to the
// portrait orientation.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
EXPECT_TRUE(Video().IsFullscreen());
}
TEST_F(
MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
AutoRotateDisabledPortraitLockedLandscapeFullscreenBackToPortraitInline) {
// Naturally portrait device, initially portrait device orientation but locked
// to landscape screen orientation, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
// But this time the user has disabled auto rotate, e.g. locked to portrait
// (even though the app's landscape screen orientation lock overrides it).
SetIsAutoRotateEnabledByUser(false);
// Initially fullscreen, locked orientation.
SimulateEnterFullscreen();
ASSERT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Simulate user clicking on media controls exit fullscreen button.
SimulateExitFullscreen();
EXPECT_FALSE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should unlock orientation.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
// Play the video and make it visible, just to make sure
// MediaControlsRotateToFullscreenDelegate doesn't react to the
// orientationchange event.
PlayVideo();
UpdateVisibilityObserver();
// Unlocking the orientation earlier will trigger a screen orientation change
// to portrait, since the user had locked the screen orientation to portrait,
// (which happens to also match the device orientation) and
// MediaControlsOrientationLockDelegate is no longer overriding that lock.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
// Video should remain inline, unlocked.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
EXPECT_FALSE(Video().IsFullscreen());
}
TEST_F(
MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
AutoRotateDisabledLandscapeLockedLandscapeFullscreenRotateToPortraitLockedLandscapeFullscreen) {
// Naturally portrait device, initially landscape device orientation yet also
// locked to landscape screen orientation since the user had disabled auto
// rotate, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
// The user has disabled auto rotate, e.g. locked to portrait (even though the
// app's landscape screen orientation lock overrides it).
SetIsAutoRotateEnabledByUser(false);
// Initially fullscreen, locked orientation.
SimulateEnterFullscreen();
ASSERT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Simulate user rotating their device to portrait (matching the user's
// rotation lock, but perpendicular to MediaControlsOrientationLockDelegate's
// screen orientation lock which overrides it).
RotateDeviceTo(0 /* portrait primary */);
test::RunDelayedTasks(GetUnlockDelay());
// Video should remain locked and fullscreen. This may disappoint users who
// expect MediaControlsRotateToFullscreenDelegate to let them always leave
// fullscreen by rotating perpendicular to the video's orientation (i.e.
// rotating to portrait for a landscape video), however in this specific case,
// since the user disabled auto rotate at the OS level, it's likely that they
// wish to be able to use their phone whilst their head is lying sideways on a
// pillow (or similar), in which case it's essential to keep the fullscreen
// orientation lock.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
EXPECT_TRUE(Video().IsFullscreen());
}
TEST_F(
MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
AutoRotateDisabledLandscapeLockedLandscapeFullscreenBackToPortraitInline) {
// Naturally portrait device, initially landscape device orientation yet also
// locked to landscape screen orientation since the user had disabled auto
// rotate, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(640, 480);
// The user has disabled auto rotate, e.g. locked to portrait (even though the
// app's landscape screen orientation lock overrides it).
SetIsAutoRotateEnabledByUser(false);
// Initially fullscreen, locked orientation.
SimulateEnterFullscreen();
ASSERT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Simulate user clicking on media controls exit fullscreen button.
SimulateExitFullscreen();
EXPECT_FALSE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should unlock orientation.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
// Play the video and make it visible, just to make sure
// MediaControlsRotateToFullscreenDelegate doesn't react to the
// orientationchange event.
PlayVideo();
UpdateVisibilityObserver();
// Unlocking the orientation earlier will trigger a screen orientation change
// to portrait even though the device orientation is landscape, since the user
// had locked the screen orientation to portrait, and
// MediaControlsOrientationLockDelegate is no longer overriding that.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
// Video should remain inline, unlocked.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
EXPECT_FALSE(Video().IsFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
PortraitVideoRotateEnterExit) {
// Naturally portrait device, initially landscape, with *portrait* video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
InitVideo(480, 640);
SetIsAutoRotateEnabledByUser(true);
PlayVideo();
UpdateVisibilityObserver();
// Initially inline, unlocked orientation.
ASSERT_FALSE(Video().IsFullscreen());
CheckStatePendingFullscreen();
ASSERT_FALSE(DelegateWillUnlockFullscreen());
// Simulate user rotating their device to portrait triggering a screen
// orientation change.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
// MediaControlsRotateToFullscreenDelegate should enter fullscreen, so
// MediaControlsOrientationLockDelegate should lock orientation to portrait
// (even though the screen is already portrait).
EXPECT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockPortrait, DelegateOrientationLock());
// Device orientation events received by MediaControlsOrientationLockDelegate
// will confirm that the device is already portrait.
RotateDeviceTo(0 /* portrait primary */);
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsOrientationLockDelegate should lock to "any" orientation.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockAny, DelegateOrientationLock());
EXPECT_TRUE(DelegateWillUnlockFullscreen());
EXPECT_TRUE(Video().IsFullscreen());
// Simulate user rotating their device to landscape triggering a screen
// orientation change.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
// MediaControlsRotateToFullscreenDelegate should exit fullscreen.
EXPECT_FALSE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should unlock screen orientation.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
LandscapeDeviceRotateEnterExit) {
// Naturally *landscape* device, initially portrait, with landscape video.
natural_orientation_is_portrait_ = false;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 270));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
PlayVideo();
UpdateVisibilityObserver();
// Initially inline, unlocked orientation.
ASSERT_FALSE(Video().IsFullscreen());
CheckStatePendingFullscreen();
ASSERT_FALSE(DelegateWillUnlockFullscreen());
// Simulate user rotating their device to landscape triggering a screen
// orientation change.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 0));
// MediaControlsRotateToFullscreenDelegate should enter fullscreen, so
// MediaControlsOrientationLockDelegate should lock orientation to landscape
// (even though the screen is already landscape).
EXPECT_TRUE(Video().IsFullscreen());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Device orientation events received by MediaControlsOrientationLockDelegate
// will confirm that the device is already landscape.
RotateDeviceTo(0 /* landscape primary */);
test::RunDelayedTasks(GetUnlockDelay());
// MediaControlsOrientationLockDelegate should lock to "any" orientation.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockAny, DelegateOrientationLock());
EXPECT_TRUE(DelegateWillUnlockFullscreen());
EXPECT_TRUE(Video().IsFullscreen());
// Simulate user rotating their device to portrait triggering a screen
// orientation change.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 270));
// MediaControlsRotateToFullscreenDelegate should exit fullscreen.
EXPECT_FALSE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should unlock screen orientation.
CheckStatePendingFullscreen();
EXPECT_FALSE(DelegateWillUnlockFullscreen());
}
TEST_F(MediaControlsOrientationLockAndRotateToFullscreenDelegateTest,
ScreenOrientationRaceCondition) {
// Naturally portrait device, initially portrait, with landscape video.
natural_orientation_is_portrait_ = true;
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationPortraitPrimary, 0));
InitVideo(640, 480);
SetIsAutoRotateEnabledByUser(true);
// Initially inline, unlocked orientation.
ASSERT_FALSE(Video().IsFullscreen());
CheckStatePendingFullscreen();
ASSERT_FALSE(DelegateWillUnlockFullscreen());
// Simulate user clicking on media controls fullscreen button.
SimulateEnterFullscreen();
EXPECT_TRUE(Video().IsFullscreen());
// MediaControlsOrientationLockDelegate should lock to landscape.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// This will trigger a screen orientation change to landscape.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapePrimary, 90));
// Even though the device is still held in portrait.
RotateDeviceTo(0 /* portrait primary */);
// MediaControlsOrientationLockDelegate should remain locked to landscape
// indefinitely.
test::RunDelayedTasks(GetUnlockDelay());
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Now suppose the user actually rotates from portrait-primary to landscape-
// secondary, despite the screen currently being landscape-primary.
RotateDeviceTo(270 /* landscape secondary */);
// There can be a significant delay, between the device orientation changing
// and the OS updating the screen orientation to match the new device
// orientation. Manual testing showed that it could take longer than 200ms,
// but less than 250ms, on common Android devices. Partly this is because OSes
// often low-pass filter the device orientation to ignore high frequency
// noise.
//
// During this period, MediaControlsOrientationLockDelegate should
// remain locked to landscape. This prevents a race condition where the
// delegate unlocks the screen orientation, so Android changes the screen
// orientation back to portrait because it hasn't yet processed the device
// orientation change to landscape.
constexpr TimeDelta kMinUnlockDelay = TimeDelta::FromMilliseconds(249);
static_assert(GetUnlockDelay() > kMinUnlockDelay,
"GetUnlockDelay() should significantly exceed kMinUnlockDelay");
test::RunDelayedTasks(kMinUnlockDelay);
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Simulate the OS processing the device orientation change after a delay of
// `kMinUnlockDelay` and hence changing the screen orientation.
ASSERT_NO_FATAL_FAILURE(
RotateScreenTo(kWebScreenOrientationLandscapeSecondary, 270));
// MediaControlsOrientationLockDelegate should remain locked to landscape.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockLandscape, DelegateOrientationLock());
// Wait for the rest of the unlock delay.
test::RunDelayedTasks(GetUnlockDelay() - kMinUnlockDelay);
// MediaControlsOrientationLockDelegate should've locked to "any" orientation.
CheckStateMaybeLockedFullscreen();
EXPECT_EQ(kWebScreenOrientationLockAny, DelegateOrientationLock());
EXPECT_TRUE(DelegateWillUnlockFullscreen());
}
} // namespace blink