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..271816424108da281777b788021d5dde02e968c3 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsRotateToFullscreenDelegate.cpp |
| @@ -0,0 +1,232 @@ |
| +// 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. |
| +const unsigned kMinSize = 200; |
| + |
| +// At least this fraction of the video must be visible. |
| +const float kVisibilityThreshold = 0.75; |
| + |
| +} // anonymous namespace |
| + |
| +MediaControlsRotateToFullscreenDelegate:: |
| + MediaControlsRotateToFullscreenDelegate(HTMLVideoElement& video) |
| + : EventListener(kCPPEventListenerType), video_element_(video) {} |
|
mlamouri (slow - plz ping)
2017/04/19 17:10:46
I guess removing Attach() above was intended then?
johnme
2017/04/19 18:21:28
Yes, see https://crbug.com/713275 as above. I kept
|
| + |
| +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_); |
| + |
| + 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: Does orientationchange get delivered to background tabs? Perhaps we |
| + // should also check for at least page visibility before exiting? |
|
mlamouri (slow - plz ping)
2017/04/19 17:10:46
No, it's not. A background tab doesn't receive eve
johnme
2017/04/19 18:21:28
Good point. Changed TODO to:
// TODO(johnme): If
|
| + if (!video_element_->IsFullscreen() && |
| + (!is_visible_ || video_element_->paused())) { |
| + return; |
| + } |
| + |
| + // 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_) |
| + 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 |