Chromium Code Reviews| Index: third_party/WebKit/Source/modules/media_controls/MediaControlsRotateToFullscreenDelegate.cpp |
| diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsRotateToFullscreenDelegate.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsRotateToFullscreenDelegate.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..617adba7e35be21f651552a71fcae300e0831da1 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsRotateToFullscreenDelegate.cpp |
| @@ -0,0 +1,233 @@ |
| +// 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 "modules/media_controls/MediaControlsRotateToFullscreenDelegate.h" |
| + |
| +#include "core/dom/DocumentUserGestureToken.h" |
| +#include "core/dom/ElementVisibilityObserver.h" |
| +#include "core/events/Event.h" |
| +#include "core/frame/LocalDOMWindow.h" |
| +#include "core/html/HTMLVideoElement.h" |
| +#include "core/page/ChromeClient.h" |
| +#include "modules/media_controls/MediaControlsImpl.h" |
| +#include "platform/UserGestureIndicator.h" |
| +#include "public/platform/WebScreenInfo.h" |
| + |
| +namespace blink { |
| + |
| +namespace { |
| + |
| +// Videos must be at least this big in both dimensions to qualify. |
| +constexpr unsigned kMinSize = 200; |
| + |
| +// At least this fraction of the video must be visible. |
| +constexpr float kVisibilityThreshold = 0.75; |
| + |
| +} // anonymous namespace |
| + |
| +MediaControlsRotateToFullscreenDelegate:: |
| + MediaControlsRotateToFullscreenDelegate(HTMLVideoElement& video) |
| + : EventListener(kCPPEventListenerType), video_element_(video) {} |
| + |
| +void MediaControlsRotateToFullscreenDelegate::Attach() { |
| + DCHECK(video_element_->isConnected()); |
| + |
| + LocalDOMWindow* dom_window = video_element_->GetDocument().domWindow(); |
| + if (!dom_window) |
| + return; |
| + |
| + video_element_->addEventListener(EventTypeNames::play, this, true); |
| + video_element_->addEventListener(EventTypeNames::pause, this, true); |
| + |
| + // Listen to two different fullscreen events in order to make sure the new and |
| + // old APIs are handled. |
| + video_element_->addEventListener(EventTypeNames::webkitfullscreenchange, this, |
| + true); |
| + video_element_->GetDocument().addEventListener( |
| + EventTypeNames::fullscreenchange, this, true); |
| + |
| + current_screen_orientation_ = ComputeScreenOrientation(); |
| + // TODO(johnme): Check this is battery efficient (note that this doesn't need |
| + // to receive events for 180 deg rotations). |
| + dom_window->addEventListener(EventTypeNames::orientationchange, this, false); |
| +} |
| + |
| +void MediaControlsRotateToFullscreenDelegate::Detach() { |
| + DCHECK(!video_element_->isConnected()); |
| + |
| + if (visibility_observer_) { |
| + // TODO(johnme): Should I also call Stop in a prefinalizer? |
| + visibility_observer_->Stop(); |
| + visibility_observer_ = nullptr; |
| + is_visible_ = false; |
| + } |
| + |
| + video_element_->removeEventListener(EventTypeNames::play, this, true); |
| + video_element_->removeEventListener(EventTypeNames::pause, this, true); |
| + |
| + video_element_->removeEventListener(EventTypeNames::webkitfullscreenchange, |
| + this, true); |
| + video_element_->GetDocument().removeEventListener( |
| + EventTypeNames::fullscreenchange, this, true); |
| + |
| + LocalDOMWindow* dom_window = video_element_->GetDocument().domWindow(); |
| + if (!dom_window) |
| + return; |
| + dom_window->removeEventListener(EventTypeNames::orientationchange, this, |
| + false); |
| +} |
| + |
| +bool MediaControlsRotateToFullscreenDelegate::operator==( |
| + const EventListener& other) const { |
| + return this == &other; |
| +} |
| + |
| +void MediaControlsRotateToFullscreenDelegate::handleEvent( |
| + ExecutionContext* execution_context, |
| + Event* event) { |
| + // LOG(INFO) << __PRETTY_FUNCTION__ << " type=" << event->type() |
| + // << " visibility_observer_=" << visibility_observer_ |
| + // << " current_screen_orientation_=" |
| + // << static_cast<int>(current_screen_orientation_); |
|
mlamouri (slow - plz ping)
2017/04/20 14:10:40
Will have to be cleaned-up same for the uncommente
kinuko
2017/04/25 06:30:58
Yeah, please clean up these before landing.
mlamouri (slow - plz ping)
2017/04/25 10:57:30
This is johnme@'s CL. I'm going to send a follow-u
johnme
2017/04/27 15:57:48
Removed this commented out LOG(INFO). Converted th
|
| + |
| + if (event->type() == EventTypeNames::play || |
| + event->type() == EventTypeNames::pause || |
| + event->type() == EventTypeNames::fullscreenchange || |
| + event->type() == EventTypeNames::webkitfullscreenchange) { |
| + OnStateChange(); |
| + return; |
| + } |
| + if (event->type() == EventTypeNames::orientationchange) { |
| + OnScreenOrientationChange(); |
| + return; |
| + } |
| + |
| + NOTREACHED(); |
| +} |
| + |
| +void MediaControlsRotateToFullscreenDelegate::OnStateChange() { |
| + // TODO(johnme): Check this aggressive disabling doesn't lead to race |
| + // conditions where we briefly don't know if the video is visible. |
| + bool needs_visibility_observer = |
| + !video_element_->paused() && !video_element_->IsFullscreen(); |
| + LOG(INFO) << __FUNCTION__ << " " << !!visibility_observer_ << " -> " |
| + << needs_visibility_observer; |
| + if (needs_visibility_observer && !visibility_observer_) { |
| + visibility_observer_ = new ElementVisibilityObserver( |
| + video_element_, |
| + WTF::Bind(&MediaControlsRotateToFullscreenDelegate::OnVisibilityChange, |
| + WrapWeakPersistent(this))); |
| + visibility_observer_->Start(kVisibilityThreshold); |
| + } else if (!needs_visibility_observer && visibility_observer_) { |
| + visibility_observer_->Stop(); |
| + visibility_observer_ = nullptr; |
| + is_visible_ = false; |
| + } |
| +} |
| + |
| +void MediaControlsRotateToFullscreenDelegate::OnVisibilityChange( |
| + bool is_visible) { |
| + LOG(INFO) << __FUNCTION__ << " " << is_visible_ << " -> " << is_visible; |
| + is_visible_ = is_visible; |
| +} |
| + |
| +void MediaControlsRotateToFullscreenDelegate::OnScreenOrientationChange() { |
| + SimpleOrientation previous_screen_orientation = current_screen_orientation_; |
| + current_screen_orientation_ = ComputeScreenOrientation(); |
| + LOG(INFO) << __FUNCTION__ << " " |
| + << static_cast<int>(previous_screen_orientation) << " -> " |
| + << static_cast<int>(current_screen_orientation_); |
| + |
| + // Only enable if native media controls are used. |
| + if (!video_element_->ShouldShowControls()) |
| + return; |
| + |
| + // To enter fullscreen, video must be visible and playing. |
| + // TODO(johnme): If orientation changes whilst this tab is in the background, |
| + // we'll get an orientationchange event when this tab next becomes active. |
| + // Check that those events don't trigger rotate-to-fullscreen. |
| + if (!video_element_->IsFullscreen() && |
| + (!is_visible_ || video_element_->paused())) { |
| + return; |
| + } |
|
mlamouri (slow - plz ping)
2017/04/20 14:10:40
TODO: we should also not do this if the document i
johnme
2017/04/27 15:57:48
Fixed, and added tests EnterFailDocumentFullscreen
|
| + |
| + // Ignore (unexpected) events where we have incomplete information. |
| + if (previous_screen_orientation == SimpleOrientation::kUnknown || |
| + current_screen_orientation_ == SimpleOrientation::kUnknown) { |
| + return; |
| + } |
| + |
| + // Ignore 180 degree rotations between PortraitPrimary and PortraitSecondary, |
| + // or between LandscapePrimary and LandscapeSecondary. |
| + if (previous_screen_orientation == current_screen_orientation_) |
|
mlamouri (slow - plz ping)
2017/04/20 14:10:40
I'm a bit worried about not doing anything for squ
johnme
2017/04/27 15:57:48
Good point. I've changed ComputeVideoOrientation()
|
| + return; |
| + |
| + SimpleOrientation video_orientation = ComputeVideoOrientation(); |
| + |
| + // Ignore videos that are square/small/etc. |
| + if (video_orientation == SimpleOrientation::kUnknown) |
| + return; |
| + |
| + MediaControlsImpl& media_controls = |
| + *static_cast<MediaControlsImpl*>(video_element_->GetMediaControls()); |
| + |
| + { |
| + UserGestureIndicator gesture( |
| + DocumentUserGestureToken::Create(&video_element_->GetDocument())); |
| + |
| + bool should_be_fullscreen = |
| + current_screen_orientation_ == video_orientation; |
| + if (should_be_fullscreen && !video_element_->IsFullscreen()) |
| + media_controls.EnterFullscreen(); |
| + else if (!should_be_fullscreen && video_element_->IsFullscreen()) |
| + media_controls.ExitFullscreen(); |
| + } |
| +} |
| + |
| +MediaControlsRotateToFullscreenDelegate::SimpleOrientation |
| +MediaControlsRotateToFullscreenDelegate::ComputeVideoOrientation() const { |
| + if (video_element_->getReadyState() == HTMLMediaElement::kHaveNothing) |
| + return SimpleOrientation::kUnknown; |
| + |
| + const unsigned width = video_element_->videoWidth(); |
| + const unsigned height = video_element_->videoHeight(); |
| + |
| + if (width < kMinSize || height < kMinSize) |
| + return SimpleOrientation::kUnknown; // Too small, ignore this video. |
| + |
| + if (width > height) |
| + return SimpleOrientation::kLandscape; |
| + if (height > width) |
| + return SimpleOrientation::kPortrait; |
| + return SimpleOrientation::kUnknown; // Square. |
| +} |
| + |
| +MediaControlsRotateToFullscreenDelegate::SimpleOrientation |
| +MediaControlsRotateToFullscreenDelegate::ComputeScreenOrientation() const { |
| + Frame* frame = video_element_->GetDocument().GetFrame(); |
| + if (!frame) |
| + return SimpleOrientation::kUnknown; |
| + |
| + switch (frame->GetChromeClient().GetScreenInfo().orientation_type) { |
| + case kWebScreenOrientationPortraitPrimary: |
| + case kWebScreenOrientationPortraitSecondary: |
| + return SimpleOrientation::kPortrait; |
| + case kWebScreenOrientationLandscapePrimary: |
| + case kWebScreenOrientationLandscapeSecondary: |
| + return SimpleOrientation::kLandscape; |
| + case kWebScreenOrientationUndefined: |
| + return SimpleOrientation::kUnknown; |
| + } |
| + |
| + NOTREACHED(); |
| + return SimpleOrientation::kUnknown; |
| +} |
| + |
| +DEFINE_TRACE(MediaControlsRotateToFullscreenDelegate) { |
| + EventListener::Trace(visitor); |
| + visitor->Trace(video_element_); |
| + visitor->Trace(visibility_observer_); |
| +} |
| + |
| +} // namespace blink |