OLD | NEW |
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" | 8 #include "core/frame/LocalDOMWindow.h" |
9 #include "core/frame/Screen.h" | 9 #include "core/frame/Screen.h" |
10 #include "core/frame/ScreenOrientationController.h" | 10 #include "core/frame/ScreenOrientationController.h" |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 // If the rotate-to-fullscreen feature is also enabled, then start listening | 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 | 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 | 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 | 180 // to then exit fullscreen by rotating their device back to the opposite |
181 // orientation). Otherwise, don't listen for deviceorientation events and just | 181 // orientation). Otherwise, don't listen for deviceorientation events and just |
182 // hold the orientation lock until the user exits fullscreen (which prevents | 182 // hold the orientation lock until the user exits fullscreen (which prevents |
183 // the user rotating to the wrong fullscreen orientation). | 183 // the user rotating to the wrong fullscreen orientation). |
184 if (!RuntimeEnabledFeatures::VideoRotateToFullscreenEnabled()) | 184 if (!RuntimeEnabledFeatures::VideoRotateToFullscreenEnabled()) |
185 return; | 185 return; |
186 | 186 |
| 187 if (is_auto_rotate_enabled_by_user_override_for_testing_ != WTF::nullopt) { |
| 188 GotIsAutoRotateEnabledByUser( |
| 189 is_auto_rotate_enabled_by_user_override_for_testing_.value()); |
| 190 return; |
| 191 } |
| 192 |
187 // Check whether the user locked screen orientation at the OS level. | 193 // Check whether the user locked screen orientation at the OS level. |
188 #if OS(ANDROID) | 194 #if OS(ANDROID) |
189 DCHECK(!monitor_.is_bound()); | 195 DCHECK(!monitor_.is_bound()); |
190 Platform::Current()->GetConnector()->BindInterface( | 196 Platform::Current()->GetConnector()->BindInterface( |
191 device::mojom::blink::kServiceName, mojo::MakeRequest(&monitor_)); | 197 device::mojom::blink::kServiceName, mojo::MakeRequest(&monitor_)); |
192 monitor_->IsAutoRotateEnabledByUser(ConvertToBaseCallback(WTF::Bind( | 198 monitor_->IsAutoRotateEnabledByUser(ConvertToBaseCallback(WTF::Bind( |
193 &MediaControlsOrientationLockDelegate::GotIsAutoRotateEnabledByUser, | 199 &MediaControlsOrientationLockDelegate::GotIsAutoRotateEnabledByUser, |
194 WrapPersistent(this)))); | 200 WrapPersistent(this)))); |
195 #else | 201 #else |
196 GotIsAutoRotateEnabledByUser(true); // Assume always enabled on other OSes. | 202 GotIsAutoRotateEnabledByUser(true); // Assume always enabled on other OSes. |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 case kWebScreenOrientationLandscapeSecondary: | 296 case kWebScreenOrientationLandscapeSecondary: |
291 return kWebScreenOrientationLockLandscape; | 297 return kWebScreenOrientationLockLandscape; |
292 case kWebScreenOrientationUndefined: | 298 case kWebScreenOrientationUndefined: |
293 return kWebScreenOrientationLockLandscape; | 299 return kWebScreenOrientationLockLandscape; |
294 } | 300 } |
295 | 301 |
296 NOTREACHED(); | 302 NOTREACHED(); |
297 return kWebScreenOrientationLockLandscape; | 303 return kWebScreenOrientationLockLandscape; |
298 } | 304 } |
299 | 305 |
300 void MediaControlsOrientationLockDelegate:: | 306 MediaControlsOrientationLockDelegate::DeviceOrientationType |
301 MaybeUnlockIfDeviceOrientationMatchesVideo(DeviceOrientationEvent* event) { | 307 MediaControlsOrientationLockDelegate::ComputeDeviceOrientation( |
302 DCHECK_EQ(state_, State::kMaybeLockedFullscreen); | 308 DeviceOrientationData* data) const { |
303 DCHECK_NE(locked_orientation_, kWebScreenOrientationLockDefault); | |
304 | |
305 LocalDOMWindow* dom_window = GetDocument().domWindow(); | 309 LocalDOMWindow* dom_window = GetDocument().domWindow(); |
306 if (!dom_window) | 310 if (!dom_window) |
307 return; | 311 return DeviceOrientationType::kUnknown; |
308 | 312 |
309 if (!event->Orientation()->CanProvideBeta() || | 313 if (!data->CanProvideBeta() || !data->CanProvideGamma()) |
310 !event->Orientation()->CanProvideGamma()) { | 314 return DeviceOrientationType::kUnknown; |
311 return; | 315 double beta = data->Beta(); |
312 } | 316 double gamma = data->Gamma(); |
313 double beta = event->Orientation()->Beta(); | |
314 double gamma = event->Orientation()->Gamma(); | |
315 | 317 |
316 // Calculate the projection of the up vector (normal to the earth's surface) | 318 // 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 | 319 // 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 | 320 // 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). | 321 // pointing upwards (x,y) will be (0,-1). |
320 double x = -std::sin(deg2rad(gamma)) * std::cos(deg2rad(beta)); | 322 double x = -std::sin(deg2rad(gamma)) * std::cos(deg2rad(beta)); |
321 double y = -std::sin(deg2rad(beta)); | 323 double y = -std::sin(deg2rad(beta)); |
322 | 324 |
323 // Convert (x,y) to polar coordinates: 0 <= device_orientation_angle < 360 and | 325 // 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 | 326 // 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 | 327 // degrees between the current physical orientation of the device and the |
326 // natural physical orientation of the device (ignoring the screen | 328 // natural physical orientation of the device (ignoring the screen |
327 // orientation). Thus snapping device_orientation_angle to the nearest | 329 // orientation). Thus snapping device_orientation_angle to the nearest |
328 // multiple of 90 gives the value screen.orientation.angle would have if the | 330 // 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 | 331 // 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 | 332 // 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, | 333 // 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. | 334 // so we pass y=x and x=-y to atan2 to rotate by 90 degrees. |
333 double r = std::sqrt(x * x + y * y); | 335 double r = std::sqrt(x * x + y * y); |
334 double device_orientation_angle = | 336 double device_orientation_angle = |
335 std::fmod(rad2deg(std::atan2(/* y= */ x, /* x= */ -y)) + 360, 360); | 337 std::fmod(rad2deg(std::atan2(/* y= */ x, /* x= */ -y)) + 360, 360); |
336 | 338 |
| 339 // If angle between device's screen and the horizontal plane is less than |
| 340 // kMinElevationAngle (chosen to approximately match Android's behavior), then |
| 341 // device is too flat to reliably determine orientation. |
337 constexpr double kMinElevationAngle = 24; // degrees from horizontal plane | 342 constexpr double kMinElevationAngle = 24; // degrees from horizontal plane |
338 if (r < std::sin(deg2rad(kMinElevationAngle))) | 343 if (r < std::sin(deg2rad(kMinElevationAngle))) |
339 return; // Device is too flat to reliably determine orientation. | 344 return DeviceOrientationType::kFlat; |
340 | 345 |
341 // device_orientation_angle snapped to nearest multiple of 90. | 346 // device_orientation_angle snapped to nearest multiple of 90. |
342 int device_orientation_angle90 = | 347 int device_orientation_angle90 = |
343 std::lround(device_orientation_angle / 90) * 90; | 348 std::lround(device_orientation_angle / 90) * 90; |
344 | 349 |
345 if (std::abs(device_orientation_angle - device_orientation_angle90) > 23) { | 350 // To be considered portrait or landscape, allow the device to be rotated 23 |
346 // Device is diagonal (within 44 degree hysteresis zone). | 351 // degrees (chosen to approximately match Android's behavior) to either side |
347 return; | 352 // of those orientations. In the remaining 90 - 2*23 = 44 degree hysteresis |
348 } | 353 // zones, consider the device to be diagonal. These hysteresis zones prevent |
| 354 // the computed orientation from oscillating rapidly between portrait and |
| 355 // landscape when the device is in between the two orientations. |
| 356 if (std::abs(device_orientation_angle - device_orientation_angle90) > 23) |
| 357 return DeviceOrientationType::kDiagonal; |
349 | 358 |
350 // screen.orientation.angle is the standardized replacement for | 359 // screen.orientation.angle is the standardized replacement for |
351 // window.orientation. They are equal, except -90 was replaced by 270. | 360 // window.orientation. They are equal, except -90 was replaced by 270. |
352 int screen_orientation_angle = | 361 int screen_orientation_angle = |
353 ScreenScreenOrientation::orientation(nullptr /* ScriptState */, | 362 ScreenScreenOrientation::orientation(nullptr /* ScriptState */, |
354 *dom_window->screen()) | 363 *dom_window->screen()) |
355 ->angle(); | 364 ->angle(); |
356 | 365 |
357 // This is equivalent to screen.orientation.type.startsWith('landscape'). | 366 // This is equivalent to screen.orientation.type.startsWith('landscape'). |
358 bool screen_orientation_is_landscape = | 367 bool screen_orientation_is_portrait = |
359 dom_window->screen()->width() > dom_window->screen()->height(); | 368 dom_window->screen()->width() <= dom_window->screen()->height(); |
360 | 369 |
361 // The natural orientation of the device could either be portrait (almost | 370 // The natural orientation of the device could either be portrait (almost |
362 // all phones, and some tablets like Nexus 7) or landscape (other tablets | 371 // all phones, and some tablets like Nexus 7) or landscape (other tablets |
363 // like Pixel C). Detect this by comparing angle to orientation. | 372 // like Pixel C). Detect this by comparing angle to orientation. |
364 // TODO(johnme): This might get confused on square screens. | 373 // TODO(johnme): This might get confused on square screens. |
365 bool screen_orientation_is_natural_or_flipped_natural = | 374 bool screen_orientation_is_natural_or_flipped_natural = |
366 screen_orientation_angle % 180 == 0; | 375 screen_orientation_angle % 180 == 0; |
367 bool natural_orientation_is_landscape = | 376 bool natural_orientation_is_portrait = |
368 screen_orientation_is_landscape == | 377 screen_orientation_is_portrait == |
369 screen_orientation_is_natural_or_flipped_natural; | 378 screen_orientation_is_natural_or_flipped_natural; |
370 | 379 |
371 bool natural_orientation_matches_video = | 380 // If natural_orientation_is_portrait_, then angles 0 and 180 are portrait, |
372 natural_orientation_is_landscape == | 381 // otherwise angles 90 and 270 are portrait. |
373 (locked_orientation_ == kWebScreenOrientationLockLandscape); | 382 int portrait_angle_mod_180 = natural_orientation_is_portrait ? 0 : 90; |
| 383 return device_orientation_angle90 % 180 == portrait_angle_mod_180 |
| 384 ? DeviceOrientationType::kPortrait |
| 385 : DeviceOrientationType::kLandscape; |
| 386 } |
374 | 387 |
375 // If natural_orientation_matches_video, then 0 and 180 match video, otherwise | 388 void MediaControlsOrientationLockDelegate:: |
376 // 90 and 270 match video. | 389 MaybeUnlockIfDeviceOrientationMatchesVideo(DeviceOrientationEvent* event) { |
377 bool device_orientation_matches_video = | 390 DCHECK_EQ(state_, State::kMaybeLockedFullscreen); |
378 (device_orientation_angle90 % 180) == | 391 DCHECK(locked_orientation_ == kWebScreenOrientationLockPortrait || |
379 (natural_orientation_matches_video ? 0 : 90); | 392 locked_orientation_ == kWebScreenOrientationLockLandscape); |
380 | 393 |
381 if (!device_orientation_matches_video) | 394 DeviceOrientationType device_orientation = |
| 395 ComputeDeviceOrientation(event->Orientation()); |
| 396 |
| 397 DeviceOrientationType video_orientation = |
| 398 locked_orientation_ == kWebScreenOrientationLockPortrait |
| 399 ? DeviceOrientationType::kPortrait |
| 400 : DeviceOrientationType::kLandscape; |
| 401 |
| 402 if (device_orientation != video_orientation) |
382 return; | 403 return; |
383 | 404 |
384 // Job done: the user rotated their device to match the orientation of the | 405 // 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). | 406 // video that we locked to, so now we can unlock (and stop listening). |
386 MaybeUnlockOrientation(); | 407 MaybeUnlockOrientation(); |
387 } | 408 } |
388 | 409 |
389 DEFINE_TRACE(MediaControlsOrientationLockDelegate) { | 410 DEFINE_TRACE(MediaControlsOrientationLockDelegate) { |
390 EventListener::Trace(visitor); | 411 EventListener::Trace(visitor); |
391 visitor->Trace(video_element_); | 412 visitor->Trace(video_element_); |
392 } | 413 } |
393 | 414 |
394 } // namespace blink | 415 } // namespace blink |
OLD | NEW |