Call InvalidationController#refreshRegisteredTypes() on Chrome startup
[chromium-blink-merge.git] / ash / content / display / screen_orientation_controller_chromeos.cc
blob0e63f65d6fcb688c554c291949e6cf7c967375cb
1 // Copyright 2014 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.
5 #include "ash/content/display/screen_orientation_controller_chromeos.h"
7 #include "ash/ash_switches.h"
8 #include "ash/display/display_info.h"
9 #include "ash/display/display_manager.h"
10 #include "ash/rotator/screen_rotation_animator.h"
11 #include "ash/shell.h"
12 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
13 #include "base/auto_reset.h"
14 #include "base/command_line.h"
15 #include "chromeos/accelerometer/accelerometer_reader.h"
16 #include "chromeos/accelerometer/accelerometer_types.h"
17 #include "content/public/browser/screen_orientation_provider.h"
18 #include "content/public/browser/web_contents.h"
19 #include "ui/aura/window.h"
20 #include "ui/aura/window_observer.h"
21 #include "ui/chromeos/accelerometer/accelerometer_util.h"
22 #include "ui/gfx/display.h"
23 #include "ui/gfx/geometry/size.h"
24 #include "ui/wm/public/activation_client.h"
26 namespace {
28 // The angle which the screen has to be rotated past before the display will
29 // rotate to match it (i.e. 45.0f is no stickiness).
30 const float kDisplayRotationStickyAngleDegrees = 60.0f;
32 // The minimum acceleration in m/s^2 in a direction required to trigger screen
33 // rotation. This prevents rapid toggling of rotation when the device is near
34 // flat and there is very little screen aligned force on it. The value is
35 // effectively the sine of the rise angle required times the acceleration due
36 // to gravity, with the current value requiring at least a 25 degree rise.
37 const float kMinimumAccelerationScreenRotation = 4.2f;
39 blink::WebScreenOrientationLockType GetDisplayNaturalOrientation() {
40 if (!gfx::Display::HasInternalDisplay())
41 return blink::WebScreenOrientationLockLandscape;
43 ash::DisplayInfo info =
44 ash::Shell::GetInstance()->display_manager()->GetDisplayInfo(
45 gfx::Display::InternalDisplayId());
46 gfx::Size size = info.size_in_pixel();
47 switch (info.GetActiveRotation()) {
48 case gfx::Display::ROTATE_0:
49 case gfx::Display::ROTATE_180:
50 return size.height() >= size.width()
51 ? blink::WebScreenOrientationLockPortrait
52 : blink::WebScreenOrientationLockLandscape;
53 case gfx::Display::ROTATE_90:
54 case gfx::Display::ROTATE_270:
55 return size.height() < size.width()
56 ? blink::WebScreenOrientationLockPortrait
57 : blink::WebScreenOrientationLockLandscape;
59 NOTREACHED();
60 return blink::WebScreenOrientationLockLandscape;
63 } // namespace
65 namespace ash {
67 ScreenOrientationController::ScreenOrientationController()
68 : natural_orientation_(GetDisplayNaturalOrientation()),
69 ignore_display_configuration_updates_(false),
70 rotation_locked_(false),
71 rotation_locked_orientation_(blink::WebScreenOrientationLockAny),
72 user_rotation_(gfx::Display::ROTATE_0),
73 current_rotation_(gfx::Display::ROTATE_0) {
74 content::ScreenOrientationProvider::SetDelegate(this);
75 Shell::GetInstance()->AddShellObserver(this);
78 ScreenOrientationController::~ScreenOrientationController() {
79 content::ScreenOrientationProvider::SetDelegate(NULL);
80 Shell::GetInstance()->RemoveShellObserver(this);
81 chromeos::AccelerometerReader::GetInstance()->RemoveObserver(this);
82 Shell::GetInstance()->display_controller()->RemoveObserver(this);
83 Shell::GetInstance()->activation_client()->RemoveObserver(this);
84 for (auto& windows : locking_windows_)
85 windows.first->RemoveObserver(this);
88 void ScreenOrientationController::AddObserver(Observer* observer) {
89 observers_.AddObserver(observer);
92 void ScreenOrientationController::RemoveObserver(Observer* observer) {
93 observers_.RemoveObserver(observer);
96 void ScreenOrientationController::SetRotationLocked(bool rotation_locked) {
97 if (rotation_locked_ == rotation_locked)
98 return;
99 rotation_locked_ = rotation_locked;
100 if (!rotation_locked_)
101 rotation_locked_orientation_ = blink::WebScreenOrientationLockAny;
102 FOR_EACH_OBSERVER(Observer, observers_,
103 OnRotationLockChanged(rotation_locked_));
104 if (!gfx::Display::HasInternalDisplay())
105 return;
106 base::AutoReset<bool> auto_ignore_display_configuration_updates(
107 &ignore_display_configuration_updates_, true);
108 Shell::GetInstance()->display_manager()->RegisterDisplayRotationProperties(
109 rotation_locked_, current_rotation_);
112 void ScreenOrientationController::SetDisplayRotation(
113 gfx::Display::Rotation rotation,
114 gfx::Display::RotationSource source) {
115 if (!gfx::Display::HasInternalDisplay())
116 return;
117 current_rotation_ = rotation;
118 base::AutoReset<bool> auto_ignore_display_configuration_updates(
119 &ignore_display_configuration_updates_, true);
121 ash::ScreenRotationAnimator screen_rotation_animator(
122 gfx::Display::InternalDisplayId());
123 if (screen_rotation_animator.CanAnimate()) {
124 screen_rotation_animator.Rotate(rotation, source);
125 } else {
126 Shell::GetInstance()->display_manager()->SetDisplayRotation(
127 gfx::Display::InternalDisplayId(), rotation, source);
131 void ScreenOrientationController::OnWindowActivated(
132 aura::client::ActivationChangeObserver::ActivationReason reason,
133 aura::Window* gained_active,
134 aura::Window* lost_active) {
135 ApplyLockForActiveWindow();
138 // Currently contents::WebContents will only be able to lock rotation while
139 // fullscreen. In this state a user cannot click on the tab strip to change. If
140 // this becomes supported for non-fullscreen tabs then the following interferes
141 // with TabDragController. OnWindowVisibilityChanged is called between a mouse
142 // down and mouse up. The rotation this triggers leads to a coordinate space
143 // change in the middle of an event. Causes the tab to separate from the tab
144 // strip.
145 void ScreenOrientationController::OnWindowVisibilityChanged(
146 aura::Window* window,
147 bool visible) {
148 if (locking_windows_.find(window) == locking_windows_.end())
149 return;
150 ApplyLockForActiveWindow();
153 void ScreenOrientationController::OnWindowDestroying(aura::Window* window) {
154 RemoveLockingWindow(window);
157 void ScreenOrientationController::OnAccelerometerUpdated(
158 scoped_refptr<const chromeos::AccelerometerUpdate> update) {
159 if (rotation_locked_ && !CanRotateInLockedState())
160 return;
161 if (!update->has(chromeos::ACCELEROMETER_SOURCE_SCREEN))
162 return;
163 // Ignore the reading if it appears unstable. The reading is considered
164 // unstable if it deviates too much from gravity
165 if (ui::IsAccelerometerReadingStable(*update,
166 chromeos::ACCELEROMETER_SOURCE_SCREEN)) {
167 HandleScreenRotation(update->get(chromeos::ACCELEROMETER_SOURCE_SCREEN));
171 bool ScreenOrientationController::FullScreenRequired(
172 content::WebContents* web_contents) {
173 return true;
176 void ScreenOrientationController::Lock(
177 content::WebContents* web_contents,
178 blink::WebScreenOrientationLockType lock_orientation) {
179 if (locking_windows_.empty())
180 Shell::GetInstance()->activation_client()->AddObserver(this);
182 aura::Window* requesting_window = web_contents->GetNativeView();
183 if (!requesting_window->HasObserver(this))
184 requesting_window->AddObserver(this);
185 locking_windows_[requesting_window] = lock_orientation;
187 ApplyLockForActiveWindow();
190 bool ScreenOrientationController::ScreenOrientationProviderSupported() {
191 return Shell::GetInstance()
192 ->maximize_mode_controller()
193 ->IsMaximizeModeWindowManagerEnabled() &&
194 !base::CommandLine::ForCurrentProcess()->HasSwitch(
195 switches::kAshDisableScreenOrientationLock);
198 void ScreenOrientationController::Unlock(content::WebContents* web_contents) {
199 aura::Window* requesting_window = web_contents->GetNativeView();
200 RemoveLockingWindow(requesting_window);
203 void ScreenOrientationController::OnDisplayConfigurationChanged() {
204 if (ignore_display_configuration_updates_)
205 return;
206 if (!gfx::Display::HasInternalDisplay())
207 return;
208 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
209 gfx::Display::Rotation user_rotation =
210 display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId())
211 .GetActiveRotation();
212 if (user_rotation != current_rotation_) {
213 // A user may change other display configuration settings. When the user
214 // does change the rotation setting, then lock rotation to prevent the
215 // accelerometer from erasing their change.
216 SetRotationLocked(true);
217 user_rotation_ = current_rotation_ = user_rotation;
221 void ScreenOrientationController::OnMaximizeModeStarted() {
222 // Do not exit early, as the internal display can be determined after Maximize
223 // Mode has started. (chrome-os-partner:38796)
224 // Always start observing.
225 if (gfx::Display::HasInternalDisplay()) {
226 current_rotation_ = user_rotation_ =
227 Shell::GetInstance()
228 ->display_manager()
229 ->GetDisplayInfo(gfx::Display::InternalDisplayId())
230 .GetActiveRotation();
232 if (!rotation_locked_)
233 LoadDisplayRotationProperties();
234 chromeos::AccelerometerReader::GetInstance()->AddObserver(this);
235 Shell::GetInstance()->display_controller()->AddObserver(this);
238 void ScreenOrientationController::OnMaximizeModeEnded() {
239 chromeos::AccelerometerReader::GetInstance()->RemoveObserver(this);
240 Shell::GetInstance()->display_controller()->RemoveObserver(this);
241 if (current_rotation_ != user_rotation_)
242 SetDisplayRotation(user_rotation_, gfx::Display::ROTATION_SOURCE_USER);
245 void ScreenOrientationController::LockRotation(
246 gfx::Display::Rotation rotation,
247 gfx::Display::RotationSource source) {
248 SetRotationLocked(true);
249 SetDisplayRotation(rotation, source);
252 void ScreenOrientationController::LockRotationToOrientation(
253 blink::WebScreenOrientationLockType lock_orientation) {
254 rotation_locked_orientation_ = lock_orientation;
255 switch (lock_orientation) {
256 case blink::WebScreenOrientationLockAny:
257 SetRotationLocked(false);
258 break;
259 case blink::WebScreenOrientationLockDefault:
260 NOTREACHED();
261 break;
262 case blink::WebScreenOrientationLockPortraitPrimary:
263 LockRotationToPrimaryOrientation(blink::WebScreenOrientationLockPortrait);
264 break;
265 case blink::WebScreenOrientationLockLandscape:
266 case blink::WebScreenOrientationLockPortrait:
267 LockToRotationMatchingOrientation(lock_orientation);
268 break;
269 case blink::WebScreenOrientationLockPortraitSecondary:
270 LockRotationToSecondaryOrientation(
271 blink::WebScreenOrientationLockPortrait);
272 break;
273 case blink::WebScreenOrientationLockLandscapeSecondary:
274 LockRotationToSecondaryOrientation(
275 blink::WebScreenOrientationLockLandscape);
276 break;
277 case blink::WebScreenOrientationLockLandscapePrimary:
278 LockRotationToPrimaryOrientation(
279 blink::WebScreenOrientationLockLandscape);
280 break;
281 case blink::WebScreenOrientationLockNatural:
282 LockRotation(gfx::Display::ROTATE_0,
283 gfx::Display::ROTATION_SOURCE_ACTIVE);
284 break;
285 default:
286 NOTREACHED();
287 break;
291 void ScreenOrientationController::LockRotationToPrimaryOrientation(
292 blink::WebScreenOrientationLockType lock_orientation) {
293 LockRotation(natural_orientation_ == lock_orientation
294 ? gfx::Display::ROTATE_0
295 : gfx::Display::ROTATE_90,
296 gfx::Display::ROTATION_SOURCE_ACTIVE);
299 void ScreenOrientationController::LockRotationToSecondaryOrientation(
300 blink::WebScreenOrientationLockType lock_orientation) {
301 LockRotation(natural_orientation_ == lock_orientation
302 ? gfx::Display::ROTATE_180
303 : gfx::Display::ROTATE_270,
304 gfx::Display::ROTATION_SOURCE_ACTIVE);
307 void ScreenOrientationController::LockToRotationMatchingOrientation(
308 blink::WebScreenOrientationLockType lock_orientation) {
309 if (!gfx::Display::HasInternalDisplay())
310 return;
312 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
313 gfx::Display::Rotation rotation =
314 display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId())
315 .GetActiveRotation();
316 if (natural_orientation_ == lock_orientation) {
317 if (rotation == gfx::Display::ROTATE_0 ||
318 rotation == gfx::Display::ROTATE_180) {
319 SetRotationLocked(true);
320 } else {
321 LockRotation(gfx::Display::ROTATE_0,
322 gfx::Display::ROTATION_SOURCE_ACTIVE);
324 } else {
325 if (rotation == gfx::Display::ROTATE_90 ||
326 rotation == gfx::Display::ROTATE_270) {
327 SetRotationLocked(true);
328 } else {
329 LockRotation(gfx::Display::ROTATE_90,
330 gfx::Display::ROTATION_SOURCE_ACTIVE);
335 void ScreenOrientationController::HandleScreenRotation(
336 const chromeos::AccelerometerReading& lid) {
337 gfx::Vector3dF lid_flattened(lid.x, lid.y, 0.0f);
338 float lid_flattened_length = lid_flattened.Length();
339 // When the lid is close to being flat, don't change rotation as it is too
340 // sensitive to slight movements.
341 if (lid_flattened_length < kMinimumAccelerationScreenRotation)
342 return;
344 // The reference vector is the angle of gravity when the device is rotated
345 // clockwise by 45 degrees. Computing the angle between this vector and
346 // gravity we can easily determine the expected display rotation.
347 static const gfx::Vector3dF rotation_reference(-1.0f, -1.0f, 0.0f);
349 // Set the down vector to match the expected direction of gravity given the
350 // last configured rotation. This is used to enforce a stickiness that the
351 // user must overcome to rotate the display and prevents frequent rotations
352 // when holding the device near 45 degrees.
353 gfx::Vector3dF down(0.0f, 0.0f, 0.0f);
354 if (current_rotation_ == gfx::Display::ROTATE_0)
355 down.set_y(-1.0f);
356 else if (current_rotation_ == gfx::Display::ROTATE_90)
357 down.set_x(-1.0f);
358 else if (current_rotation_ == gfx::Display::ROTATE_180)
359 down.set_y(1.0f);
360 else
361 down.set_x(1.0f);
363 // Don't rotate if the screen has not passed the threshold.
364 if (gfx::AngleBetweenVectorsInDegrees(down, lid_flattened) <
365 kDisplayRotationStickyAngleDegrees) {
366 return;
369 float angle = gfx::ClockwiseAngleBetweenVectorsInDegrees(
370 rotation_reference, lid_flattened, gfx::Vector3dF(0.0f, 0.0f, -1.0f));
372 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_90;
373 if (angle < 90.0f)
374 new_rotation = gfx::Display::ROTATE_0;
375 else if (angle < 180.0f)
376 new_rotation = gfx::Display::ROTATE_270;
377 else if (angle < 270.0f)
378 new_rotation = gfx::Display::ROTATE_180;
380 if (new_rotation != current_rotation_ &&
381 IsRotationAllowedInLockedState(new_rotation))
382 SetDisplayRotation(new_rotation,
383 gfx::Display::ROTATION_SOURCE_ACCELEROMETER);
386 void ScreenOrientationController::LoadDisplayRotationProperties() {
387 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
388 if (!display_manager->registered_internal_display_rotation_lock())
389 return;
390 SetDisplayRotation(display_manager->registered_internal_display_rotation(),
391 gfx::Display::ROTATION_SOURCE_ACCELEROMETER);
392 SetRotationLocked(true);
395 void ScreenOrientationController::ApplyLockForActiveWindow() {
396 aura::Window* active_window =
397 Shell::GetInstance()->activation_client()->GetActiveWindow();
398 for (auto const& windows : locking_windows_) {
399 if (windows.first->TargetVisibility() &&
400 active_window->Contains(windows.first)) {
401 LockRotationToOrientation(windows.second);
402 return;
405 SetRotationLocked(false);
408 void ScreenOrientationController::RemoveLockingWindow(aura::Window* window) {
409 locking_windows_.erase(window);
410 if (locking_windows_.empty())
411 Shell::GetInstance()->activation_client()->RemoveObserver(this);
412 window->RemoveObserver(this);
413 ApplyLockForActiveWindow();
416 bool ScreenOrientationController::IsRotationAllowedInLockedState(
417 gfx::Display::Rotation rotation) {
418 if (!rotation_locked_)
419 return true;
421 if (!CanRotateInLockedState())
422 return false;
424 if (natural_orientation_ == rotation_locked_orientation_) {
425 return rotation == gfx::Display::ROTATE_0 ||
426 rotation == gfx::Display::ROTATE_180;
427 } else {
428 return rotation == gfx::Display::ROTATE_90 ||
429 rotation == gfx::Display::ROTATE_270;
431 return false;
434 bool ScreenOrientationController::CanRotateInLockedState() {
435 return rotation_locked_orientation_ ==
436 blink::WebScreenOrientationLockLandscape ||
437 rotation_locked_orientation_ ==
438 blink::WebScreenOrientationLockPortrait;
441 } // namespace ash