NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / ui / ash / multi_user / multi_user_window_manager_chromeos.cc
blob80e74bf02e8b7d9fbc20f69e1f98937076cfef13
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"
51 namespace {
53 // The animation time in milliseconds for a single window which is fading
54 // in / out.
55 static int kAnimationTimeMS = 100;
57 // The animation time in millseconds for the fade in and / or out when switching
58 // users.
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) {
66 if (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)
71 return true;
73 return false;
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())
84 return false;
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();
98 ++it) {
99 if (IsUserEvent((*it)->GetDispatcher()->current_event()))
100 return true;
102 return false;
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;
111 if (browser) {
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;
118 } else {
119 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_BROWSER;
121 } else {
122 // Unit tests might come here without a profile manager.
123 if (!g_browser_process->profile_manager())
124 return;
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;
132 it++) {
133 app_window = apps::AppWindowRegistry::Get(*it)
134 ->GetAppWindowForNativeWindow(window);
136 if (app_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;
140 } else {
141 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V2_APP;
145 ash::MultiProfileUMA::RecordTeleportWindowType(window_type);
148 } // namespace
150 namespace chrome {
152 // A class to temporarily change the animation properties for a window.
153 class AnimationSetter {
154 public:
155 AnimationSetter(aura::Window* window, int animation_time_in_ms)
156 : window_(window),
157 previous_animation_type_(
158 views::corewm::GetWindowVisibilityAnimationType(window_)),
159 previous_animation_time_(
160 views::corewm::GetWindowVisibilityAnimationDuration(*window_)) {
161 views::corewm::SetWindowVisibilityAnimationType(
162 window_,
163 views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
164 views::corewm::SetWindowVisibilityAnimationDuration(
165 window_,
166 base::TimeDelta::FromMilliseconds(animation_time_in_ms));
169 ~AnimationSetter() {
170 views::corewm::SetWindowVisibilityAnimationType(window_,
171 previous_animation_type_);
172 views::corewm::SetWindowVisibilityAnimationDuration(
173 window_,
174 previous_animation_time_);
177 private:
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 {
192 public:
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(
201 false);
203 private:
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 {
213 public:
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();
220 DCHECK(window);
221 MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(window,
222 user_id_);
224 virtual void OnAppWindowIconChanged(apps::AppWindow* app_window) OVERRIDE {}
225 virtual void OnAppWindowRemoved(apps::AppWindow* app_window) OVERRIDE {}
227 private:
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);
252 if (profile)
253 AddUser(profile);
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);
270 DCHECK(profile);
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.
287 DCHECK(window);
288 DCHECK(!user_id.empty());
289 if (GetWindowOwner(window) == user_id)
290 return;
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))
329 return;
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
332 // visible desktop.
333 if (user_id == current_user_id_ || previous_owner != current_user_id_)
334 return;
336 ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser(
337 user_id);
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())
344 return true;
346 return false;
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())
379 return;
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 =
389 app_windows.begin();
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
424 // been faded away.
425 if (animations_disabled_) {
426 TransitionUser(SHOW_NEW_USER);
427 } else {
428 user_changed_animation_timer_.reset(new base::Timer(
429 FROM_HERE,
430 base::TimeDelta::FromMilliseconds(kUserFadeTimeMS),
431 base::Bind(&MultiUserWindowManagerChromeOS::TransitionUser,
432 base::Unretained(this),
433 SHOW_NEW_USER),
434 false));
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);
444 return;
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
461 // used for this.
462 if (suppress_visibility_changes_)
463 return;
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);
471 } else {
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_)
482 return;
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);
487 return;
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);
500 return;
502 aura::Window* owned_parent =
503 GetOwningWindowInTransientChain(transient_window);
504 if (!owned_parent)
505 return;
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(
521 int type,
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
544 // is required.
545 const std::string& owner = GetWindowOwner(window);
546 if (owner.empty() ||
547 (owner == user_id && IsWindowOnDesktopOfUser(window, user_id)))
548 return false;
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)
553 return false;
555 if (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);
559 } else {
560 // If the window was transferred without getting minimized, we should record
561 // the window type.
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);
573 } else {
574 SetWindowVisibility(window, false, kTeleportAnimationTimeMS);
577 // Notify entry change.
578 FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryChanged(window));
579 return true;
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
605 // their appearance.
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.
647 if (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(
673 current_user_id_);
674 } else {
675 // Revert the wallpaper cross dissolve animation duration back to the
676 // default.
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_)
697 return;
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(
704 duration_override);
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())
722 return;
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)
730 return;
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
735 // window.
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(
752 user_id);
753 return;
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);
761 if (visible) {
762 ShowWithTransientChildrenRecursive(window, animation_time_in_ms);
763 } else {
764 if (window->HasFocus())
765 window->Blur();
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())
787 return NULL;
788 aura::Window* parent = views::corewm::GetTransientParent(window);
789 while (parent) {
790 if (!GetWindowOwner(parent).empty())
791 return parent;
792 parent = views::corewm::GetTransientParent(parent);
794 return NULL;
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)
808 return;
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);
851 window->Show();
855 void MultiUserWindowManagerChromeOS::SetWindowVisible(
856 aura::Window* window,
857 bool visible,
858 int animation_time_in_ms) {
859 AnimationSetter animation_setter(
860 window,
861 animations_disabled_ ? 0 : animation_time_in_ms);
863 if (visible)
864 window->Show();
865 else
866 window->Hide();
868 // Make sure that animations have no influence on the window state after the
869 // call.
870 DCHECK_EQ(visible, window->IsVisible());
873 } // namespace chrome