blob: 37e14f2ee31b9d444414f8429aae8dcaabb04ed1 [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.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_MEDIA_CONTROLS_ORIENTATION_LOCK_DELEGATE_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_MEDIA_CONTROLS_ORIENTATION_LOCK_DELEGATE_H_
#include "base/optional.h"
#include "services/device/public/mojom/screen_orientation.mojom-blink.h"
#include "third_party/blink/public/common/screen_orientation/web_screen_orientation_lock_type.h"
#include "third_party/blink/renderer/core/dom/events/event_listener.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
#include "third_party/blink/renderer/platform/wtf/time.h"
namespace blink {
class DeviceOrientationData;
class DeviceOrientationEvent;
class Document;
class HTMLVideoElement;
// MediaControlsOrientationLockDelegate is implementing the orientation lock
// feature when a <video> is fullscreen. It is meant to be created by
// `MediaControlsImpl` when the feature applies. Once created, it will use
// events to change state.
//
// The behavior depends on whether MediaControlsRotateToFullscreenDelegate is
// enabled. If it is enabled and the user has not locked the screen orientation
// at the OS level, then the orientation lock is only held until the user
// rotates their device to match the orientation of the video; otherwise it is
// held until fullscreen is exited.
//
// The different states of the class are:
// - PendingFullscreen: the object is created and it is either waiting for the
// associated <video> to go fullscreen in order to apply an orientation lock,
// or it already went fullscreen then the lock was unlocked since the user
// rotated their device, and now it is waiting until fullscreen is re-entered;
// - PendingMetadata: the <video> is fullscreen but the metadata have not been
// downloaded yet. It can happen because of network latency or because the
// <video> went fullscreen before playback and download started;
// - MaybeLockedFullscreen: the <video> is fullscreen and a screen orientation
// lock is applied.
//
// The possible state transitions are:
// - PendingFullscreen => PendingMetadata: on fullscreenchange event (entering
// fullscreen) when metadata are not available;
// - PendingFullscreen => MaybeLockedFullscreen: on fullscreenchange event
// (entering fullscreen) when metadata are available;
// - PendingMetadata => MaybeLockedFullscreen: on loadedmetadata;
// - PendingMetadata => PendingFullscreen: on fullscreenchange event (exiting
// fullscreen);
// - MaybeLockedFullscreen => PendingFullscreen: on fullscreenchange event
// (exiting fullscreen) or on deviceorientation event (rotated to match the
// orientation of the video).
class MediaControlsOrientationLockDelegate final : public EventListener {
public:
explicit MediaControlsOrientationLockDelegate(HTMLVideoElement&);
// Called by MediaControlsImpl when the HTMLMediaElement is added to a
// document. All event listeners should be added.
void Attach();
// Called by MediaControlsImpl when the HTMLMediaElement is no longer in the
// document. All event listeners should be removed in order to prepare the
// object to be garbage collected.
void Detach();
// EventListener implementation.
bool operator==(const EventListener&) const override;
void Trace(blink::Visitor*) override;
private:
friend class MediaControlsOrientationLockDelegateTest;
friend class MediaControlsOrientationLockAndRotateToFullscreenDelegateTest;
enum class State {
kPendingFullscreen,
kPendingMetadata,
kMaybeLockedFullscreen,
};
enum class DeviceOrientationType {
kUnknown,
kFlat,
kDiagonal,
kPortrait,
kLandscape
};
// EventListener implementation.
void Invoke(ExecutionContext*, Event*) override;
HTMLVideoElement& VideoElement() const;
Document& GetDocument() const;
// Returns the orientation in which the video should be locked based on its
// size.
MODULES_EXPORT WebScreenOrientationLockType ComputeOrientationLock() const;
// Locks the screen orientation if the video has metadata information
// available. Delays locking orientation until metadata are available
// otherwise.
void MaybeLockOrientation();
// Changes a previously locked screen orientation to instead be locked to
// the "any" orientation that allows accelerometer-based rotation. This is
// not the same as unlocking (which returns to the "default" orientation,
// which may in fact be more restrictive).
void ChangeLockToAnyOrientation();
// Unlocks the screen orientation if the screen orientation was previously
// locked.
void MaybeUnlockOrientation();
void MaybeListenToDeviceOrientation();
void GotIsAutoRotateEnabledByUser(bool enabled);
MODULES_EXPORT DeviceOrientationType
ComputeDeviceOrientation(DeviceOrientationData*) const;
void MaybeLockToAnyIfDeviceOrientationMatchesVideo(DeviceOrientationEvent*);
// Delay before `MaybeLockToAnyIfDeviceOrientationMatchesVideo` changes lock.
// Emprically, 200ms is too short, but 250ms avoids glitches. 500ms gives us
// a 2x margin in case the device is running slow, without being noticeable.
MODULES_EXPORT static constexpr TimeDelta kLockToAnyDelay =
TimeDelta::FromMilliseconds(500);
// Current state of the object. See comment at the top of the file for a
// detailed description.
State state_ = State::kPendingFullscreen;
// Which lock is currently applied by this delegate.
WebScreenOrientationLockType locked_orientation_ =
kWebScreenOrientationLockDefault /* unlocked */;
TaskHandle lock_to_any_task_;
device::mojom::blink::ScreenOrientationListenerPtr monitor_;
base::Optional<bool> is_auto_rotate_enabled_by_user_override_for_testing_;
// `video_element_` owns MediaControlsImpl that owns |this|.
Member<HTMLVideoElement> video_element_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_MEDIA_CONTROLS_ORIENTATION_LOCK_DELEGATE_H_