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 |