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

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

Powered by Google App Engine
This is Rietveld 408576698