Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / ash / multi_user / multi_user_window_manager_chromeos.cc
blob16e4113cc855b001876e20a7c39d0fec324c87d0
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 "ash/ash_switches.h"
8 #include "ash/multi_profile_uma.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/session/session_state_delegate.h"
11 #include "ash/shelf/shelf.h"
12 #include "ash/shell.h"
13 #include "ash/shell_delegate.h"
14 #include "ash/shell_window_ids.h"
15 #include "ash/system/tray/system_tray_notifier.h"
16 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
17 #include "ash/wm/window_state.h"
18 #include "base/auto_reset.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/strings/string_util.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/profiles/profile_manager.h"
26 #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
27 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
28 #include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h"
29 #include "chrome/browser/ui/browser.h"
30 #include "chrome/browser/ui/browser_finder.h"
31 #include "chrome/browser/ui/browser_list.h"
32 #include "chrome/browser/ui/browser_window.h"
33 #include "content/public/browser/notification_service.h"
34 #include "extensions/browser/app_window/app_window.h"
35 #include "extensions/browser/app_window/app_window_registry.h"
36 #include "google_apis/gaia/gaia_auth_util.h"
37 #include "ui/aura/client/aura_constants.h"
38 #include "ui/aura/window.h"
39 #include "ui/aura/window_event_dispatcher.h"
40 #include "ui/base/ui_base_types.h"
41 #include "ui/events/event.h"
42 #include "ui/message_center/message_center.h"
43 #include "ui/wm/core/transient_window_manager.h"
44 #include "ui/wm/core/window_animations.h"
45 #include "ui/wm/core/window_util.h"
47 namespace {
49 // The animation time in milliseconds for a single window which is fading
50 // in / out.
51 const int kAnimationTimeMS = 100;
53 // The animation time in milliseconds for the fade in and / or out when
54 // switching users.
55 const int kUserFadeTimeMS = 110;
57 // The animation time in ms for a window which get teleported to another screen.
58 const int kTeleportAnimationTimeMS = 300;
60 // Checks if a given event is a user event.
61 bool IsUserEvent(const ui::Event* e) {
62 if (e) {
63 ui::EventType type = e->type();
64 if (type != ui::ET_CANCEL_MODE &&
65 type != ui::ET_UMA_DATA &&
66 type != ui::ET_UNKNOWN)
67 return true;
69 return false;
72 // Test if we are currently processing a user event which might lead to a
73 // browser / app creation.
74 bool IsProcessingUserEvent() {
75 // When there is a nested message loop (e.g. active menu or drag and drop
76 // operation) - we are in a nested loop and can ignore this.
77 // Note: Unit tests might not have a message loop.
78 base::MessageLoop* message_loop = base::MessageLoop::current();
79 if (message_loop && message_loop->is_running() && message_loop->IsNested())
80 return false;
82 // TODO(skuhne): "Open link in new window" will come here after the menu got
83 // closed, executing the command from the nested menu loop. However at that
84 // time there is no active event processed. A solution for that need to be
85 // found past M-32. A global event handler filter (pre and post) might fix
86 // that problem in conjunction with a depth counter - but - for the menu
87 // execution we come here after the loop was finished (so it's not nested
88 // anymore) and the root window should therefore still have the event which
89 // lead to the menu invocation, but it is not. By fixing that problem this
90 // would "magically work".
91 aura::Window::Windows root_window_list = ash::Shell::GetAllRootWindows();
92 for (aura::Window::Windows::iterator it = root_window_list.begin();
93 it != root_window_list.end();
94 ++it) {
95 if (IsUserEvent((*it)->GetHost()->dispatcher()->current_event()))
96 return true;
98 return false;
101 // Records the type of window which was transferred to another desktop.
102 void RecordUMAForTransferredWindowType(aura::Window* window) {
103 // We need to figure out what kind of window this is to record the transfer.
104 Browser* browser = chrome::FindBrowserWithWindow(window);
105 ash::MultiProfileUMA::TeleportWindowType window_type =
106 ash::MultiProfileUMA::TELEPORT_WINDOW_UNKNOWN;
107 if (browser) {
108 if (browser->profile()->IsOffTheRecord()) {
109 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_INCOGNITO_BROWSER;
110 } else if (browser->is_app()) {
111 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V1_APP;
112 } else if (browser->is_type_popup()) {
113 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_POPUP;
114 } else {
115 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_BROWSER;
117 } else {
118 // Unit tests might come here without a profile manager.
119 if (!g_browser_process->profile_manager())
120 return;
121 // If it is not a browser, it is probably be a V2 application. In that case
122 // one of the AppWindowRegistry instances should know about it.
123 extensions::AppWindow* app_window = NULL;
124 std::vector<Profile*> profiles =
125 g_browser_process->profile_manager()->GetLoadedProfiles();
126 for (std::vector<Profile*>::iterator it = profiles.begin();
127 it != profiles.end() && app_window == NULL;
128 it++) {
129 app_window = extensions::AppWindowRegistry::Get(*it)
130 ->GetAppWindowForNativeWindow(window);
132 if (app_window) {
133 if (app_window->window_type() ==
134 extensions::AppWindow::WINDOW_TYPE_PANEL ||
135 app_window->window_type() ==
136 extensions::AppWindow::WINDOW_TYPE_V1_PANEL) {
137 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_PANEL;
138 } else {
139 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V2_APP;
143 ash::MultiProfileUMA::RecordTeleportWindowType(window_type);
146 } // namespace
148 namespace chrome {
150 // A class to temporarily change the animation properties for a window.
151 class AnimationSetter {
152 public:
153 AnimationSetter(aura::Window* window, int animation_time_in_ms)
154 : window_(window),
155 previous_animation_type_(
156 wm::GetWindowVisibilityAnimationType(window_)),
157 previous_animation_time_(
158 wm::GetWindowVisibilityAnimationDuration(*window_)) {
159 wm::SetWindowVisibilityAnimationType(
160 window_,
161 wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
162 wm::SetWindowVisibilityAnimationDuration(
163 window_,
164 base::TimeDelta::FromMilliseconds(animation_time_in_ms));
167 ~AnimationSetter() {
168 wm::SetWindowVisibilityAnimationType(window_,
169 previous_animation_type_);
170 wm::SetWindowVisibilityAnimationDuration(
171 window_,
172 previous_animation_time_);
175 private:
176 // The window which gets used.
177 aura::Window* window_;
179 // Previous animation type.
180 const int previous_animation_type_;
182 // Previous animation time.
183 const base::TimeDelta previous_animation_time_;
185 DISALLOW_COPY_AND_ASSIGN(AnimationSetter);
188 // This class keeps track of all applications which were started for a user.
189 // When an app gets created, the window will be tagged for that user. Note
190 // that the destruction does not need to be tracked here since the universal
191 // window observer will take care of that.
192 class AppObserver : public extensions::AppWindowRegistry::Observer {
193 public:
194 explicit AppObserver(const std::string& user_id) : user_id_(user_id) {}
195 ~AppObserver() override {}
197 // AppWindowRegistry::Observer overrides:
198 void OnAppWindowAdded(extensions::AppWindow* app_window) override {
199 aura::Window* window = app_window->GetNativeWindow();
200 DCHECK(window);
201 MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(window,
202 user_id_);
205 private:
206 std::string user_id_;
208 DISALLOW_COPY_AND_ASSIGN(AppObserver);
211 MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS(
212 const std::string& current_user_id)
213 : current_user_id_(current_user_id),
214 notification_blocker_(new MultiUserNotificationBlockerChromeOS(
215 message_center::MessageCenter::Get(), current_user_id)),
216 suppress_visibility_changes_(false),
217 animation_speed_(ANIMATION_SPEED_NORMAL) {
220 MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() {
221 // When the MultiUserWindowManager gets destroyed, ash::Shell is mostly gone.
222 // As such we should not try to finalize any outstanding user animations.
223 // Note that the destruction of the object can be done later.
224 if (animation_.get())
225 animation_->CancelAnimation();
227 // Remove all window observers.
228 WindowToEntryMap::iterator window = window_to_entry_.begin();
229 while (window != window_to_entry_.end()) {
230 OnWindowDestroyed(window->first);
231 window = window_to_entry_.begin();
234 // Remove all app observers.
235 UserIDToAppWindowObserver::iterator app_observer_iterator =
236 user_id_to_app_observer_.begin();
237 while (app_observer_iterator != user_id_to_app_observer_.end()) {
238 Profile* profile = multi_user_util::GetProfileFromUserID(
239 app_observer_iterator->first);
240 CHECK(profile) << "profile not found for:" << app_observer_iterator->first;
241 extensions::AppWindowRegistry::Get(profile)
242 ->RemoveObserver(app_observer_iterator->second);
243 delete app_observer_iterator->second;
244 user_id_to_app_observer_.erase(app_observer_iterator);
245 app_observer_iterator = user_id_to_app_observer_.begin();
248 if (ash::Shell::HasInstance())
249 ash::Shell::GetInstance()->session_state_delegate()->
250 RemoveSessionStateObserver(this);
253 void MultiUserWindowManagerChromeOS::Init() {
254 // Since we are setting the SessionStateObserver and adding the user, this
255 // function should get called only once.
256 DCHECK(user_id_to_app_observer_.find(current_user_id_) ==
257 user_id_to_app_observer_.end());
259 // Add a session state observer to be able to monitor session changes.
260 if (ash::Shell::HasInstance()) {
261 ash::Shell::GetInstance()->session_state_delegate()->
262 AddSessionStateObserver(this);
265 // The BrowserListObserver would have been better to use then the old
266 // notification system, but that observer fires before the window got created.
267 registrar_.Add(this, NOTIFICATION_BROWSER_WINDOW_READY,
268 content::NotificationService::AllSources());
270 // Add an app window observer & all already running apps.
271 Profile* profile = multi_user_util::GetProfileFromUserID(current_user_id_);
272 if (profile)
273 AddUser(profile);
276 void MultiUserWindowManagerChromeOS::SetWindowOwner(
277 aura::Window* window,
278 const std::string& user_id) {
279 // Make sure the window is valid and there was no owner yet.
280 DCHECK(window);
281 DCHECK(!user_id.empty());
282 if (GetWindowOwner(window) == user_id)
283 return;
284 DCHECK(GetWindowOwner(window).empty());
285 window_to_entry_[window] = new WindowEntry(user_id);
287 // Remember the initial visibility of the window.
288 window_to_entry_[window]->set_show(window->IsVisible());
290 // Add observers to track state changes.
291 window->AddObserver(this);
292 wm::TransientWindowManager::Get(window)->AddObserver(this);
294 // Check if this window was created due to a user interaction. If it was,
295 // transfer it to the current user.
296 if (IsProcessingUserEvent())
297 window_to_entry_[window]->set_show_for_user(current_user_id_);
299 // Add all transient children to our set of windows. Note that the function
300 // will add the children but not the owner to the transient children map.
301 AddTransientOwnerRecursive(window, window);
303 // Notify entry adding.
304 FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryAdded(window));
306 if (!IsWindowOnDesktopOfUser(window, current_user_id_))
307 SetWindowVisibility(window, false, 0);
310 const std::string& MultiUserWindowManagerChromeOS::GetWindowOwner(
311 aura::Window* window) const {
312 WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
313 return it != window_to_entry_.end() ? it->second->owner()
314 : base::EmptyString();
317 void MultiUserWindowManagerChromeOS::ShowWindowForUser(
318 aura::Window* window,
319 const std::string& user_id) {
320 std::string previous_owner(GetUserPresentingWindow(window));
321 if (!ShowWindowForUserIntern(window, user_id))
322 return;
323 // The window switched to a new desktop and we have to switch to that desktop,
324 // but only when it was on the visible desktop and the the target is not the
325 // visible desktop.
326 if (user_id == current_user_id_ || previous_owner != current_user_id_)
327 return;
329 ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser(
330 user_id);
333 bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() const {
334 WindowToEntryMap::const_iterator it = window_to_entry_.begin();
335 for (; it != window_to_entry_.end(); ++it) {
336 if (it->second->owner() != it->second->show_for_user())
337 return true;
339 return false;
342 void MultiUserWindowManagerChromeOS::GetOwnersOfVisibleWindows(
343 std::set<std::string>* user_ids) const {
344 for (WindowToEntryMap::const_iterator it = window_to_entry_.begin();
345 it != window_to_entry_.end();
346 ++it) {
347 if (it->first->IsVisible())
348 user_ids->insert(it->second->owner());
352 bool MultiUserWindowManagerChromeOS::IsWindowOnDesktopOfUser(
353 aura::Window* window,
354 const std::string& user_id) const {
355 const std::string& presenting_user = GetUserPresentingWindow(window);
356 return presenting_user.empty() || presenting_user == user_id;
359 const std::string& MultiUserWindowManagerChromeOS::GetUserPresentingWindow(
360 aura::Window* window) const {
361 WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
362 // If the window is not owned by anyone it is shown on all desktops and we
363 // return the empty string.
364 if (it == window_to_entry_.end())
365 return base::EmptyString();
366 // Otherwise we ask the object for its desktop.
367 return it->second->show_for_user();
370 void MultiUserWindowManagerChromeOS::AddUser(content::BrowserContext* context) {
371 Profile* profile = Profile::FromBrowserContext(context);
372 const std::string& user_id = multi_user_util::GetUserIDFromProfile(profile);
373 if (user_id_to_app_observer_.find(user_id) != user_id_to_app_observer_.end())
374 return;
376 user_id_to_app_observer_[user_id] = new AppObserver(user_id);
377 extensions::AppWindowRegistry::Get(profile)
378 ->AddObserver(user_id_to_app_observer_[user_id]);
380 // Account all existing application windows of this user accordingly.
381 const extensions::AppWindowRegistry::AppWindowList& app_windows =
382 extensions::AppWindowRegistry::Get(profile)->app_windows();
383 extensions::AppWindowRegistry::AppWindowList::const_iterator it =
384 app_windows.begin();
385 for (; it != app_windows.end(); ++it)
386 user_id_to_app_observer_[user_id]->OnAppWindowAdded(*it);
388 // Account all existing browser windows of this user accordingly.
389 BrowserList* browser_list = BrowserList::GetInstance(HOST_DESKTOP_TYPE_ASH);
390 BrowserList::const_iterator browser_it = browser_list->begin();
391 for (; browser_it != browser_list->end(); ++browser_it) {
392 if ((*browser_it)->profile()->GetOriginalProfile() == profile)
393 AddBrowserWindow(*browser_it);
395 // When adding another user to the session, we auto switch users.
396 if (user_id_to_app_observer_.size() == 1)
397 return;
399 // Don't do anything special in case of user session restore after crash.
400 // In that case session restore process will automatically switch to the
401 // last active user session after whole process is complete.
402 if (!chromeos::UserSessionManager::GetInstance()->
403 UserSessionsRestoreInProgress()) {
404 // Immediately hide the windows of the current user.
405 base::AutoReset<AnimationSpeed> animation_speed(&animation_speed_,
406 ANIMATION_SPEED_DISABLED);
407 ActiveUserChanged(user_id);
411 void MultiUserWindowManagerChromeOS::AddObserver(Observer* observer) {
412 observers_.AddObserver(observer);
415 void MultiUserWindowManagerChromeOS::RemoveObserver(Observer* observer) {
416 observers_.RemoveObserver(observer);
419 void MultiUserWindowManagerChromeOS::ActiveUserChanged(
420 const std::string& user_id) {
421 // This needs to be set before the animation starts.
422 current_user_id_ = user_id;
424 // Here to avoid a very nasty race condition, we must destruct any previously
425 // created animation before creating a new one. Otherwise, the newly
426 // constructed will hide all windows of the old user in the first step of the
427 // animation only to be reshown again by the destructor of the old animation.
428 animation_.reset();
429 animation_.reset(
430 new UserSwitchAnimatorChromeOS(
431 this, user_id, GetAdjustedAnimationTimeInMS(kUserFadeTimeMS)));
432 // Call notifier here instead of observing ActiveUserChanged because
433 // this must happen after MultiUserWindowManagerChromeOS is notified.
434 ash::Shell::GetInstance()
435 ->system_tray_notifier()
436 ->NotifyMediaCaptureChanged();
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 wm::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::SetAnimationSpeedForTest(
529 MultiUserWindowManagerChromeOS::AnimationSpeed speed) {
530 animation_speed_ = speed;
533 bool MultiUserWindowManagerChromeOS::IsAnimationRunningForTest() {
534 return animation_.get() != NULL && !animation_->IsAnimationFinished();
537 const std::string& MultiUserWindowManagerChromeOS::GetCurrentUserForTest()
538 const {
539 return current_user_id_;
542 bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern(
543 aura::Window* window,
544 const std::string& user_id) {
545 // If there is either no owner, or the owner is the current user, no action
546 // is required.
547 const std::string& owner = GetWindowOwner(window);
548 if (owner.empty() ||
549 (owner == user_id && IsWindowOnDesktopOfUser(window, user_id)))
550 return false;
552 bool minimized = ash::wm::GetWindowState(window)->IsMinimized();
553 // Check that we are not trying to transfer ownership of a minimized window.
554 if (user_id != owner && minimized)
555 return false;
557 if (minimized) {
558 // If it is minimized it falls back to the original desktop.
559 ash::MultiProfileUMA::RecordTeleportAction(
560 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_MINIMIZE);
561 } else {
562 // If the window was transferred without getting minimized, we should record
563 // the window type.
564 RecordUMAForTransferredWindowType(window);
567 WindowToEntryMap::iterator it = window_to_entry_.find(window);
568 it->second->set_show_for_user(user_id);
570 // Show the window if the added user is the current one.
571 if (user_id == current_user_id_) {
572 // Only show the window if it should be shown according to its state.
573 if (it->second->show())
574 SetWindowVisibility(window, true, kTeleportAnimationTimeMS);
575 } else {
576 SetWindowVisibility(window, false, kTeleportAnimationTimeMS);
579 // Notify entry change.
580 FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryChanged(window));
581 return true;
584 void MultiUserWindowManagerChromeOS::SetWindowVisibility(
585 aura::Window* window, bool visible, int animation_time_in_ms) {
586 if (window->IsVisible() == visible)
587 return;
589 // Hiding a system modal dialog should not be allowed. Instead we switch to
590 // the user which is showing the system modal window.
591 // Note that in some cases (e.g. unit test) windows might not have a root
592 // window.
593 if (!visible && window->GetRootWindow()) {
594 // Get the system modal container for the window's root window.
595 aura::Window* system_modal_container =
596 window->GetRootWindow()->GetChildById(
597 ash::kShellWindowId_SystemModalContainer);
598 if (window->parent() == system_modal_container) {
599 // The window is system modal and we need to find the parent which owns
600 // it so that we can switch to the desktop accordingly.
601 std::string user_id = GetUserPresentingWindow(window);
602 if (user_id.empty()) {
603 aura::Window* owning_window = GetOwningWindowInTransientChain(window);
604 DCHECK(owning_window);
605 user_id = GetUserPresentingWindow(owning_window);
606 DCHECK(!user_id.empty());
608 ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser(
609 user_id);
610 return;
614 // To avoid that these commands are recorded as any other commands, we are
615 // suppressing any window entry changes while this is going on.
616 base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
618 if (visible)
619 ShowWithTransientChildrenRecursive(window, animation_time_in_ms);
620 else
621 SetWindowVisible(window, false, animation_time_in_ms);
624 void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) {
625 // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can
626 // come here with no valid window.
627 if (!browser->window() || !browser->window()->GetNativeWindow())
628 return;
629 SetWindowOwner(browser->window()->GetNativeWindow(),
630 multi_user_util::GetUserIDFromProfile(browser->profile()));
633 void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive(
634 aura::Window* window, int animation_time_in_ms) {
635 aura::Window::Windows::const_iterator it =
636 wm::GetTransientChildren(window).begin();
637 for (; it != wm::GetTransientChildren(window).end(); ++it)
638 ShowWithTransientChildrenRecursive(*it, animation_time_in_ms);
640 // We show all children which were not explicitly hidden.
641 TransientWindowToVisibility::iterator it2 =
642 transient_window_to_visibility_.find(window);
643 if (it2 == transient_window_to_visibility_.end() || it2->second)
644 SetWindowVisible(window, true, animation_time_in_ms);
647 aura::Window* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain(
648 aura::Window* window) const {
649 if (!GetWindowOwner(window).empty())
650 return NULL;
651 aura::Window* parent = wm::GetTransientParent(window);
652 while (parent) {
653 if (!GetWindowOwner(parent).empty())
654 return parent;
655 parent = wm::GetTransientParent(parent);
657 return NULL;
660 void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive(
661 aura::Window* window,
662 aura::Window* owned_parent) {
663 // First add all child windows.
664 aura::Window::Windows::const_iterator it =
665 wm::GetTransientChildren(window).begin();
666 for (; it != wm::GetTransientChildren(window).end(); ++it)
667 AddTransientOwnerRecursive(*it, owned_parent);
669 // If this window is the owned window, we do not have to handle it again.
670 if (window == owned_parent)
671 return;
673 // Remember the current visibility.
674 DCHECK(transient_window_to_visibility_.find(window) ==
675 transient_window_to_visibility_.end());
676 transient_window_to_visibility_[window] = window->IsVisible();
678 // Add observers to track state changes.
679 window->AddObserver(this);
680 wm::TransientWindowManager::Get(window)->AddObserver(this);
682 // Hide the window if it should not be shown. Note that this hide operation
683 // will hide recursively this and all children - but we have already collected
684 // their initial view state.
685 if (!IsWindowOnDesktopOfUser(owned_parent, current_user_id_))
686 SetWindowVisibility(window, false, kAnimationTimeMS);
689 void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive(
690 aura::Window* window) {
691 // First remove all child windows.
692 aura::Window::Windows::const_iterator it =
693 wm::GetTransientChildren(window).begin();
694 for (; it != wm::GetTransientChildren(window).end(); ++it)
695 RemoveTransientOwnerRecursive(*it);
697 // Find from transient window storage the visibility for the given window,
698 // set the visibility accordingly and delete the window from the map.
699 TransientWindowToVisibility::iterator visibility_item =
700 transient_window_to_visibility_.find(window);
701 DCHECK(visibility_item != transient_window_to_visibility_.end());
703 window->RemoveObserver(this);
704 wm::TransientWindowManager::Get(window)->RemoveObserver(this);
706 bool unowned_view_state = visibility_item->second;
707 transient_window_to_visibility_.erase(visibility_item);
708 if (unowned_view_state && !window->IsVisible()) {
709 // To prevent these commands from being recorded as any other commands, we
710 // are suppressing any window entry changes while this is going on.
711 // Instead of calling SetWindowVisible, only show gets called here since all
712 // dependents have been shown previously already.
713 base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
714 window->Show();
718 void MultiUserWindowManagerChromeOS::SetWindowVisible(
719 aura::Window* window,
720 bool visible,
721 int animation_time_in_ms) {
722 // The MaximizeModeWindowManager will not handle invisible windows since they
723 // are not user activatable. Since invisible windows are not being tracked,
724 // we tell it to maximize / track this window now before it gets shown, to
725 // reduce animation jank from multiple resizes.
726 if (visible)
727 ash::Shell::GetInstance()->maximize_mode_controller()->AddWindow(window);
729 AnimationSetter animation_setter(
730 window,
731 GetAdjustedAnimationTimeInMS(animation_time_in_ms));
733 if (visible)
734 window->Show();
735 else
736 window->Hide();
738 // Make sure that animations have no influence on the window state after the
739 // call.
740 DCHECK_EQ(visible, window->IsVisible());
743 int MultiUserWindowManagerChromeOS::GetAdjustedAnimationTimeInMS(
744 int default_time_in_ms) const {
745 return animation_speed_ == ANIMATION_SPEED_NORMAL ? default_time_in_ms :
746 (animation_speed_ == ANIMATION_SPEED_FAST ? 10 : 0);
749 } // namespace chrome