| 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 |