1 // Copyright 2013 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 "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
7 #include "apps/app_window.h"
8 #include "apps/app_window_registry.h"
9 #include "ash/ash_switches.h"
10 #include "ash/desktop_background/user_wallpaper_delegate.h"
11 #include "ash/multi_profile_uma.h"
12 #include "ash/root_window_controller.h"
13 #include "ash/session_state_delegate.h"
14 #include "ash/shelf/shelf.h"
15 #include "ash/shelf/shelf_layout_manager.h"
16 #include "ash/shell.h"
17 #include "ash/shell_delegate.h"
18 #include "ash/shell_window_ids.h"
19 #include "ash/wm/mru_window_tracker.h"
20 #include "ash/wm/window_positioner.h"
21 #include "ash/wm/window_state.h"
22 #include "base/auto_reset.h"
23 #include "base/message_loop/message_loop.h"
24 #include "base/strings/string_util.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/chrome_notification_types.h"
27 #include "chrome/browser/chromeos/login/user_manager.h"
28 #include "chrome/browser/chromeos/login/wallpaper_manager.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
32 #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
33 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
34 #include "chrome/browser/ui/browser.h"
35 #include "chrome/browser/ui/browser_finder.h"
36 #include "chrome/browser/ui/browser_list.h"
37 #include "chrome/browser/ui/browser_window.h"
38 #include "content/public/browser/notification_service.h"
39 #include "google_apis/gaia/gaia_auth_util.h"
40 #include "ui/aura/client/activation_client.h"
41 #include "ui/aura/client/aura_constants.h"
42 #include "ui/aura/root_window.h"
43 #include "ui/aura/window.h"
44 #include "ui/base/ui_base_types.h"
45 #include "ui/events/event.h"
46 #include "ui/message_center/message_center.h"
47 #include "ui/views/corewm/transient_window_manager.h"
48 #include "ui/views/corewm/window_animations.h"
49 #include "ui/views/corewm/window_util.h"
53 // The animation time in milliseconds for a single window which is fading
55 static int kAnimationTimeMS
= 100;
57 // The animation time in millseconds for the fade in and / or out when switching
59 static int kUserFadeTimeMS
= 110;
61 // The animation time in ms for a window which get teleported to another screen.
62 static int kTeleportAnimationTimeMS
= 300;
64 // Checks if a given event is a user event.
65 bool IsUserEvent(ui::Event
* e
) {
67 ui::EventType type
= e
->type();
68 if (type
!= ui::ET_CANCEL_MODE
&&
69 type
!= ui::ET_UMA_DATA
&&
70 type
!= ui::ET_UNKNOWN
)
76 // Test if we are currently processing a user event which might lead to a
77 // browser / app creation.
78 bool IsProcessingUserEvent() {
79 // When there is a nested message loop (e.g. active menu or drag and drop
80 // operation) - we are in a nested loop and can ignore this.
81 // Note: Unit tests might not have a message loop.
82 base::MessageLoop
* message_loop
= base::MessageLoop::current();
83 if (message_loop
&& message_loop
->is_running() && message_loop
->IsNested())
86 // TODO(skuhne): "Open link in new window" will come here after the menu got
87 // closed, executing the command from the nested menu loop. However at that
88 // time there is no active event processed. A solution for that need to be
89 // found past M-32. A global event handler filter (pre and post) might fix
90 // that problem in conjunction with a depth counter - but - for the menu
91 // execution we come here after the loop was finished (so it's not nested
92 // anymore) and the root window should therefore still have the event which
93 // lead to the menu invocation, but it is not. By fixing that problem this
94 // would "magically work".
95 aura::Window::Windows root_window_list
= ash::Shell::GetAllRootWindows();
96 for (aura::Window::Windows::iterator it
= root_window_list
.begin();
97 it
!= root_window_list
.end();
99 if (IsUserEvent((*it
)->GetDispatcher()->current_event()))
105 // Records the type of window which was transferred to another desktop.
106 void RecordUMAForTransferredWindowType(aura::Window
* window
) {
107 // We need to figure out what kind of window this is to record the transfer.
108 Browser
* browser
= chrome::FindBrowserWithWindow(window
);
109 ash::MultiProfileUMA::TeleportWindowType window_type
=
110 ash::MultiProfileUMA::TELEPORT_WINDOW_UNKNOWN
;
112 if (browser
->profile()->IsOffTheRecord()) {
113 window_type
= ash::MultiProfileUMA::TELEPORT_WINDOW_INCOGNITO_BROWSER
;
114 } else if (browser
->is_app()) {
115 window_type
= ash::MultiProfileUMA::TELEPORT_WINDOW_V1_APP
;
116 } else if (browser
->is_type_popup()) {
117 window_type
= ash::MultiProfileUMA::TELEPORT_WINDOW_POPUP
;
119 window_type
= ash::MultiProfileUMA::TELEPORT_WINDOW_BROWSER
;
122 // Unit tests might come here without a profile manager.
123 if (!g_browser_process
->profile_manager())
125 // If it is not a browser, it is probably be a V2 application. In that case
126 // one of the AppWindowRegistry instances should know about it.
127 apps::AppWindow
* app_window
= NULL
;
128 std::vector
<Profile
*> profiles
=
129 g_browser_process
->profile_manager()->GetLoadedProfiles();
130 for (std::vector
<Profile
*>::iterator it
= profiles
.begin();
131 it
!= profiles
.end() && app_window
== NULL
;
133 app_window
= apps::AppWindowRegistry::Get(*it
)
134 ->GetAppWindowForNativeWindow(window
);
137 if (app_window
->window_type() == apps::AppWindow::WINDOW_TYPE_PANEL
||
138 app_window
->window_type() == apps::AppWindow::WINDOW_TYPE_V1_PANEL
) {
139 window_type
= ash::MultiProfileUMA::TELEPORT_WINDOW_PANEL
;
141 window_type
= ash::MultiProfileUMA::TELEPORT_WINDOW_V2_APP
;
145 ash::MultiProfileUMA::RecordTeleportWindowType(window_type
);
152 // A class to temporarily change the animation properties for a window.
153 class AnimationSetter
{
155 AnimationSetter(aura::Window
* window
, int animation_time_in_ms
)
157 previous_animation_type_(
158 views::corewm::GetWindowVisibilityAnimationType(window_
)),
159 previous_animation_time_(
160 views::corewm::GetWindowVisibilityAnimationDuration(*window_
)) {
161 views::corewm::SetWindowVisibilityAnimationType(
163 views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE
);
164 views::corewm::SetWindowVisibilityAnimationDuration(
166 base::TimeDelta::FromMilliseconds(animation_time_in_ms
));
170 views::corewm::SetWindowVisibilityAnimationType(window_
,
171 previous_animation_type_
);
172 views::corewm::SetWindowVisibilityAnimationDuration(
174 previous_animation_time_
);
178 // The window which gets used.
179 aura::Window
* window_
;
181 // Previous animation type.
182 const int previous_animation_type_
;
184 // Previous animation time.
185 const base::TimeDelta previous_animation_time_
;
187 DISALLOW_COPY_AND_ASSIGN(AnimationSetter
);
190 // logic while the user gets switched.
191 class UserChangeActionDisabler
{
193 UserChangeActionDisabler() {
194 ash::WindowPositioner::DisableAutoPositioning(true);
195 ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true);
198 ~UserChangeActionDisabler() {
199 ash::WindowPositioner::DisableAutoPositioning(false);
200 ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(
205 DISALLOW_COPY_AND_ASSIGN(UserChangeActionDisabler
);
208 // This class keeps track of all applications which were started for a user.
209 // When an app gets created, the window will be tagged for that user. Note
210 // that the destruction does not need to be tracked here since the universal
211 // window observer will take care of that.
212 class AppObserver
: public apps::AppWindowRegistry::Observer
{
214 explicit AppObserver(const std::string
& user_id
) : user_id_(user_id
) {}
215 virtual ~AppObserver() {}
217 // AppWindowRegistry::Observer overrides:
218 virtual void OnAppWindowAdded(apps::AppWindow
* app_window
) OVERRIDE
{
219 aura::Window
* window
= app_window
->GetNativeWindow();
221 MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(window
,
224 virtual void OnAppWindowIconChanged(apps::AppWindow
* app_window
) OVERRIDE
{}
225 virtual void OnAppWindowRemoved(apps::AppWindow
* app_window
) OVERRIDE
{}
228 std::string user_id_
;
230 DISALLOW_COPY_AND_ASSIGN(AppObserver
);
233 MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS(
234 const std::string
& current_user_id
)
235 : current_user_id_(current_user_id
),
236 notification_blocker_(new MultiUserNotificationBlockerChromeOS(
237 message_center::MessageCenter::Get(), this, current_user_id
)),
238 suppress_visibility_changes_(false),
239 animations_disabled_(false) {
240 // Add a session state observer to be able to monitor session changes.
241 if (ash::Shell::HasInstance())
242 ash::Shell::GetInstance()->session_state_delegate()->
243 AddSessionStateObserver(this);
245 // The BrowserListObserver would have been better to use then the old
246 // notification system, but that observer fires before the window got created.
247 registrar_
.Add(this, NOTIFICATION_BROWSER_WINDOW_READY
,
248 content::NotificationService::AllSources());
250 // Add an app window observer & all already running apps.
251 Profile
* profile
= multi_user_util::GetProfileFromUserID(current_user_id
);
256 MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() {
257 // Remove all window observers.
258 WindowToEntryMap::iterator window
= window_to_entry_
.begin();
259 while (window
!= window_to_entry_
.end()) {
260 OnWindowDestroyed(window
->first
);
261 window
= window_to_entry_
.begin();
264 // Remove all app observers.
265 UserIDToAppWindowObserver::iterator app_observer_iterator
=
266 user_id_to_app_observer_
.begin();
267 while (app_observer_iterator
!= user_id_to_app_observer_
.end()) {
268 Profile
* profile
= multi_user_util::GetProfileFromUserID(
269 app_observer_iterator
->first
);
271 apps::AppWindowRegistry::Get(profile
)
272 ->RemoveObserver(app_observer_iterator
->second
);
273 delete app_observer_iterator
->second
;
274 user_id_to_app_observer_
.erase(app_observer_iterator
);
275 app_observer_iterator
= user_id_to_app_observer_
.begin();
278 if (ash::Shell::HasInstance())
279 ash::Shell::GetInstance()->session_state_delegate()->
280 RemoveSessionStateObserver(this);
283 void MultiUserWindowManagerChromeOS::SetWindowOwner(
284 aura::Window
* window
,
285 const std::string
& user_id
) {
286 // Make sure the window is valid and there was no owner yet.
288 DCHECK(!user_id
.empty());
289 if (GetWindowOwner(window
) == user_id
)
291 DCHECK(GetWindowOwner(window
).empty());
292 window_to_entry_
[window
] = new WindowEntry(user_id
);
294 // Remember the initial visibility of the window.
295 window_to_entry_
[window
]->set_show(window
->IsVisible());
297 // Add observers to track state changes.
298 window
->AddObserver(this);
299 views::corewm::TransientWindowManager::Get(window
)->AddObserver(this);
301 // Check if this window was created due to a user interaction. If it was,
302 // transfer it to the current user.
303 if (IsProcessingUserEvent())
304 window_to_entry_
[window
]->set_show_for_user(current_user_id_
);
306 // Add all transient children to our set of windows. Note that the function
307 // will add the children but not the owner to the transient children map.
308 AddTransientOwnerRecursive(window
, window
);
310 // Notify entry adding.
311 FOR_EACH_OBSERVER(Observer
, observers_
, OnOwnerEntryAdded(window
));
313 if (!IsWindowOnDesktopOfUser(window
, current_user_id_
))
314 SetWindowVisibility(window
, false, 0);
317 const std::string
& MultiUserWindowManagerChromeOS::GetWindowOwner(
318 aura::Window
* window
) {
319 WindowToEntryMap::iterator it
= window_to_entry_
.find(window
);
320 return it
!= window_to_entry_
.end() ? it
->second
->owner()
321 : base::EmptyString();
324 void MultiUserWindowManagerChromeOS::ShowWindowForUser(
325 aura::Window
* window
,
326 const std::string
& user_id
) {
327 std::string
previous_owner(GetUserPresentingWindow(window
));
328 if (!ShowWindowForUserIntern(window
, user_id
))
330 // The window switched to a new desktop and we have to switch to that desktop,
331 // but only when it was on the visible desktop and the the target is not the
333 if (user_id
== current_user_id_
|| previous_owner
!= current_user_id_
)
336 ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser(
340 bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() {
341 WindowToEntryMap::iterator it
= window_to_entry_
.begin();
342 for (; it
!= window_to_entry_
.end(); ++it
) {
343 if (it
->second
->owner() != it
->second
->show_for_user())
349 void MultiUserWindowManagerChromeOS::GetOwnersOfVisibleWindows(
350 std::set
<std::string
>* user_ids
) {
351 for (WindowToEntryMap::iterator it
= window_to_entry_
.begin();
352 it
!= window_to_entry_
.end(); ++it
) {
353 if (it
->first
->IsVisible())
354 user_ids
->insert(it
->second
->owner());
358 bool MultiUserWindowManagerChromeOS::IsWindowOnDesktopOfUser(
359 aura::Window
* window
,
360 const std::string
& user_id
) {
361 const std::string
& presenting_user
= GetUserPresentingWindow(window
);
362 return presenting_user
.empty() || presenting_user
== user_id
;
365 const std::string
& MultiUserWindowManagerChromeOS::GetUserPresentingWindow(
366 aura::Window
* window
) {
367 WindowToEntryMap::iterator it
= window_to_entry_
.find(window
);
368 // If the window is not owned by anyone it is shown on all desktops and we
369 // return the empty string.
370 if (it
== window_to_entry_
.end())
371 return base::EmptyString();
372 // Otherwise we ask the object for its desktop.
373 return it
->second
->show_for_user();
376 void MultiUserWindowManagerChromeOS::AddUser(Profile
* profile
) {
377 const std::string
& user_id
= multi_user_util::GetUserIDFromProfile(profile
);
378 if (user_id_to_app_observer_
.find(user_id
) != user_id_to_app_observer_
.end())
381 user_id_to_app_observer_
[user_id
] = new AppObserver(user_id
);
382 apps::AppWindowRegistry::Get(profile
)
383 ->AddObserver(user_id_to_app_observer_
[user_id
]);
385 // Account all existing application windows of this user accordingly.
386 const apps::AppWindowRegistry::AppWindowList
& app_windows
=
387 apps::AppWindowRegistry::Get(profile
)->app_windows();
388 apps::AppWindowRegistry::AppWindowList::const_iterator it
=
390 for (; it
!= app_windows
.end(); ++it
)
391 user_id_to_app_observer_
[user_id
]->OnAppWindowAdded(*it
);
393 // Account all existing browser windows of this user accordingly.
394 BrowserList
* browser_list
= BrowserList::GetInstance(HOST_DESKTOP_TYPE_ASH
);
395 BrowserList::const_iterator browser_it
= browser_list
->begin();
396 for (; browser_it
!= browser_list
->end(); ++browser_it
) {
397 if ((*browser_it
)->profile()->GetOriginalProfile() == profile
)
398 AddBrowserWindow(*browser_it
);
402 void MultiUserWindowManagerChromeOS::AddObserver(Observer
* observer
) {
403 observers_
.AddObserver(observer
);
406 void MultiUserWindowManagerChromeOS::RemoveObserver(Observer
* observer
) {
407 observers_
.RemoveObserver(observer
);
410 void MultiUserWindowManagerChromeOS::ActiveUserChanged(
411 const std::string
& user_id
) {
412 DCHECK(user_id
!= current_user_id_
);
413 current_user_id_
= user_id
;
414 // If there is an animation in progress finish the pending switch which also
415 // kills the timer (if there is one).
416 if (user_changed_animation_timer_
.get())
417 TransitionUser(SHOW_NEW_USER
);
419 // Start the animation by hiding the old user.
420 TransitionUser(HIDE_OLD_USER
);
422 // If animations are disabled we immediately switch to the new user, otherwise
423 // we create a timer which will fade in the new user once the other user has
425 if (animations_disabled_
) {
426 TransitionUser(SHOW_NEW_USER
);
428 user_changed_animation_timer_
.reset(new base::Timer(
430 base::TimeDelta::FromMilliseconds(kUserFadeTimeMS
),
431 base::Bind(&MultiUserWindowManagerChromeOS::TransitionUser
,
432 base::Unretained(this),
435 user_changed_animation_timer_
->Reset();
439 void MultiUserWindowManagerChromeOS::OnWindowDestroyed(aura::Window
* window
) {
440 if (GetWindowOwner(window
).empty()) {
441 // This must be a window in the transient chain - remove it and its
442 // children from the owner.
443 RemoveTransientOwnerRecursive(window
);
446 views::corewm::TransientWindowManager::Get(window
)->RemoveObserver(this);
447 // Remove the window from the owners list.
448 delete window_to_entry_
[window
];
449 window_to_entry_
.erase(window
);
451 // Notify entry change.
452 FOR_EACH_OBSERVER(Observer
, observers_
, OnOwnerEntryRemoved(window
));
455 void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanging(
456 aura::Window
* window
, bool visible
) {
457 // This command gets called first and immediately when show or hide gets
458 // called. We remember here the desired state for restoration IF we were
459 // not ourselves issuing the call.
460 // Note also that using the OnWindowVisibilityChanged callback cannot be
462 if (suppress_visibility_changes_
)
465 WindowToEntryMap::iterator it
= window_to_entry_
.find(window
);
466 // If the window is not owned by anyone it is shown on all desktops.
467 if (it
!= window_to_entry_
.end()) {
468 // Remember what was asked for so that we can restore this when the user's
469 // desktop gets restored.
470 it
->second
->set_show(visible
);
472 TransientWindowToVisibility::iterator it
=
473 transient_window_to_visibility_
.find(window
);
474 if (it
!= transient_window_to_visibility_
.end())
475 it
->second
= visible
;
479 void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanged(
480 aura::Window
* window
, bool visible
) {
481 if (suppress_visibility_changes_
)
484 // Don't allow to make the window visible if it shouldn't be.
485 if (visible
&& !IsWindowOnDesktopOfUser(window
, current_user_id_
)) {
486 SetWindowVisibility(window
, false, 0);
489 aura::Window
* owned_parent
= GetOwningWindowInTransientChain(window
);
490 if (owned_parent
&& owned_parent
!= window
&& visible
&&
491 !IsWindowOnDesktopOfUser(owned_parent
, current_user_id_
))
492 SetWindowVisibility(window
, false, 0);
495 void MultiUserWindowManagerChromeOS::OnTransientChildAdded(
496 aura::Window
* window
,
497 aura::Window
* transient_window
) {
498 if (!GetWindowOwner(window
).empty()) {
499 AddTransientOwnerRecursive(transient_window
, window
);
502 aura::Window
* owned_parent
=
503 GetOwningWindowInTransientChain(transient_window
);
507 AddTransientOwnerRecursive(transient_window
, owned_parent
);
510 void MultiUserWindowManagerChromeOS::OnTransientChildRemoved(
511 aura::Window
* window
,
512 aura::Window
* transient_window
) {
513 // Remove the transient child if the window itself is owned, or one of the
514 // windows in its transient parents chain.
515 if (!GetWindowOwner(window
).empty() ||
516 GetOwningWindowInTransientChain(window
))
517 RemoveTransientOwnerRecursive(transient_window
);
520 void MultiUserWindowManagerChromeOS::Observe(
522 const content::NotificationSource
& source
,
523 const content::NotificationDetails
& details
) {
524 if (type
== NOTIFICATION_BROWSER_WINDOW_READY
)
525 AddBrowserWindow(content::Source
<Browser
>(source
).ptr());
528 void MultiUserWindowManagerChromeOS::SetAnimationsForTest(bool disable
) {
529 animations_disabled_
= disable
;
532 bool MultiUserWindowManagerChromeOS::IsAnimationRunningForTest() {
533 return user_changed_animation_timer_
.get() != NULL
;
536 const std::string
& MultiUserWindowManagerChromeOS::GetCurrentUserForTest() {
537 return current_user_id_
;
540 bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern(
541 aura::Window
* window
,
542 const std::string
& user_id
) {
543 // If there is either no owner, or the owner is the current user, no action
545 const std::string
& owner
= GetWindowOwner(window
);
547 (owner
== user_id
&& IsWindowOnDesktopOfUser(window
, user_id
)))
550 bool minimized
= ash::wm::GetWindowState(window
)->IsMinimized();
551 // Check that we are not trying to transfer ownership of a minimized window.
552 if (user_id
!= owner
&& minimized
)
556 // If it is minimized it falls back to the original desktop.
557 ash::MultiProfileUMA::RecordTeleportAction(
558 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_MINIMIZE
);
560 // If the window was transferred without getting minimized, we should record
562 RecordUMAForTransferredWindowType(window
);
565 WindowToEntryMap::iterator it
= window_to_entry_
.find(window
);
566 it
->second
->set_show_for_user(user_id
);
568 // Show the window if the added user is the current one.
569 if (user_id
== current_user_id_
) {
570 // Only show the window if it should be shown according to its state.
571 if (it
->second
->show())
572 SetWindowVisibility(window
, true, kTeleportAnimationTimeMS
);
574 SetWindowVisibility(window
, false, kTeleportAnimationTimeMS
);
577 // Notify entry change.
578 FOR_EACH_OBSERVER(Observer
, observers_
, OnOwnerEntryChanged(window
));
582 void MultiUserWindowManagerChromeOS::TransitionUser(
583 MultiUserWindowManagerChromeOS::AnimationStep animation_step
) {
584 TransitionWallpaper(animation_step
);
585 TransitionUserShelf(animation_step
);
587 // Disable the window position manager and the MRU window tracker temporarily.
588 scoped_ptr
<UserChangeActionDisabler
> disabler(new UserChangeActionDisabler
);
590 // We need to show/hide the windows in the same order as they were created in
591 // their parent window(s) to keep the layer / window hierarchy in sync. To
592 // achieve that we first collect all parent windows and then enumerate all
593 // windows in those parent windows and show or hide them accordingly.
595 // Create a list of all parent windows we have to check and their parents.
596 std::set
<aura::Window
*> parent_list
;
597 for (WindowToEntryMap::iterator it
= window_to_entry_
.begin();
598 it
!= window_to_entry_
.end(); ++it
) {
599 aura::Window
* parent
= it
->first
->parent();
600 if (parent_list
.find(parent
) == parent_list
.end())
601 parent_list
.insert(parent
);
604 // Traverse the found parent windows to handle their child windows in order of
606 for (std::set
<aura::Window
*>::iterator it_parents
= parent_list
.begin();
607 it_parents
!= parent_list
.end(); ++it_parents
) {
608 const aura::Window::Windows window_list
= (*it_parents
)->children();
609 for (aura::Window::Windows::const_iterator it_window
= window_list
.begin();
610 it_window
!= window_list
.end(); ++it_window
) {
611 aura::Window
* window
= *it_window
;
612 WindowToEntryMap::iterator it_map
= window_to_entry_
.find(window
);
613 if (it_map
!= window_to_entry_
.end()) {
614 bool should_be_visible
=
615 it_map
->second
->show_for_user() == current_user_id_
&&
616 it_map
->second
->show();
617 bool is_visible
= window
->IsVisible();
618 ash::wm::WindowState
* window_state
= ash::wm::GetWindowState(window
);
619 if (animation_step
== SHOW_NEW_USER
&&
620 it_map
->second
->owner() == current_user_id_
&&
621 it_map
->second
->show_for_user() != current_user_id_
&&
622 window_state
->IsMinimized()) {
623 // Pull back minimized visiting windows to the owners desktop.
624 ShowWindowForUserIntern(window
, current_user_id_
);
625 window_state
->Unminimize();
626 } else if (should_be_visible
!= is_visible
&&
627 should_be_visible
== (animation_step
== SHOW_NEW_USER
)) {
628 SetWindowVisibility(window
, should_be_visible
, kUserFadeTimeMS
);
634 // Activation and real switch are happening after the other user gets shown.
635 if (animation_step
== SHOW_NEW_USER
) {
636 // Finally we need to restore the previously active window.
637 ash::MruWindowTracker::WindowList mru_list
=
638 ash::Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList();
639 if (mru_list
.size()) {
640 aura::Window
* window
= mru_list
[0];
641 ash::wm::WindowState
* window_state
= ash::wm::GetWindowState(window
);
642 if (IsWindowOnDesktopOfUser(window
, current_user_id_
) &&
643 !window_state
->IsMinimized()) {
644 aura::client::ActivationClient
* client
=
645 aura::client::GetActivationClient(window
->GetRootWindow());
646 // Several unit tests come here without an activation client.
648 client
->ActivateWindow(window
);
652 // This is called directly here to make sure notification_blocker will see
653 // the new window status.
654 notification_blocker_
->ActiveUserChanged(current_user_id_
);
656 // We can reset the timer at this point.
657 // Note: The timer can be destroyed while it is performing its task.
658 user_changed_animation_timer_
.reset();
662 void MultiUserWindowManagerChromeOS::TransitionWallpaper(
663 MultiUserWindowManagerChromeOS::AnimationStep animation_step
) {
664 // Handle the wallpaper switch.
665 if (chromeos::WallpaperManager::Get()) {
666 ash::UserWallpaperDelegate
* wallpaper_delegate
=
667 ash::Shell::GetInstance()->user_wallpaper_delegate();
668 if (animation_step
== HIDE_OLD_USER
) {
669 // Set the wallpaper cross dissolve animation duration to our complete
670 // animation cycle for a fade in and fade out.
671 wallpaper_delegate
->SetAnimationDurationOverride(2 * kUserFadeTimeMS
);
672 chromeos::WallpaperManager::Get()->SetUserWallpaperDelayed(
675 // Revert the wallpaper cross dissolve animation duration back to the
677 wallpaper_delegate
->SetAnimationDurationOverride(0);
682 void MultiUserWindowManagerChromeOS::TransitionUserShelf(
683 MultiUserWindowManagerChromeOS::AnimationStep animation_step
) {
684 // The shelf animation duration override.
685 int duration_override
= kUserFadeTimeMS
;
686 // Handle the shelf order of items. This is done once the old user is hidden.
687 if (animation_step
== SHOW_NEW_USER
) {
688 // Some unit tests have no ChromeLauncherController.
689 if (ChromeLauncherController::instance())
690 ChromeLauncherController::instance()->ActiveUserChanged(current_user_id_
);
691 // We kicked off the shelf animation in the command above. As such we can
692 // disable the override now again.
693 duration_override
= 0;
696 if (animations_disabled_
)
699 ash::Shell::RootWindowControllerList controller
=
700 ash::Shell::GetInstance()->GetAllRootWindowControllers();
701 for (ash::Shell::RootWindowControllerList::iterator it1
= controller
.begin();
702 it1
!= controller
.end(); ++it1
) {
703 (*it1
)->GetShelfLayoutManager()->SetAnimationDurationOverride(
707 // For each root window hide the shelf.
708 if (animation_step
== HIDE_OLD_USER
) {
709 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
710 for (aura::Window::Windows::const_iterator iter
= root_windows
.begin();
711 iter
!= root_windows
.end(); ++iter
) {
712 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
713 ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN
, *iter
);
718 void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser
* browser
) {
719 // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can
720 // come here with no valid window.
721 if (!browser
->window() || !browser
->window()->GetNativeWindow())
723 SetWindowOwner(browser
->window()->GetNativeWindow(),
724 multi_user_util::GetUserIDFromProfile(browser
->profile()));
727 void MultiUserWindowManagerChromeOS::SetWindowVisibility(
728 aura::Window
* window
, bool visible
, int animation_time_in_ms
) {
729 if (window
->IsVisible() == visible
)
732 // Hiding a system modal dialog should not be allowed. Instead we switch to
733 // the user which is showing the system modal window.
734 // Note that in some cases (e.g. unit test) windows might not have a root
736 if (!visible
&& window
->GetRootWindow()) {
737 // Get the system modal container for the window's root window.
738 aura::Window
* system_modal_container
=
739 window
->GetRootWindow()->GetChildById(
740 ash::internal::kShellWindowId_SystemModalContainer
);
741 if (window
->parent() == system_modal_container
) {
742 // The window is system modal and we need to find the parent which owns
743 // it so that we can switch to the desktop accordingly.
744 std::string user_id
= GetUserPresentingWindow(window
);
745 if (user_id
.empty()) {
746 aura::Window
* owning_window
= GetOwningWindowInTransientChain(window
);
747 DCHECK(owning_window
);
748 user_id
= GetUserPresentingWindow(owning_window
);
749 DCHECK(!user_id
.empty());
751 ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser(
757 // To avoid that these commands are recorded as any other commands, we are
758 // suppressing any window entry changes while this is going on.
759 base::AutoReset
<bool> suppressor(&suppress_visibility_changes_
, true);
762 ShowWithTransientChildrenRecursive(window
, animation_time_in_ms
);
764 if (window
->HasFocus())
766 SetWindowVisible(window
, false, animation_time_in_ms
);
770 void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive(
771 aura::Window
* window
, int animation_time_in_ms
) {
772 aura::Window::Windows::const_iterator it
=
773 views::corewm::GetTransientChildren(window
).begin();
774 for (; it
!= views::corewm::GetTransientChildren(window
).end(); ++it
)
775 ShowWithTransientChildrenRecursive(*it
, animation_time_in_ms
);
777 // We show all children which were not explicitly hidden.
778 TransientWindowToVisibility::iterator it2
=
779 transient_window_to_visibility_
.find(window
);
780 if (it2
== transient_window_to_visibility_
.end() || it2
->second
)
781 SetWindowVisible(window
, true, animation_time_in_ms
);
784 aura::Window
* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain(
785 aura::Window
* window
) {
786 if (!GetWindowOwner(window
).empty())
788 aura::Window
* parent
= views::corewm::GetTransientParent(window
);
790 if (!GetWindowOwner(parent
).empty())
792 parent
= views::corewm::GetTransientParent(parent
);
797 void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive(
798 aura::Window
* window
,
799 aura::Window
* owned_parent
) {
800 // First add all child windows.
801 aura::Window::Windows::const_iterator it
=
802 views::corewm::GetTransientChildren(window
).begin();
803 for (; it
!= views::corewm::GetTransientChildren(window
).end(); ++it
)
804 AddTransientOwnerRecursive(*it
, owned_parent
);
806 // If this window is the owned window, we do not have to handle it again.
807 if (window
== owned_parent
)
810 // Remember the current visibility.
811 DCHECK(transient_window_to_visibility_
.find(window
) ==
812 transient_window_to_visibility_
.end());
813 transient_window_to_visibility_
[window
] = window
->IsVisible();
815 // Add observers to track state changes.
816 window
->AddObserver(this);
817 views::corewm::TransientWindowManager::Get(window
)->AddObserver(this);
819 // Hide the window if it should not be shown. Note that this hide operation
820 // will hide recursively this and all children - but we have already collected
821 // their initial view state.
822 if (!IsWindowOnDesktopOfUser(owned_parent
, current_user_id_
))
823 SetWindowVisibility(window
, false, kAnimationTimeMS
);
826 void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive(
827 aura::Window
* window
) {
828 // First remove all child windows.
829 aura::Window::Windows::const_iterator it
=
830 views::corewm::GetTransientChildren(window
).begin();
831 for (; it
!= views::corewm::GetTransientChildren(window
).end(); ++it
)
832 RemoveTransientOwnerRecursive(*it
);
834 // Find from transient window storage the visibility for the given window,
835 // set the visibility accordingly and delete the window from the map.
836 TransientWindowToVisibility::iterator visibility_item
=
837 transient_window_to_visibility_
.find(window
);
838 DCHECK(visibility_item
!= transient_window_to_visibility_
.end());
840 window
->RemoveObserver(this);
841 views::corewm::TransientWindowManager::Get(window
)->RemoveObserver(this);
843 bool unowned_view_state
= visibility_item
->second
;
844 transient_window_to_visibility_
.erase(visibility_item
);
845 if (unowned_view_state
&& !window
->IsVisible()) {
846 // To prevent these commands from being recorded as any other commands, we
847 // are suppressing any window entry changes while this is going on.
848 // Instead of calling SetWindowVisible, only show gets called here since all
849 // dependents have been shown previously already.
850 base::AutoReset
<bool> suppressor(&suppress_visibility_changes_
, true);
855 void MultiUserWindowManagerChromeOS::SetWindowVisible(
856 aura::Window
* window
,
858 int animation_time_in_ms
) {
859 AnimationSetter
animation_setter(
861 animations_disabled_
? 0 : animation_time_in_ms
);
868 // Make sure that animations have no influence on the window state after the
870 DCHECK_EQ(visible
, window
->IsVisible());
873 } // namespace chrome