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