OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "modules/media_controls/MediaControlsRotateToFullscreenDelegate.h" | |
6 | |
7 #include "core/dom/DocumentUserGestureToken.h" | |
8 #include "core/dom/ElementVisibilityObserver.h" | |
9 #include "core/events/Event.h" | |
10 #include "core/frame/LocalDOMWindow.h" | |
11 #include "core/html/HTMLVideoElement.h" | |
12 #include "core/page/ChromeClient.h" | |
13 #include "modules/media_controls/MediaControlsImpl.h" | |
14 #include "platform/UserGestureIndicator.h" | |
15 #include "public/platform/WebScreenInfo.h" | |
16 | |
17 namespace blink { | |
18 | |
19 namespace { | |
20 | |
21 // Videos must be at least this big in both dimensions to qualify. | |
22 const unsigned kMinSize = 200; | |
23 | |
24 // At least this fraction of the video must be visible. | |
25 const float kVisibilityThreshold = 0.75; | |
26 | |
27 } // anonymous namespace | |
28 | |
29 MediaControlsRotateToFullscreenDelegate:: | |
30 MediaControlsRotateToFullscreenDelegate(HTMLVideoElement& video) | |
31 : 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
| |
32 | |
33 void MediaControlsRotateToFullscreenDelegate::Attach() { | |
34 DCHECK(video_element_->isConnected()); | |
35 | |
36 LocalDOMWindow* dom_window = video_element_->GetDocument().domWindow(); | |
37 if (!dom_window) | |
38 return; | |
39 | |
40 video_element_->addEventListener(EventTypeNames::play, this, true); | |
41 video_element_->addEventListener(EventTypeNames::pause, this, true); | |
42 | |
43 // Listen to two different fullscreen events in order to make sure the new and | |
44 // old APIs are handled. | |
45 video_element_->addEventListener(EventTypeNames::webkitfullscreenchange, this, | |
46 true); | |
47 video_element_->GetDocument().addEventListener( | |
48 EventTypeNames::fullscreenchange, this, true); | |
49 | |
50 current_screen_orientation_ = ComputeScreenOrientation(); | |
51 // TODO(johnme): Check this is battery efficient (note that this doesn't need | |
52 // to receive events for 180 deg rotations). | |
53 dom_window->addEventListener(EventTypeNames::orientationchange, this, false); | |
54 } | |
55 | |
56 void MediaControlsRotateToFullscreenDelegate::Detach() { | |
57 DCHECK(!video_element_->isConnected()); | |
58 | |
59 if (visibility_observer_) { | |
60 // TODO(johnme): Should I also call Stop in a prefinalizer? | |
61 visibility_observer_->Stop(); | |
62 visibility_observer_ = nullptr; | |
63 is_visible_ = false; | |
64 } | |
65 | |
66 video_element_->removeEventListener(EventTypeNames::play, this, true); | |
67 video_element_->removeEventListener(EventTypeNames::pause, this, true); | |
68 | |
69 video_element_->removeEventListener(EventTypeNames::webkitfullscreenchange, | |
70 this, true); | |
71 video_element_->GetDocument().removeEventListener( | |
72 EventTypeNames::fullscreenchange, this, true); | |
73 | |
74 LocalDOMWindow* dom_window = video_element_->GetDocument().domWindow(); | |
75 if (!dom_window) | |
76 return; | |
77 dom_window->removeEventListener(EventTypeNames::orientationchange, this, | |
78 false); | |
79 } | |
80 | |
81 bool MediaControlsRotateToFullscreenDelegate::operator==( | |
82 const EventListener& other) const { | |
83 return this == &other; | |
84 } | |
85 | |
86 void MediaControlsRotateToFullscreenDelegate::handleEvent( | |
87 ExecutionContext* execution_context, | |
88 Event* event) { | |
89 // LOG(INFO) << __PRETTY_FUNCTION__ << " type=" << event->type() | |
90 // << " visibility_observer_=" << visibility_observer_ | |
91 // << " current_screen_orientation_=" | |
92 // << static_cast<int>(current_screen_orientation_); | |
93 | |
94 if (event->type() == EventTypeNames::play || | |
95 event->type() == EventTypeNames::pause || | |
96 event->type() == EventTypeNames::fullscreenchange || | |
97 event->type() == EventTypeNames::webkitfullscreenchange) { | |
98 OnStateChange(); | |
99 return; | |
100 } | |
101 if (event->type() == EventTypeNames::orientationchange) { | |
102 OnScreenOrientationChange(); | |
103 return; | |
104 } | |
105 | |
106 NOTREACHED(); | |
107 } | |
108 | |
109 void MediaControlsRotateToFullscreenDelegate::OnStateChange() { | |
110 // TODO(johnme): Check this aggressive disabling doesn't lead to race | |
111 // conditions where we briefly don't know if the video is visible. | |
112 bool needs_visibility_observer = | |
113 !video_element_->paused() && !video_element_->IsFullscreen(); | |
114 LOG(INFO) << __FUNCTION__ << " " << !!visibility_observer_ << " -> " | |
115 << needs_visibility_observer; | |
116 if (needs_visibility_observer && !visibility_observer_) { | |
117 visibility_observer_ = new ElementVisibilityObserver( | |
118 video_element_, | |
119 WTF::Bind(&MediaControlsRotateToFullscreenDelegate::OnVisibilityChange, | |
120 WrapWeakPersistent(this))); | |
121 visibility_observer_->Start(kVisibilityThreshold); | |
122 } else if (!needs_visibility_observer && visibility_observer_) { | |
123 visibility_observer_->Stop(); | |
124 visibility_observer_ = nullptr; | |
125 is_visible_ = false; | |
126 } | |
127 } | |
128 | |
129 void MediaControlsRotateToFullscreenDelegate::OnVisibilityChange( | |
130 bool is_visible) { | |
131 LOG(INFO) << __FUNCTION__ << " " << is_visible_ << " -> " << is_visible; | |
132 is_visible_ = is_visible; | |
133 } | |
134 | |
135 void MediaControlsRotateToFullscreenDelegate::OnScreenOrientationChange() { | |
136 SimpleOrientation previous_screen_orientation = current_screen_orientation_; | |
137 current_screen_orientation_ = ComputeScreenOrientation(); | |
138 LOG(INFO) << __FUNCTION__ << " " | |
139 << static_cast<int>(previous_screen_orientation) << " -> " | |
140 << static_cast<int>(current_screen_orientation_); | |
141 | |
142 // Only enable if native media controls are used. | |
143 if (!video_element_->ShouldShowControls()) | |
144 return; | |
145 | |
146 // To enter fullscreen, video must be visible and playing. | |
147 // TODO: Does orientationchange get delivered to background tabs? Perhaps we | |
148 // 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
| |
149 if (!video_element_->IsFullscreen() && | |
150 (!is_visible_ || video_element_->paused())) { | |
151 return; | |
152 } | |
153 | |
154 // Ignore (unexpected) events where we have incomplete information. | |
155 if (previous_screen_orientation == SimpleOrientation::kUnknown || | |
156 current_screen_orientation_ == SimpleOrientation::kUnknown) { | |
157 return; | |
158 } | |
159 | |
160 // Ignore 180 degree rotations between PortraitPrimary and PortraitSecondary, | |
161 // or between LandscapePrimary and LandscapeSecondary. | |
162 if (previous_screen_orientation == current_screen_orientation_) | |
163 return; | |
164 | |
165 SimpleOrientation video_orientation = ComputeVideoOrientation(); | |
166 | |
167 // Ignore videos that are square/small/etc. | |
168 if (video_orientation == SimpleOrientation::kUnknown) | |
169 return; | |
170 | |
171 MediaControlsImpl& media_controls = | |
172 *static_cast<MediaControlsImpl*>(video_element_->GetMediaControls()); | |
173 | |
174 { | |
175 UserGestureIndicator gesture( | |
176 DocumentUserGestureToken::Create(&video_element_->GetDocument())); | |
177 | |
178 bool should_be_fullscreen = | |
179 current_screen_orientation_ == video_orientation; | |
180 if (should_be_fullscreen && !video_element_->IsFullscreen()) | |
181 media_controls.EnterFullscreen(); | |
182 else if (!should_be_fullscreen && video_element_->IsFullscreen()) | |
183 media_controls.ExitFullscreen(); | |
184 } | |
185 } | |
186 | |
187 MediaControlsRotateToFullscreenDelegate::SimpleOrientation | |
188 MediaControlsRotateToFullscreenDelegate::ComputeVideoOrientation() const { | |
189 if (video_element_->getReadyState() == HTMLMediaElement::kHaveNothing) | |
190 return SimpleOrientation::kUnknown; | |
191 | |
192 const unsigned width = video_element_->videoWidth(); | |
193 const unsigned height = video_element_->videoHeight(); | |
194 | |
195 if (width < kMinSize || height < kMinSize) | |
196 return SimpleOrientation::kUnknown; // Too small, ignore this video. | |
197 | |
198 if (width > height) | |
199 return SimpleOrientation::kLandscape; | |
200 if (height > width) | |
201 return SimpleOrientation::kPortrait; | |
202 return SimpleOrientation::kUnknown; // Square. | |
203 } | |
204 | |
205 MediaControlsRotateToFullscreenDelegate::SimpleOrientation | |
206 MediaControlsRotateToFullscreenDelegate::ComputeScreenOrientation() const { | |
207 Frame* frame = video_element_->GetDocument().GetFrame(); | |
208 if (!frame) | |
209 return SimpleOrientation::kUnknown; | |
210 | |
211 switch (frame->GetChromeClient().GetScreenInfo().orientation_type) { | |
212 case kWebScreenOrientationPortraitPrimary: | |
213 case kWebScreenOrientationPortraitSecondary: | |
214 return SimpleOrientation::kPortrait; | |
215 case kWebScreenOrientationLandscapePrimary: | |
216 case kWebScreenOrientationLandscapeSecondary: | |
217 return SimpleOrientation::kLandscape; | |
218 case kWebScreenOrientationUndefined: | |
219 return SimpleOrientation::kUnknown; | |
220 } | |
221 | |
222 NOTREACHED(); | |
223 return SimpleOrientation::kUnknown; | |
224 } | |
225 | |
226 DEFINE_TRACE(MediaControlsRotateToFullscreenDelegate) { | |
227 EventListener::Trace(visitor); | |
228 visitor->Trace(video_element_); | |
229 visitor->Trace(visibility_observer_); | |
230 } | |
231 | |
232 } // namespace blink | |
OLD | NEW |