Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: third_party/WebKit/Source/modules/media_controls/MediaControlsOrientationLockDelegate.cpp

Issue 2890423003: [Media controls] Integrate rotate-to-fullscreen with orientation lock (Closed)
Patch Set: Fix existing tests Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "modules/media_controls/MediaControlsOrientationLockDelegate.h" 5 #include "modules/media_controls/MediaControlsOrientationLockDelegate.h"
6 6
7 #include <cmath>
8
7 #include "core/events/Event.h" 9 #include "core/events/Event.h"
10 #include "core/frame/LocalDOMWindow.h"
11 #include "core/frame/Screen.h"
8 #include "core/frame/ScreenOrientationController.h" 12 #include "core/frame/ScreenOrientationController.h"
9 #include "core/html/HTMLVideoElement.h" 13 #include "core/html/HTMLVideoElement.h"
10 #include "core/page/ChromeClient.h" 14 #include "core/page/ChromeClient.h"
15 #include "modules/device_orientation/DeviceOrientationData.h"
16 #include "modules/device_orientation/DeviceOrientationEvent.h"
17 #include "modules/screen_orientation/ScreenOrientation.h"
18 #include "modules/screen_orientation/ScreenScreenOrientation.h"
11 #include "platform/Histogram.h" 19 #include "platform/Histogram.h"
20 #include "platform/RuntimeEnabledFeatures.h"
21 #include "platform/wtf/Functional.h"
22 #include "platform/wtf/MathExtras.h"
12 #include "public/platform/WebScreenInfo.h" 23 #include "public/platform/WebScreenInfo.h"
13 #include "public/platform/modules/screen_orientation/WebLockOrientationCallback. h" 24 #include "public/platform/modules/screen_orientation/WebLockOrientationCallback. h"
14 25
26 #if OS(ANDROID)
27 #include "platform/mojo/MojoHelper.h"
28 #include "public/platform/Platform.h"
29 #include "services/device/public/interfaces/constants.mojom-blink.h"
30 #include "services/service_manager/public/cpp/connector.h"
31 #endif // OS(ANDROID)
32
15 namespace blink { 33 namespace blink {
16 34
17 namespace { 35 namespace {
18 36
19 // These values are used for histograms. Do not reorder. 37 // These values are used for histograms. Do not reorder.
20 enum class MetadataAvailabilityMetrics { 38 enum class MetadataAvailabilityMetrics {
21 kAvailable = 0, // Available when lock was attempted. 39 kAvailable = 0, // Available when lock was attempted.
22 kMissing = 1, // Missing when lock was attempted. 40 kMissing = 1, // Missing when lock was attempted.
23 kReceived = 2, // Received after being missing in order to lock. 41 kReceived = 2, // Received after being missing in order to lock.
24 42
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 if (!GetDocument().GetFrame()) 130 if (!GetDocument().GetFrame())
113 return; 131 return;
114 132
115 auto controller = 133 auto controller =
116 ScreenOrientationController::From(*GetDocument().GetFrame()); 134 ScreenOrientationController::From(*GetDocument().GetFrame());
117 if (controller->MaybeHasActiveLock()) { 135 if (controller->MaybeHasActiveLock()) {
118 RecordLockResult(LockResultMetrics::kAlreadyLocked); 136 RecordLockResult(LockResultMetrics::kAlreadyLocked);
119 return; 137 return;
120 } 138 }
121 139
122 WebScreenOrientationLockType orientation_lock = ComputeOrientationLock(); 140 locked_orientation_ = ComputeOrientationLock();
123 controller->lock(orientation_lock, 141 DCHECK_NE(locked_orientation_, kWebScreenOrientationLockDefault);
142 controller->lock(locked_orientation_,
124 WTF::WrapUnique(new DummyScreenOrientationCallback)); 143 WTF::WrapUnique(new DummyScreenOrientationCallback));
125 should_unlock_orientation_ = true;
126 144
127 if (orientation_lock == kWebScreenOrientationLockLandscape) 145 if (locked_orientation_ == kWebScreenOrientationLockLandscape)
128 RecordLockResult(LockResultMetrics::kLandscape); 146 RecordLockResult(LockResultMetrics::kLandscape);
129 else 147 else
130 RecordLockResult(LockResultMetrics::kPortrait); 148 RecordLockResult(LockResultMetrics::kPortrait);
149
150 MaybeListenToDeviceOrientation();
131 } 151 }
132 152
133 void MediaControlsOrientationLockDelegate::MaybeUnlockOrientation() { 153 void MediaControlsOrientationLockDelegate::MaybeUnlockOrientation() {
134 DCHECK(state_ != State::kPendingFullscreen); 154 DCHECK(state_ != State::kPendingFullscreen);
135 155
136 state_ = State::kPendingFullscreen; 156 state_ = State::kPendingFullscreen;
137 157
138 if (!should_unlock_orientation_) 158 if (locked_orientation_ == kWebScreenOrientationLockDefault /* unlocked */)
139 return; 159 return;
140 160
161 monitor_.reset(); // Cancel any GotIsAutoRotateEnabledByUser Mojo callback.
162 if (LocalDOMWindow* dom_window = GetDocument().domWindow()) {
163 dom_window->removeEventListener(EventTypeNames::deviceorientation, this,
164 false);
165 }
166
141 ScreenOrientationController::From(*GetDocument().GetFrame())->unlock(); 167 ScreenOrientationController::From(*GetDocument().GetFrame())->unlock();
142 should_unlock_orientation_ = false; 168 locked_orientation_ = kWebScreenOrientationLockDefault /* unlocked */;
169 }
170
171 void MediaControlsOrientationLockDelegate::MaybeListenToDeviceOrientation() {
172 DCHECK_EQ(state_, State::kMaybeLockedFullscreen);
173 DCHECK_NE(locked_orientation_, kWebScreenOrientationLockDefault);
174
175 // If the rotate-to-fullscreen feature is also enabled, then start listening
176 // to deviceorientation events so the orientation can be unlocked once the
177 // user rotates the device to match the video's orientation (allowing the user
178 // to then exit fullscreen by rotating their device back to the opposite
179 // orientation). Otherwise, don't listen for deviceorientation events and just
180 // hold the orientation lock until the user exits fullscreen (which prevents
181 // the user rotating to the wrong fullscreen orientation).
182 if (!RuntimeEnabledFeatures::videoRotateToFullscreenEnabled())
183 return;
184
185 // Check whether the user locked screen orientation at the OS level.
186 #if OS(ANDROID)
187 DCHECK(!monitor_.is_bound());
188 Platform::Current()->GetConnector()->BindInterface(
189 device::mojom::blink::kServiceName, mojo::MakeRequest(&monitor_));
190 monitor_->IsAutoRotateEnabledByUser(ConvertToBaseCallback(WTF::Bind(
191 &MediaControlsOrientationLockDelegate::GotIsAutoRotateEnabledByUser,
192 WrapPersistent(this))));
193 #else
194 GotIsAutoRotateEnabledByUser(true); // Assume always enabled on other OSes.
195 #endif // OS(ANDROID)
196 }
197
198 void MediaControlsOrientationLockDelegate::GotIsAutoRotateEnabledByUser(
199 bool enabled) {
200 monitor_.reset();
201
202 if (!enabled) {
203 // Since the user has locked their screen orientation, prevent
204 // MediaControlsRotateToFullscreenDelegate from exiting fullscreen by not
205 // listening for deviceorientation events and instead continuing to hold the
206 // orientation lock until the user exits fullscreen. This enables users to
207 // watch videos in bed with their head facing sideways (which requires a
208 // landscape screen orientation when the device is portrait and vice versa).
209 // TODO(johnme): Ideally we would start listening for deviceorientation
210 // events and allow rotating to exit if a user enables screen auto rotation
211 // after we have locked to landscape. That would require listening for
212 // changes to the auto rotate setting, rather than only checking it once.
213 return;
214 }
215
216 if (LocalDOMWindow* dom_window = GetDocument().domWindow()) {
217 dom_window->addEventListener(EventTypeNames::deviceorientation, this,
218 false);
219 }
143 } 220 }
144 221
145 HTMLVideoElement& MediaControlsOrientationLockDelegate::VideoElement() const { 222 HTMLVideoElement& MediaControlsOrientationLockDelegate::VideoElement() const {
146 return *video_element_; 223 return *video_element_;
147 } 224 }
148 225
149 Document& MediaControlsOrientationLockDelegate::GetDocument() const { 226 Document& MediaControlsOrientationLockDelegate::GetDocument() const {
150 return VideoElement().GetDocument(); 227 return VideoElement().GetDocument();
151 } 228 }
152 229
(...skipping 13 matching lines...) Expand all
166 return; 243 return;
167 } 244 }
168 245
169 if (event->type() == EventTypeNames::loadedmetadata) { 246 if (event->type() == EventTypeNames::loadedmetadata) {
170 if (state_ == State::kPendingMetadata) 247 if (state_ == State::kPendingMetadata)
171 MaybeLockOrientation(); 248 MaybeLockOrientation();
172 249
173 return; 250 return;
174 } 251 }
175 252
253 if (event->type() == EventTypeNames::deviceorientation) {
254 MaybeUnlockIfDeviceOrientationMatchesVideo(ToDeviceOrientationEvent(event));
255
256 return;
257 }
258
176 NOTREACHED(); 259 NOTREACHED();
177 } 260 }
178 261
179 WebScreenOrientationLockType 262 WebScreenOrientationLockType
180 MediaControlsOrientationLockDelegate::ComputeOrientationLock() const { 263 MediaControlsOrientationLockDelegate::ComputeOrientationLock() const {
181 DCHECK(VideoElement().getReadyState() != HTMLMediaElement::kHaveNothing); 264 DCHECK(VideoElement().getReadyState() != HTMLMediaElement::kHaveNothing);
182 265
183 const unsigned width = VideoElement().videoWidth(); 266 const unsigned width = VideoElement().videoWidth();
184 const unsigned height = VideoElement().videoHeight(); 267 const unsigned height = VideoElement().videoHeight();
185 268
(...skipping 19 matching lines...) Expand all
205 case kWebScreenOrientationLandscapeSecondary: 288 case kWebScreenOrientationLandscapeSecondary:
206 return kWebScreenOrientationLockLandscape; 289 return kWebScreenOrientationLockLandscape;
207 case kWebScreenOrientationUndefined: 290 case kWebScreenOrientationUndefined:
208 return kWebScreenOrientationLockLandscape; 291 return kWebScreenOrientationLockLandscape;
209 } 292 }
210 293
211 NOTREACHED(); 294 NOTREACHED();
212 return kWebScreenOrientationLockLandscape; 295 return kWebScreenOrientationLockLandscape;
213 } 296 }
214 297
298 void MediaControlsOrientationLockDelegate::
299 MaybeUnlockIfDeviceOrientationMatchesVideo(DeviceOrientationEvent* event) {
300 DCHECK_EQ(state_, State::kMaybeLockedFullscreen);
301 DCHECK_NE(locked_orientation_, kWebScreenOrientationLockDefault);
302
303 LocalDOMWindow* dom_window = GetDocument().domWindow();
304 if (!dom_window)
305 return;
306
307 if (!event->Orientation()->CanProvideBeta() ||
308 !event->Orientation()->CanProvideGamma()) {
309 return;
310 }
311 double beta = event->Orientation()->Beta();
312 double gamma = event->Orientation()->Gamma();
313
314 // Calculate the projection of the up vector (normal to the earth's surface)
315 // onto the device's screen in its natural orientation. (x,y) will lie within
316 // the unit circle centered on (0,0), e.g. if the top of the device is
317 // pointing upwards (x,y) will be (0,-1).
318 double x = -std::sin(deg2rad(gamma)) * std::cos(deg2rad(beta));
319 double y = -std::sin(deg2rad(beta));
320
321 // Convert (x,y) to polar coordinates: 0 <= device_orientation_angle < 360 and
322 // 0 <= r <= 1, such that device_orientation_angle is the clockwise angle in
323 // degrees between the current physical orientation of the device and the
324 // natural physical orientation of the device (ignoring the screen
325 // orientation). Thus snapping device_orientation_angle to the nearest
326 // multiple of 90 gives the value screen.orientation.angle would have if the
327 // screen orientation was allowed to rotate freely to match the device
328 // orientation. Note that we want device_orientation_angle==0 when the top of
329 // the device is pointing upwards, but atan2's zero angle points to the right,
330 // so we pass y=x and x=-y to atan2 to rotate by 90 degrees.
331 double r = std::sqrt(x * x + y * y);
332 double device_orientation_angle =
333 std::fmod(rad2deg(std::atan2(/* y= */ x, /* x= */ -y)) + 360, 360);
334
335 constexpr double kMinElevationAngle = 24; // degrees from horizontal plane
336 if (r < std::sin(deg2rad(kMinElevationAngle)))
337 return; // Device is too flat to reliably determine orientation.
338
339 // device_orientation_angle snapped to nearest multiple of 90.
340 int device_orientation_angle90 =
341 std::lround(device_orientation_angle / 90) * 90;
342
343 if (std::abs(device_orientation_angle - device_orientation_angle90) > 23) {
344 // Device is diagonal (within 44 degree hysteresis zone).
345 return;
346 }
347
348 // screen.orientation.angle is the standardized replacement for
349 // window.orientation. They are equal, except -90 was replaced by 270.
350 int screen_orientation_angle =
351 ScreenScreenOrientation::orientation(nullptr /* ScriptState */,
352 *dom_window->screen())
353 ->angle();
354
355 // This is equivalent to screen.orientation.type.startsWith('landscape').
356 bool screen_orientation_is_landscape =
357 dom_window->screen()->width() > dom_window->screen()->height();
358
359 // The natural orientation of the device could either be portrait (almost
360 // all phones, and some tablets like Nexus 7) or landscape (other tablets
361 // like Pixel C). Detect this by comparing angle to orientation.
362 // TODO(johnme): This might get confused on square screens.
363 bool screen_orientation_is_natural_or_flipped_natural =
364 screen_orientation_angle % 180 == 0;
365 bool natural_orientation_is_landscape =
366 screen_orientation_is_landscape ==
367 screen_orientation_is_natural_or_flipped_natural;
368
369 bool natural_orientation_matches_video =
370 natural_orientation_is_landscape ==
371 (locked_orientation_ == kWebScreenOrientationLockLandscape);
372
373 // If natural_orientation_matches_video, then 0 and 180 match video, otherwise
374 // 90 and 270 match video.
375 bool device_orientation_matches_video =
376 (device_orientation_angle90 % 180) ==
377 (natural_orientation_matches_video ? 0 : 90);
378
379 if (!device_orientation_matches_video)
380 return;
381
382 // Job done: the user rotated their device to match the orientation of the
383 // video that we locked to, so now we can unlock (and stop listening).
384 MaybeUnlockOrientation();
385 }
386
215 DEFINE_TRACE(MediaControlsOrientationLockDelegate) { 387 DEFINE_TRACE(MediaControlsOrientationLockDelegate) {
216 EventListener::Trace(visitor); 388 EventListener::Trace(visitor);
217 visitor->Trace(video_element_); 389 visitor->Trace(video_element_);
218 } 390 }
219 391
220 } // namespace blink 392 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698