Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / ash / multi_user / multi_user_window_manager_chromeos.cc
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.
4
5 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
6
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/profiles/profile.h"
24 #include "chrome/browser/profiles/profile_manager.h"
25 #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
26 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
27 #include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h"
28 #include "chrome/browser/ui/browser.h"
29 #include "chrome/browser/ui/browser_finder.h"
30 #include "chrome/browser/ui/browser_list.h"
31 #include "chrome/browser/ui/browser_window.h"
32 #include "content/public/browser/notification_service.h"
33 #include "extensions/browser/app_window/app_window.h"
34 #include "extensions/browser/app_window/app_window_registry.h"
35 #include "google_apis/gaia/gaia_auth_util.h"
36 #include "ui/aura/client/aura_constants.h"
37 #include "ui/aura/window.h"
38 #include "ui/aura/window_event_dispatcher.h"
39 #include "ui/base/ui_base_types.h"
40 #include "ui/events/event.h"
41 #include "ui/message_center/message_center.h"
42 #include "ui/wm/core/transient_window_manager.h"
43 #include "ui/wm/core/window_animations.h"
44 #include "ui/wm/core/window_util.h"
45
46 namespace {
47
48 // The animation time in milliseconds for a single window which is fading
49 // in / out.
50 const int kAnimationTimeMS = 100;
51
52 // The animation time in milliseconds for the fade in and / or out when
53 // switching users.
54 const int kUserFadeTimeMS = 110;
55
56 // The animation time in ms for a window which get teleported to another screen.
57 const int kTeleportAnimationTimeMS = 300;
58
59 // Checks if a given event is a user event.
60 bool IsUserEvent(const ui::Event* e) {
61   if (e) {
62     ui::EventType type = e->type();
63     if (type != ui::ET_CANCEL_MODE &&
64         type != ui::ET_UMA_DATA &&
65         type != ui::ET_UNKNOWN)
66       return true;
67   }
68   return false;
69 }
70
71 // Test if we are currently processing a user event which might lead to a
72 // browser / app creation.
73 bool IsProcessingUserEvent() {
74   // When there is a nested message loop (e.g. active menu or drag and drop
75   // operation) - we are in a nested loop and can ignore this.
76   // Note: Unit tests might not have a message loop.
77   base::MessageLoop* message_loop = base::MessageLoop::current();
78   if (message_loop && message_loop->is_running() && message_loop->IsNested())
79     return false;
80
81   // TODO(skuhne): "Open link in new window" will come here after the menu got
82   // closed, executing the command from the nested menu loop. However at that
83   // time there is no active event processed. A solution for that need to be
84   // found past M-32. A global event handler filter (pre and post) might fix
85   // that problem in conjunction with a depth counter - but - for the menu
86   // execution we come here after the loop was finished (so it's not nested
87   // anymore) and the root window should therefore still have the event which
88   // lead to the menu invocation, but it is not. By fixing that problem this
89   // would "magically work".
90   aura::Window::Windows root_window_list = ash::Shell::GetAllRootWindows();
91   for (aura::Window::Windows::iterator it = root_window_list.begin();
92        it != root_window_list.end();
93        ++it) {
94     if (IsUserEvent((*it)->GetHost()->dispatcher()->current_event()))
95       return true;
96   }
97   return false;
98 }
99
100 // Records the type of window which was transferred to another desktop.
101 void RecordUMAForTransferredWindowType(aura::Window* window) {
102   // We need to figure out what kind of window this is to record the transfer.
103   Browser* browser = chrome::FindBrowserWithWindow(window);
104   ash::MultiProfileUMA::TeleportWindowType window_type =
105       ash::MultiProfileUMA::TELEPORT_WINDOW_UNKNOWN;
106   if (browser) {
107     if (browser->profile()->IsOffTheRecord()) {
108       window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_INCOGNITO_BROWSER;
109     } else if (browser->is_app()) {
110       window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V1_APP;
111     } else if (browser->is_type_popup()) {
112       window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_POPUP;
113     } else {
114       window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_BROWSER;
115     }
116   } else {
117     // Unit tests might come here without a profile manager.
118     if (!g_browser_process->profile_manager())
119       return;
120     // If it is not a browser, it is probably be a V2 application. In that case
121     // one of the AppWindowRegistry instances should know about it.
122     extensions::AppWindow* app_window = NULL;
123     std::vector<Profile*> profiles =
124         g_browser_process->profile_manager()->GetLoadedProfiles();
125     for (std::vector<Profile*>::iterator it = profiles.begin();
126          it != profiles.end() && app_window == NULL;
127          it++) {
128       app_window = extensions::AppWindowRegistry::Get(*it)
129                        ->GetAppWindowForNativeWindow(window);
130     }
131     if (app_window) {
132       if (app_window->window_type() ==
133           extensions::AppWindow::WINDOW_TYPE_PANEL ||
134           app_window->window_type() ==
135           extensions::AppWindow::WINDOW_TYPE_V1_PANEL) {
136         window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_PANEL;
137       } else {
138         window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V2_APP;
139       }
140     }
141   }
142   ash::MultiProfileUMA::RecordTeleportWindowType(window_type);
143 }
144
145 }  // namespace
146
147 namespace chrome {
148
149 // A class to temporarily change the animation properties for a window.
150 class AnimationSetter {
151  public:
152   AnimationSetter(aura::Window* window, int animation_time_in_ms)
153       : window_(window),
154         previous_animation_type_(
155             wm::GetWindowVisibilityAnimationType(window_)),
156         previous_animation_time_(
157             wm::GetWindowVisibilityAnimationDuration(*window_)) {
158     wm::SetWindowVisibilityAnimationType(
159         window_,
160         wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
161     wm::SetWindowVisibilityAnimationDuration(
162         window_,
163         base::TimeDelta::FromMilliseconds(animation_time_in_ms));
164   }
165
166   ~AnimationSetter() {
167     wm::SetWindowVisibilityAnimationType(window_,
168                                                     previous_animation_type_);
169     wm::SetWindowVisibilityAnimationDuration(
170         window_,
171         previous_animation_time_);
172   }
173
174  private:
175   // The window which gets used.
176   aura::Window* window_;
177
178   // Previous animation type.
179   const int previous_animation_type_;
180
181   // Previous animation time.
182   const base::TimeDelta previous_animation_time_;
183
184   DISALLOW_COPY_AND_ASSIGN(AnimationSetter);
185 };
186
187 // This class keeps track of all applications which were started for a user.
188 // When an app gets created, the window will be tagged for that user. Note
189 // that the destruction does not need to be tracked here since the universal
190 // window observer will take care of that.
191 class AppObserver : public extensions::AppWindowRegistry::Observer {
192  public:
193   explicit AppObserver(const std::string& user_id) : user_id_(user_id) {}
194   virtual ~AppObserver() {}
195
196   // AppWindowRegistry::Observer overrides:
197   virtual void OnAppWindowAdded(extensions::AppWindow* app_window) OVERRIDE {
198     aura::Window* window = app_window->GetNativeWindow();
199     DCHECK(window);
200     MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(window,
201                                                                   user_id_);
202   }
203
204  private:
205   std::string user_id_;
206
207   DISALLOW_COPY_AND_ASSIGN(AppObserver);
208 };
209
210 MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS(
211     const std::string& current_user_id)
212     : current_user_id_(current_user_id),
213       notification_blocker_(new MultiUserNotificationBlockerChromeOS(
214           message_center::MessageCenter::Get(), current_user_id)),
215       suppress_visibility_changes_(false),
216       animation_speed_(ANIMATION_SPEED_NORMAL) {
217   // Add a session state observer to be able to monitor session changes.
218   if (ash::Shell::HasInstance())
219     ash::Shell::GetInstance()->session_state_delegate()->
220         AddSessionStateObserver(this);
221
222   // The BrowserListObserver would have been better to use then the old
223   // notification system, but that observer fires before the window got created.
224   registrar_.Add(this, NOTIFICATION_BROWSER_WINDOW_READY,
225                  content::NotificationService::AllSources());
226
227   // Add an app window observer & all already running apps.
228   Profile* profile = multi_user_util::GetProfileFromUserID(current_user_id);
229   if (profile)
230     AddUser(profile);
231 }
232
233 MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() {
234   // When the MultiUserWindowManager gets destroyed, ash::Shell is mostly gone.
235   // As such we should not try to finalize any outstanding user animations.
236   // Note that the destruction of the object can be done later.
237   if (animation_.get())
238     animation_->CancelAnimation();
239
240   // Remove all window observers.
241   WindowToEntryMap::iterator window = window_to_entry_.begin();
242   while (window != window_to_entry_.end()) {
243     OnWindowDestroyed(window->first);
244     window = window_to_entry_.begin();
245   }
246
247   // Remove all app observers.
248   UserIDToAppWindowObserver::iterator app_observer_iterator =
249       user_id_to_app_observer_.begin();
250   while (app_observer_iterator != user_id_to_app_observer_.end()) {
251     Profile* profile = multi_user_util::GetProfileFromUserID(
252         app_observer_iterator->first);
253     DCHECK(profile);
254     extensions::AppWindowRegistry::Get(profile)
255         ->RemoveObserver(app_observer_iterator->second);
256     delete app_observer_iterator->second;
257     user_id_to_app_observer_.erase(app_observer_iterator);
258     app_observer_iterator = user_id_to_app_observer_.begin();
259   }
260
261   if (ash::Shell::HasInstance())
262     ash::Shell::GetInstance()->session_state_delegate()->
263         RemoveSessionStateObserver(this);
264 }
265
266 void MultiUserWindowManagerChromeOS::SetWindowOwner(
267     aura::Window* window,
268     const std::string& user_id) {
269   // Make sure the window is valid and there was no owner yet.
270   DCHECK(window);
271   DCHECK(!user_id.empty());
272   if (GetWindowOwner(window) == user_id)
273     return;
274   DCHECK(GetWindowOwner(window).empty());
275   window_to_entry_[window] = new WindowEntry(user_id);
276
277   // Remember the initial visibility of the window.
278   window_to_entry_[window]->set_show(window->IsVisible());
279
280   // Add observers to track state changes.
281   window->AddObserver(this);
282   wm::TransientWindowManager::Get(window)->AddObserver(this);
283
284   // Check if this window was created due to a user interaction. If it was,
285   // transfer it to the current user.
286   if (IsProcessingUserEvent())
287     window_to_entry_[window]->set_show_for_user(current_user_id_);
288
289   // Add all transient children to our set of windows. Note that the function
290   // will add the children but not the owner to the transient children map.
291   AddTransientOwnerRecursive(window, window);
292
293   // Notify entry adding.
294   FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryAdded(window));
295
296   if (!IsWindowOnDesktopOfUser(window, current_user_id_))
297     SetWindowVisibility(window, false, 0);
298 }
299
300 const std::string& MultiUserWindowManagerChromeOS::GetWindowOwner(
301     aura::Window* window) const {
302   WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
303   return it != window_to_entry_.end() ? it->second->owner()
304                                       : base::EmptyString();
305 }
306
307 void MultiUserWindowManagerChromeOS::ShowWindowForUser(
308     aura::Window* window,
309     const std::string& user_id) {
310   std::string previous_owner(GetUserPresentingWindow(window));
311   if (!ShowWindowForUserIntern(window, user_id))
312     return;
313   // The window switched to a new desktop and we have to switch to that desktop,
314   // but only when it was on the visible desktop and the the target is not the
315   // visible desktop.
316   if (user_id == current_user_id_ || previous_owner != current_user_id_)
317     return;
318
319   ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser(
320       user_id);
321 }
322
323 bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() const {
324   WindowToEntryMap::const_iterator it = window_to_entry_.begin();
325   for (; it != window_to_entry_.end(); ++it) {
326     if (it->second->owner() != it->second->show_for_user())
327       return true;
328   }
329   return false;
330 }
331
332 void MultiUserWindowManagerChromeOS::GetOwnersOfVisibleWindows(
333     std::set<std::string>* user_ids) const {
334   for (WindowToEntryMap::const_iterator it = window_to_entry_.begin();
335        it != window_to_entry_.end();
336        ++it) {
337     if (it->first->IsVisible())
338       user_ids->insert(it->second->owner());
339   }
340 }
341
342 bool MultiUserWindowManagerChromeOS::IsWindowOnDesktopOfUser(
343     aura::Window* window,
344     const std::string& user_id) const {
345   const std::string& presenting_user = GetUserPresentingWindow(window);
346   return presenting_user.empty() || presenting_user == user_id;
347 }
348
349 const std::string& MultiUserWindowManagerChromeOS::GetUserPresentingWindow(
350     aura::Window* window) const {
351   WindowToEntryMap::const_iterator it = window_to_entry_.find(window);
352   // If the window is not owned by anyone it is shown on all desktops and we
353   // return the empty string.
354   if (it == window_to_entry_.end())
355     return base::EmptyString();
356   // Otherwise we ask the object for its desktop.
357   return it->second->show_for_user();
358 }
359
360 void MultiUserWindowManagerChromeOS::AddUser(content::BrowserContext* context) {
361   Profile* profile = Profile::FromBrowserContext(context);
362   const std::string& user_id = multi_user_util::GetUserIDFromProfile(profile);
363   if (user_id_to_app_observer_.find(user_id) != user_id_to_app_observer_.end())
364     return;
365
366   user_id_to_app_observer_[user_id] = new AppObserver(user_id);
367   extensions::AppWindowRegistry::Get(profile)
368       ->AddObserver(user_id_to_app_observer_[user_id]);
369
370   // Account all existing application windows of this user accordingly.
371   const extensions::AppWindowRegistry::AppWindowList& app_windows =
372       extensions::AppWindowRegistry::Get(profile)->app_windows();
373   extensions::AppWindowRegistry::AppWindowList::const_iterator it =
374       app_windows.begin();
375   for (; it != app_windows.end(); ++it)
376     user_id_to_app_observer_[user_id]->OnAppWindowAdded(*it);
377
378   // Account all existing browser windows of this user accordingly.
379   BrowserList* browser_list = BrowserList::GetInstance(HOST_DESKTOP_TYPE_ASH);
380   BrowserList::const_iterator browser_it = browser_list->begin();
381   for (; browser_it != browser_list->end(); ++browser_it) {
382     if ((*browser_it)->profile()->GetOriginalProfile() == profile)
383       AddBrowserWindow(*browser_it);
384   }
385 }
386
387 void MultiUserWindowManagerChromeOS::AddObserver(Observer* observer) {
388   observers_.AddObserver(observer);
389 }
390
391 void MultiUserWindowManagerChromeOS::RemoveObserver(Observer* observer) {
392   observers_.RemoveObserver(observer);
393 }
394
395 void MultiUserWindowManagerChromeOS::ActiveUserChanged(
396     const std::string& user_id) {
397   DCHECK(user_id != current_user_id_);
398   // This needs to be set before the animation starts.
399   current_user_id_ = user_id;
400
401   animation_.reset(
402       new UserSwichAnimatorChromeOS(
403           this, user_id, GetAdjustedAnimationTimeInMS(kUserFadeTimeMS)));
404   // Call notifier here instead of observing ActiveUserChanged because
405   // this must happen after MultiUserWindowManagerChromeOS is notified.
406   ash::Shell::GetInstance()
407       ->system_tray_notifier()
408       ->NotifyMediaCaptureChanged();
409 }
410
411 void MultiUserWindowManagerChromeOS::OnWindowDestroyed(aura::Window* window) {
412   if (GetWindowOwner(window).empty()) {
413     // This must be a window in the transient chain - remove it and its
414     // children from the owner.
415     RemoveTransientOwnerRecursive(window);
416     return;
417   }
418   wm::TransientWindowManager::Get(window)->RemoveObserver(this);
419   // Remove the window from the owners list.
420   delete window_to_entry_[window];
421   window_to_entry_.erase(window);
422
423   // Notify entry change.
424   FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryRemoved(window));
425 }
426
427 void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanging(
428     aura::Window* window, bool visible) {
429   // This command gets called first and immediately when show or hide gets
430   // called. We remember here the desired state for restoration IF we were
431   // not ourselves issuing the call.
432   // Note also that using the OnWindowVisibilityChanged callback cannot be
433   // used for this.
434   if (suppress_visibility_changes_)
435     return;
436
437   WindowToEntryMap::iterator it = window_to_entry_.find(window);
438   // If the window is not owned by anyone it is shown on all desktops.
439   if (it != window_to_entry_.end()) {
440     // Remember what was asked for so that we can restore this when the user's
441     // desktop gets restored.
442     it->second->set_show(visible);
443   } else {
444     TransientWindowToVisibility::iterator it =
445         transient_window_to_visibility_.find(window);
446     if (it != transient_window_to_visibility_.end())
447       it->second = visible;
448   }
449 }
450
451 void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanged(
452     aura::Window* window, bool visible) {
453   if (suppress_visibility_changes_)
454     return;
455
456   // Don't allow to make the window visible if it shouldn't be.
457   if (visible && !IsWindowOnDesktopOfUser(window, current_user_id_)) {
458     SetWindowVisibility(window, false, 0);
459     return;
460   }
461   aura::Window* owned_parent = GetOwningWindowInTransientChain(window);
462   if (owned_parent && owned_parent != window && visible &&
463       !IsWindowOnDesktopOfUser(owned_parent, current_user_id_))
464     SetWindowVisibility(window, false, 0);
465 }
466
467 void MultiUserWindowManagerChromeOS::OnTransientChildAdded(
468     aura::Window* window,
469     aura::Window* transient_window) {
470   if (!GetWindowOwner(window).empty()) {
471     AddTransientOwnerRecursive(transient_window, window);
472     return;
473   }
474   aura::Window* owned_parent =
475       GetOwningWindowInTransientChain(transient_window);
476   if (!owned_parent)
477     return;
478
479   AddTransientOwnerRecursive(transient_window, owned_parent);
480 }
481
482 void MultiUserWindowManagerChromeOS::OnTransientChildRemoved(
483     aura::Window* window,
484     aura::Window* transient_window) {
485   // Remove the transient child if the window itself is owned, or one of the
486   // windows in its transient parents chain.
487   if (!GetWindowOwner(window).empty() ||
488       GetOwningWindowInTransientChain(window))
489     RemoveTransientOwnerRecursive(transient_window);
490 }
491
492 void MultiUserWindowManagerChromeOS::Observe(
493     int type,
494     const content::NotificationSource& source,
495     const content::NotificationDetails& details) {
496   if (type == NOTIFICATION_BROWSER_WINDOW_READY)
497     AddBrowserWindow(content::Source<Browser>(source).ptr());
498 }
499
500 void MultiUserWindowManagerChromeOS::SetAnimationSpeedForTest(
501     MultiUserWindowManagerChromeOS::AnimationSpeed speed) {
502   animation_speed_ = speed;
503 }
504
505 bool MultiUserWindowManagerChromeOS::IsAnimationRunningForTest() {
506   return animation_.get() != NULL && !animation_->IsAnimationFinished();
507 }
508
509 const std::string& MultiUserWindowManagerChromeOS::GetCurrentUserForTest()
510     const {
511   return current_user_id_;
512 }
513
514 bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern(
515     aura::Window* window,
516     const std::string& user_id) {
517   // If there is either no owner, or the owner is the current user, no action
518   // is required.
519   const std::string& owner = GetWindowOwner(window);
520   if (owner.empty() ||
521       (owner == user_id && IsWindowOnDesktopOfUser(window, user_id)))
522     return false;
523
524   bool minimized = ash::wm::GetWindowState(window)->IsMinimized();
525   // Check that we are not trying to transfer ownership of a minimized window.
526   if (user_id != owner && minimized)
527     return false;
528
529   if (minimized) {
530     // If it is minimized it falls back to the original desktop.
531     ash::MultiProfileUMA::RecordTeleportAction(
532         ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_MINIMIZE);
533   } else {
534     // If the window was transferred without getting minimized, we should record
535     // the window type.
536     RecordUMAForTransferredWindowType(window);
537   }
538
539   WindowToEntryMap::iterator it = window_to_entry_.find(window);
540   it->second->set_show_for_user(user_id);
541
542   // Show the window if the added user is the current one.
543   if (user_id == current_user_id_) {
544     // Only show the window if it should be shown according to its state.
545     if (it->second->show())
546       SetWindowVisibility(window, true, kTeleportAnimationTimeMS);
547   } else {
548     SetWindowVisibility(window, false, kTeleportAnimationTimeMS);
549   }
550
551   // Notify entry change.
552   FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryChanged(window));
553   return true;
554 }
555
556 void MultiUserWindowManagerChromeOS::SetWindowVisibility(
557     aura::Window* window, bool visible, int animation_time_in_ms) {
558   if (window->IsVisible() == visible)
559     return;
560
561   // Hiding a system modal dialog should not be allowed. Instead we switch to
562   // the user which is showing the system modal window.
563   // Note that in some cases (e.g. unit test) windows might not have a root
564   // window.
565   if (!visible && window->GetRootWindow()) {
566     // Get the system modal container for the window's root window.
567     aura::Window* system_modal_container =
568         window->GetRootWindow()->GetChildById(
569             ash::kShellWindowId_SystemModalContainer);
570     if (window->parent() == system_modal_container) {
571       // The window is system modal and we need to find the parent which owns
572       // it so that we can switch to the desktop accordingly.
573       std::string user_id = GetUserPresentingWindow(window);
574       if (user_id.empty()) {
575         aura::Window* owning_window = GetOwningWindowInTransientChain(window);
576         DCHECK(owning_window);
577         user_id = GetUserPresentingWindow(owning_window);
578         DCHECK(!user_id.empty());
579       }
580       ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser(
581           user_id);
582       return;
583     }
584   }
585
586   // To avoid that these commands are recorded as any other commands, we are
587   // suppressing any window entry changes while this is going on.
588   base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
589
590   if (visible) {
591     ShowWithTransientChildrenRecursive(window, animation_time_in_ms);
592   } else {
593     if (window->HasFocus())
594       window->Blur();
595     SetWindowVisible(window, false, animation_time_in_ms);
596   }
597 }
598
599 void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) {
600   // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can
601   // come here with no valid window.
602   if (!browser->window() || !browser->window()->GetNativeWindow())
603     return;
604   SetWindowOwner(browser->window()->GetNativeWindow(),
605                  multi_user_util::GetUserIDFromProfile(browser->profile()));
606 }
607
608 void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive(
609     aura::Window* window, int animation_time_in_ms) {
610   aura::Window::Windows::const_iterator it =
611       wm::GetTransientChildren(window).begin();
612   for (; it != wm::GetTransientChildren(window).end(); ++it)
613     ShowWithTransientChildrenRecursive(*it, animation_time_in_ms);
614
615   // We show all children which were not explicitly hidden.
616   TransientWindowToVisibility::iterator it2 =
617       transient_window_to_visibility_.find(window);
618   if (it2 == transient_window_to_visibility_.end() || it2->second)
619     SetWindowVisible(window, true, animation_time_in_ms);
620 }
621
622 aura::Window* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain(
623     aura::Window* window) const {
624   if (!GetWindowOwner(window).empty())
625     return NULL;
626   aura::Window* parent = wm::GetTransientParent(window);
627   while (parent) {
628     if (!GetWindowOwner(parent).empty())
629       return parent;
630     parent = wm::GetTransientParent(parent);
631   }
632   return NULL;
633 }
634
635 void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive(
636     aura::Window* window,
637     aura::Window* owned_parent) {
638   // First add all child windows.
639   aura::Window::Windows::const_iterator it =
640       wm::GetTransientChildren(window).begin();
641   for (; it != wm::GetTransientChildren(window).end(); ++it)
642     AddTransientOwnerRecursive(*it, owned_parent);
643
644   // If this window is the owned window, we do not have to handle it again.
645   if (window == owned_parent)
646     return;
647
648   // Remember the current visibility.
649   DCHECK(transient_window_to_visibility_.find(window) ==
650              transient_window_to_visibility_.end());
651   transient_window_to_visibility_[window] = window->IsVisible();
652
653   // Add observers to track state changes.
654   window->AddObserver(this);
655   wm::TransientWindowManager::Get(window)->AddObserver(this);
656
657   // Hide the window if it should not be shown. Note that this hide operation
658   // will hide recursively this and all children - but we have already collected
659   // their initial view state.
660   if (!IsWindowOnDesktopOfUser(owned_parent, current_user_id_))
661     SetWindowVisibility(window, false, kAnimationTimeMS);
662 }
663
664 void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive(
665     aura::Window* window) {
666   // First remove all child windows.
667   aura::Window::Windows::const_iterator it =
668       wm::GetTransientChildren(window).begin();
669   for (; it != wm::GetTransientChildren(window).end(); ++it)
670     RemoveTransientOwnerRecursive(*it);
671
672   // Find from transient window storage the visibility for the given window,
673   // set the visibility accordingly and delete the window from the map.
674   TransientWindowToVisibility::iterator visibility_item =
675       transient_window_to_visibility_.find(window);
676   DCHECK(visibility_item != transient_window_to_visibility_.end());
677
678   window->RemoveObserver(this);
679   wm::TransientWindowManager::Get(window)->RemoveObserver(this);
680
681   bool unowned_view_state = visibility_item->second;
682   transient_window_to_visibility_.erase(visibility_item);
683   if (unowned_view_state && !window->IsVisible()) {
684     // To prevent these commands from being recorded as any other commands, we
685     // are suppressing any window entry changes while this is going on.
686     // Instead of calling SetWindowVisible, only show gets called here since all
687     // dependents have been shown previously already.
688     base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
689     window->Show();
690   }
691 }
692
693 void MultiUserWindowManagerChromeOS::SetWindowVisible(
694     aura::Window* window,
695     bool visible,
696     int animation_time_in_ms) {
697   // The MaximizeModeWindowManager will not handle invisible windows since they
698   // are not user activatable. Since invisible windows are not being tracked,
699   // we tell it to maximize / track this window now before it gets shown, to
700   // reduce animation jank from multiple resizes.
701   if (visible)
702     ash::Shell::GetInstance()->maximize_mode_controller()->AddWindow(window);
703
704   AnimationSetter animation_setter(
705       window,
706       GetAdjustedAnimationTimeInMS(animation_time_in_ms));
707
708   if (visible)
709     window->Show();
710   else
711     window->Hide();
712
713   // Make sure that animations have no influence on the window state after the
714   // call.
715   DCHECK_EQ(visible, window->IsVisible());
716 }
717
718 int MultiUserWindowManagerChromeOS::GetAdjustedAnimationTimeInMS(
719     int default_time_in_ms) const {
720   return animation_speed_ == ANIMATION_SPEED_NORMAL ? default_time_in_ms :
721       (animation_speed_ == ANIMATION_SPEED_FAST ? 10 : 0);
722 }
723
724 }  // namespace chrome