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/shell_window.h"
      8 #include "apps/shell_window_registry.h"
      9 #include "ash/ash_switches.h"
     10 #include "ash/multi_profile_uma.h"
     11 #include "ash/session_state_delegate.h"
     12 #include "ash/shell.h"
     13 #include "ash/shell_delegate.h"
     14 #include "ash/shell_window_ids.h"
     15 #include "ash/wm/mru_window_tracker.h"
     16 #include "ash/wm/window_positioner.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/user_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_util.h"
     27 #include "chrome/browser/ui/browser.h"
     28 #include "chrome/browser/ui/browser_finder.h"
     29 #include "chrome/browser/ui/browser_list.h"
     30 #include "chrome/browser/ui/browser_window.h"
     31 #include "content/public/browser/notification_service.h"
     32 #include "google_apis/gaia/gaia_auth_util.h"
     33 #include "ui/aura/client/activation_client.h"
     34 #include "ui/aura/client/aura_constants.h"
     35 #include "ui/aura/root_window.h"
     36 #include "ui/aura/window.h"
     37 #include "ui/base/ui_base_types.h"
     38 #include "ui/events/event.h"
     39 
     40 namespace {
     41 
     42 // Checks if a given event is a user event.
     43 bool IsUserEvent(ui::Event* e) {
     44   if (e) {
     45     ui::EventType type = e->type();
     46     if (type != ui::ET_CANCEL_MODE &&
     47         type != ui::ET_UMA_DATA &&
     48         type != ui::ET_UNKNOWN)
     49       return true;
     50   }
     51   return false;
     52 }
     53 
     54 // Test if we are currently processing a user event which might lead to a
     55 // browser / app creation.
     56 bool IsProcessingUserEvent() {
     57   // When there is a nested message loop (e.g. active menu or drag and drop
     58   // operation) - we are in a nested loop and can ignore this.
     59   // Note: Unit tests might not have a message loop.
     60   base::MessageLoop* message_loop = base::MessageLoop::current();
     61   if (message_loop && message_loop->is_running() && message_loop->IsNested())
     62     return false;
     63 
     64   // TODO(skuhne): "Open link in new window" will come here after the menu got
     65   // closed, executing the command from the nested menu loop. However at that
     66   // time there is no active event processed. A solution for that need to be
     67   // found past M-32. A global event handler filter (pre and post) might fix
     68   // that problem in conjunction with a depth counter - but - for the menu
     69   // execution we come here after the loop was finished (so it's not nested
     70   // anymore) and the root window should therefore still have the event which
     71   // lead to the menu invocation, but it is not. By fixing that problem this
     72   // would "magically work".
     73   aura::Window::Windows root_window_list = ash::Shell::GetAllRootWindows();
     74   for (aura::Window::Windows::iterator it = root_window_list.begin();
     75        it != root_window_list.end();
     76        ++it) {
     77     if (IsUserEvent((*it)->GetDispatcher()->current_event()))
     78       return true;
     79   }
     80   return false;
     81 }
     82 
     83 // Records the type of window which was transferred to another desktop.
     84 void RecordUMAForTransferredWindowType(aura::Window* window) {
     85   // We need to figure out what kind of window this is to record the transfer.
     86   Browser* browser = chrome::FindBrowserWithWindow(window);
     87   ash::MultiProfileUMA::TeleportWindowType window_type =
     88       ash::MultiProfileUMA::TELEPORT_WINDOW_UNKNOWN;
     89   if (browser) {
     90     if (browser->profile()->IsOffTheRecord()) {
     91       window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_INCOGNITO_BROWSER;
     92     } else if (browser->is_app()) {
     93       window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V1_APP;
     94     } else if (browser->is_type_popup()) {
     95       window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_POPUP;
     96     } else {
     97       window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_BROWSER;
     98     }
     99   } else {
    100     // Unit tests might come here without a profile manager.
    101     if (!g_browser_process->profile_manager())
    102       return;
    103     // If it is not a browser, it is probably be a V2 application. In that case
    104     // one of the ShellWindowRegistries should know about it.
    105     apps::ShellWindow* shell_window = NULL;
    106     std::vector<Profile*> profiles =
    107         g_browser_process->profile_manager()->GetLoadedProfiles();
    108     for (std::vector<Profile*>::iterator it = profiles.begin();
    109          it != profiles.end() && shell_window == NULL; it++) {
    110       shell_window = apps::ShellWindowRegistry::Get(
    111           *it)->GetShellWindowForNativeWindow(window);
    112     }
    113     if (shell_window) {
    114       if (shell_window->window_type() ==
    115               apps::ShellWindow::WINDOW_TYPE_PANEL ||
    116           shell_window->window_type() ==
    117               apps::ShellWindow::WINDOW_TYPE_V1_PANEL) {
    118         window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_PANEL;
    119       } else {
    120         window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V2_APP;
    121       }
    122     }
    123   }
    124   ash::MultiProfileUMA::RecordTeleportWindowType(window_type);
    125 }
    126 
    127 }  // namespace
    128 
    129 namespace chrome {
    130 
    131 // logic while the user gets switched.
    132 class UserChangeActionDisabler {
    133  public:
    134   UserChangeActionDisabler() {
    135     ash::WindowPositioner::DisableAutoPositioning(true);
    136     ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true);
    137   }
    138 
    139   ~UserChangeActionDisabler() {
    140     ash::WindowPositioner::DisableAutoPositioning(false);
    141     ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(
    142         false);
    143   }
    144  private:
    145 
    146   DISALLOW_COPY_AND_ASSIGN(UserChangeActionDisabler);
    147 };
    148 
    149 // This class keeps track of all applications which were started for a user.
    150 // When an app gets created, the window will be tagged for that user. Note
    151 // that the destruction does not need to be tracked here since the universal
    152 // window observer will take care of that.
    153 class AppObserver : public apps::ShellWindowRegistry::Observer {
    154  public:
    155   explicit AppObserver(const std::string& user_id) : user_id_(user_id) {}
    156   virtual ~AppObserver() {}
    157 
    158   // ShellWindowRegistry::Observer overrides:
    159   virtual void OnShellWindowAdded(apps::ShellWindow* shell_window) OVERRIDE {
    160     aura::Window* window = shell_window->GetNativeWindow();
    161     DCHECK(window);
    162     MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(window,
    163                                                                   user_id_);
    164   }
    165   virtual void OnShellWindowIconChanged(apps::ShellWindow* shell_window)
    166       OVERRIDE {}
    167   virtual void OnShellWindowRemoved(apps::ShellWindow* shell_window)
    168       OVERRIDE {}
    169 
    170  private:
    171   std::string user_id_;
    172 
    173   DISALLOW_COPY_AND_ASSIGN(AppObserver);
    174 };
    175 
    176 MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS(
    177     const std::string& current_user_id)
    178     : current_user_id_(current_user_id),
    179       suppress_visibility_changes_(false) {
    180   // Add a session state observer to be able to monitor session changes.
    181   if (ash::Shell::HasInstance())
    182     ash::Shell::GetInstance()->session_state_delegate()->
    183         AddSessionStateObserver(this);
    184 
    185   // The BrowserListObserver would have been better to use then the old
    186   // notification system, but that observer fires before the window got created.
    187   registrar_.Add(this, NOTIFICATION_BROWSER_WINDOW_READY,
    188                  content::NotificationService::AllSources());
    189 
    190   // Add an app window observer & all already running apps.
    191   Profile* profile = multi_user_util::GetProfileFromUserID(current_user_id);
    192   if (profile)
    193     AddUser(profile);
    194 }
    195 
    196 MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() {
    197   // Remove all window observers.
    198   WindowToEntryMap::iterator window_observer = window_to_entry_.begin();
    199   while (window_observer != window_to_entry_.end()) {
    200     OnWindowDestroyed(window_observer->first);
    201     window_observer = window_to_entry_.begin();
    202   }
    203 
    204   // Remove all app observers.
    205   UserIDToShellWindowObserver::iterator app_observer_iterator =
    206       user_id_to_app_observer_.begin();
    207   while (app_observer_iterator != user_id_to_app_observer_.end()) {
    208     Profile* profile = multi_user_util::GetProfileFromUserID(
    209         app_observer_iterator->first);
    210     DCHECK(profile);
    211     apps::ShellWindowRegistry::Get(profile)->RemoveObserver(
    212         app_observer_iterator->second);
    213     delete app_observer_iterator->second;
    214     user_id_to_app_observer_.erase(app_observer_iterator);
    215     app_observer_iterator = user_id_to_app_observer_.begin();
    216   }
    217 
    218   if (ash::Shell::HasInstance())
    219     ash::Shell::GetInstance()->session_state_delegate()->
    220         RemoveSessionStateObserver(this);
    221 }
    222 
    223 void MultiUserWindowManagerChromeOS::SetWindowOwner(
    224     aura::Window* window,
    225     const std::string& user_id) {
    226   // Make sure the window is valid and there was no owner yet.
    227   DCHECK(window);
    228   DCHECK(!user_id.empty());
    229   if (GetWindowOwner(window) == user_id)
    230     return;
    231   DCHECK(GetWindowOwner(window).empty());
    232   window_to_entry_[window] = new WindowEntry(user_id);
    233 
    234   // Remember the initial visibility of the window.
    235   window_to_entry_[window]->set_show(window->IsVisible());
    236 
    237   // Set the window and the state observer.
    238   window->AddObserver(this);
    239   ash::wm::GetWindowState(window)->AddObserver(this);
    240 
    241   // Check if this window was created due to a user interaction. If it was,
    242   // transfer it to the current user.
    243   if (IsProcessingUserEvent())
    244     window_to_entry_[window]->set_show_for_user(current_user_id_);
    245 
    246   // Add all transient children to our set of windows. Note that the function
    247   // will add the children but not the owner to the transient children map.
    248   AddTransientOwnerRecursive(window, window);
    249 
    250   if (!IsWindowOnDesktopOfUser(window, current_user_id_))
    251     SetWindowVisibility(window, false);
    252 }
    253 
    254 const std::string& MultiUserWindowManagerChromeOS::GetWindowOwner(
    255     aura::Window* window) {
    256   WindowToEntryMap::iterator it = window_to_entry_.find(window);
    257   return it != window_to_entry_.end() ? it->second->owner()
    258                                       : base::EmptyString();
    259 }
    260 
    261 void MultiUserWindowManagerChromeOS::ShowWindowForUser(
    262     aura::Window* window,
    263     const std::string& user_id) {
    264   // If there is either no owner, or the owner is the current user, no action
    265   // is required.
    266   const std::string& owner = GetWindowOwner(window);
    267   if (owner.empty() ||
    268       (owner == user_id && IsWindowOnDesktopOfUser(window, user_id)))
    269     return;
    270 
    271   bool minimized = ash::wm::GetWindowState(window)->IsMinimized();
    272   // Check that we are not trying to transfer ownership of a minimized window.
    273   if (user_id != owner && minimized)
    274     return;
    275 
    276   if (minimized) {
    277     // If it is minimized it falls back to the original desktop.
    278     ash::MultiProfileUMA::RecordTeleportAction(
    279         ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_MINIMIZE);
    280   } else {
    281     // If the window was transferred without getting minimized, we should record
    282     // the window type.
    283     RecordUMAForTransferredWindowType(window);
    284   }
    285 
    286   WindowToEntryMap::iterator it = window_to_entry_.find(window);
    287   it->second->set_show_for_user(user_id);
    288 
    289   // Show the window if the added user is the current one.
    290   if (user_id == current_user_id_) {
    291     // Only show the window if it should be shown according to its state.
    292     if (it->second->show())
    293       SetWindowVisibility(window, true);
    294   } else {
    295     SetWindowVisibility(window, false);
    296   }
    297 }
    298 
    299 bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() {
    300   WindowToEntryMap::iterator it = window_to_entry_.begin();
    301   for (; it != window_to_entry_.end(); ++it) {
    302     if (it->second->owner() != it->second->show_for_user())
    303       return true;
    304   }
    305   return false;
    306 }
    307 
    308 bool MultiUserWindowManagerChromeOS::IsWindowOnDesktopOfUser(
    309     aura::Window* window,
    310     const std::string& user_id) {
    311   const std::string& presenting_user = GetUserPresentingWindow(window);
    312   return presenting_user.empty() || presenting_user == user_id;
    313 }
    314 
    315 const std::string& MultiUserWindowManagerChromeOS::GetUserPresentingWindow(
    316     aura::Window* window) {
    317   WindowToEntryMap::iterator it = window_to_entry_.find(window);
    318   // If the window is not owned by anyone it is shown on all desktops and we
    319   // return the empty string.
    320   if (it == window_to_entry_.end())
    321     return base::EmptyString();
    322   // Otherwise we ask the object for its desktop.
    323   return it->second->show_for_user();
    324 }
    325 
    326 void MultiUserWindowManagerChromeOS::AddUser(Profile* profile) {
    327   const std::string& user_id = multi_user_util::GetUserIDFromProfile(profile);
    328   if (user_id_to_app_observer_.find(user_id) != user_id_to_app_observer_.end())
    329     return;
    330 
    331   user_id_to_app_observer_[user_id] = new AppObserver(user_id);
    332   apps::ShellWindowRegistry::Get(profile)->AddObserver(
    333       user_id_to_app_observer_[user_id]);
    334 
    335   // Account all existing application windows of this user accordingly.
    336   const apps::ShellWindowRegistry::ShellWindowList& shell_windows =
    337       apps::ShellWindowRegistry::Get(profile)->shell_windows();
    338   apps::ShellWindowRegistry::ShellWindowList::const_iterator it =
    339       shell_windows.begin();
    340   for (; it != shell_windows.end(); ++it)
    341     user_id_to_app_observer_[user_id]->OnShellWindowAdded(*it);
    342 
    343   // Account all existing browser windows of this user accordingly.
    344   BrowserList* browser_list = BrowserList::GetInstance(HOST_DESKTOP_TYPE_ASH);
    345   BrowserList::const_iterator browser_it = browser_list->begin();
    346   for (; browser_it != browser_list->end(); ++browser_it) {
    347     if ((*browser_it)->profile()->GetOriginalProfile() == profile)
    348       AddBrowserWindow(*browser_it);
    349   }
    350 }
    351 
    352 void MultiUserWindowManagerChromeOS::ActiveUserChanged(
    353     const std::string& user_id) {
    354   DCHECK(user_id != current_user_id_);
    355   std::string old_user = current_user_id_;
    356   current_user_id_ = user_id;
    357   // Disable the window position manager and the MRU window tracker temporarily.
    358   scoped_ptr<UserChangeActionDisabler> disabler(new UserChangeActionDisabler);
    359 
    360   // We need to show/hide the windows in the same order as they were created in
    361   // their parent window(s) to keep the layer / window hierarchy in sync. To
    362   // achieve that we first collect all parent windows and then enumerate all
    363   // windows in those parent windows and show or hide them accordingly.
    364 
    365   // Create a list of all parent windows we have to check and their parents.
    366   std::set<aura::Window*> parent_list;
    367   for (WindowToEntryMap::iterator it = window_to_entry_.begin();
    368        it != window_to_entry_.end(); ++it) {
    369     aura::Window* parent = it->first->parent();
    370     if (parent_list.find(parent) == parent_list.end())
    371       parent_list.insert(parent);
    372   }
    373 
    374   // Traverse the found parent windows to handle their child windows in order of
    375   // their appearance.
    376   for (std::set<aura::Window*>::iterator it_parents = parent_list.begin();
    377        it_parents != parent_list.end(); ++it_parents) {
    378     const aura::Window::Windows window_list = (*it_parents)->children();
    379     for (aura::Window::Windows::const_iterator it_window = window_list.begin();
    380          it_window != window_list.end(); ++it_window) {
    381       aura::Window* window = *it_window;
    382       WindowToEntryMap::iterator it_map = window_to_entry_.find(window);
    383       if (it_map != window_to_entry_.end()) {
    384         bool should_be_visible = it_map->second->show_for_user() == user_id &&
    385                                  it_map->second->show();
    386         bool is_visible = window->IsVisible();
    387         if (should_be_visible != is_visible)
    388           SetWindowVisibility(window, should_be_visible);
    389       }
    390     }
    391   }
    392 
    393   // Finally we need to restore the previously active window.
    394   ash::MruWindowTracker::WindowList mru_list =
    395       ash::Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList();
    396   if (mru_list.size()) {
    397     aura::Window* window = mru_list[0];
    398     ash::wm::WindowState* window_state = ash::wm::GetWindowState(window);
    399     if (IsWindowOnDesktopOfUser(window, user_id) &&
    400         !window_state->IsMinimized()) {
    401       aura::client::ActivationClient* client =
    402           aura::client::GetActivationClient(window->GetRootWindow());
    403       // Several unit tests come here without an activation client.
    404       if (client)
    405         client->ActivateWindow(window);
    406     }
    407   }
    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   // Remove the state and the window observer.
    418   ash::wm::GetWindowState(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 
    424 void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanging(
    425     aura::Window* window, bool visible) {
    426   // This command gets called first and immediately when show or hide gets
    427   // called. We remember here the desired state for restoration IF we were
    428   // not ourselves issuing the call.
    429   // Note also that using the OnWindowVisibilityChanged callback cannot be
    430   // used for this.
    431   if (suppress_visibility_changes_)
    432     return;
    433 
    434   WindowToEntryMap::iterator it = window_to_entry_.find(window);
    435   // If the window is not owned by anyone it is shown on all desktops.
    436   if (it != window_to_entry_.end()) {
    437     // Remember what was asked for so that we can restore this when the user's
    438     // desktop gets restored.
    439     it->second->set_show(visible);
    440   } else {
    441     TransientWindowToVisibility::iterator it =
    442         transient_window_to_visibility_.find(window);
    443     if (it != transient_window_to_visibility_.end())
    444       it->second = visible;
    445   }
    446 }
    447 
    448 void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanged(
    449     aura::Window* window, bool visible) {
    450   if (suppress_visibility_changes_)
    451     return;
    452 
    453   // Don't allow to make the window visible if it shouldn't be.
    454   if (visible && !IsWindowOnDesktopOfUser(window, current_user_id_)) {
    455     SetWindowVisibility(window, false);
    456     return;
    457   }
    458   aura::Window* owned_parent = GetOwningWindowInTransientChain(window);
    459   if (owned_parent && owned_parent != window && visible &&
    460       !IsWindowOnDesktopOfUser(owned_parent, current_user_id_))
    461     SetWindowVisibility(window, false);
    462 }
    463 
    464 void MultiUserWindowManagerChromeOS::OnAddTransientChild(
    465     aura::Window* window,
    466     aura::Window* transient_window) {
    467   if (!GetWindowOwner(window).empty()) {
    468     AddTransientOwnerRecursive(transient_window, window);
    469     return;
    470   }
    471   aura::Window* owned_parent =
    472       GetOwningWindowInTransientChain(transient_window);
    473   if (!owned_parent)
    474     return;
    475 
    476   AddTransientOwnerRecursive(transient_window, owned_parent);
    477 }
    478 
    479 void MultiUserWindowManagerChromeOS::OnRemoveTransientChild(
    480     aura::Window* window,
    481     aura::Window* transient_window) {
    482   // Remove the transient child if the window itself is owned, or one of the
    483   // windows in its transient parents chain.
    484   if (!GetWindowOwner(window).empty() ||
    485       GetOwningWindowInTransientChain(window))
    486     RemoveTransientOwnerRecursive(transient_window);
    487 }
    488 
    489 void MultiUserWindowManagerChromeOS::OnWindowShowTypeChanged(
    490     ash::wm::WindowState* window_state,
    491     ash::wm::WindowShowType old_type) {
    492   if (!window_state->IsMinimized())
    493     return;
    494 
    495   aura::Window* window = window_state->window();
    496   // If the window was shown on a different users desktop: move it back.
    497   const std::string& owner = GetWindowOwner(window);
    498   if (!IsWindowOnDesktopOfUser(window, owner))
    499     ShowWindowForUser(window, owner);
    500 }
    501 
    502 void MultiUserWindowManagerChromeOS::Observe(
    503     int type,
    504     const content::NotificationSource& source,
    505     const content::NotificationDetails& details) {
    506   if (type == NOTIFICATION_BROWSER_WINDOW_READY)
    507     AddBrowserWindow(content::Source<Browser>(source).ptr());
    508 }
    509 
    510 void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) {
    511   // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can
    512   // come here with no valid window.
    513   if (!browser->window() || !browser->window()->GetNativeWindow())
    514     return;
    515   SetWindowOwner(browser->window()->GetNativeWindow(),
    516                  multi_user_util::GetUserIDFromProfile(browser->profile()));
    517 }
    518 
    519 void MultiUserWindowManagerChromeOS::SetWindowVisibility(
    520     aura::Window* window, bool visible) {
    521   if (window->IsVisible() == visible)
    522     return;
    523 
    524   // Hiding a system modal dialog should not be allowed. Instead we switch to
    525   // the user which is showing the system modal window.
    526   // Note that in some cases (e.g. unit test) windows might not have a root
    527   // window.
    528   if (!visible && window->GetRootWindow()) {
    529     // Get the system modal container for the window's root window.
    530     aura::Window* system_modal_container =
    531         window->GetRootWindow()->GetChildById(
    532             ash::internal::kShellWindowId_SystemModalContainer);
    533     if (window->parent() == system_modal_container) {
    534       // The window is system modal and we need to find the parent which owns
    535       // it so that we can switch to the desktop accordingly.
    536       std::string user_id = GetUserPresentingWindow(window);
    537       if (user_id.empty()) {
    538         aura::Window* owning_window = GetOwningWindowInTransientChain(window);
    539         DCHECK(owning_window);
    540         user_id = GetUserPresentingWindow(owning_window);
    541         DCHECK(!user_id.empty());
    542       }
    543       ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser(
    544           user_id);
    545       return;
    546     }
    547   }
    548 
    549   // To avoid that these commands are recorded as any other commands, we are
    550   // suppressing any window entry changes while this is going on.
    551   base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
    552 
    553   if (visible) {
    554     ShowWithTransientChildrenRecursive(window);
    555   } else {
    556     if (window->HasFocus())
    557       window->Blur();
    558     window->Hide();
    559   }
    560 }
    561 
    562 void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive(
    563     aura::Window* window) {
    564   aura::Window::Windows::const_iterator it =
    565       window->transient_children().begin();
    566   for (; it !=  window->transient_children().end(); ++it)
    567     ShowWithTransientChildrenRecursive(*it);
    568 
    569   // We show all children which were not explicitly hidden.
    570   TransientWindowToVisibility::iterator it2 =
    571       transient_window_to_visibility_.find(window);
    572   if (it2 == transient_window_to_visibility_.end() || it2->second)
    573     window->Show();
    574 }
    575 
    576 aura::Window* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain(
    577     aura::Window* window) {
    578   if (!GetWindowOwner(window).empty())
    579     return NULL;
    580   aura::Window* parent = window->transient_parent();
    581   while (parent) {
    582     if (!GetWindowOwner(parent).empty())
    583       return parent;
    584     parent = parent->transient_parent();
    585   }
    586   return NULL;
    587 }
    588 
    589 void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive(
    590     aura::Window* window,
    591     aura::Window* owned_parent) {
    592   // First add all child windows.
    593   aura::Window::Windows::const_iterator it =
    594       window->transient_children().begin();
    595   for (; it !=  window->transient_children().end(); ++it)
    596     AddTransientOwnerRecursive(*it, owned_parent);
    597 
    598   // If this window is the owned window, we do not have to handle it again.
    599   if (window == owned_parent)
    600     return;
    601 
    602   // Remember the current visibility.
    603   DCHECK(transient_window_to_visibility_.find(window) ==
    604              transient_window_to_visibility_.end());
    605   transient_window_to_visibility_[window] = window->IsVisible();
    606 
    607   // Add a window observer to make sure that we catch status changes.
    608   window->AddObserver(this);
    609 
    610   // Hide the window if it should not be shown. Note that this hide operation
    611   // will hide recursively this and all children - but we have already collected
    612   // their initial view state.
    613   if (!IsWindowOnDesktopOfUser(owned_parent, current_user_id_))
    614     SetWindowVisibility(window, false);
    615 }
    616 
    617 void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive(
    618     aura::Window* window) {
    619   // First remove all child windows.
    620   aura::Window::Windows::const_iterator it =
    621       window->transient_children().begin();
    622   for (; it !=  window->transient_children().end(); ++it)
    623     RemoveTransientOwnerRecursive(*it);
    624 
    625   // Find from transient window storage the visibility for the given window,
    626   // set the visibility accordingly and delete the window from the map.
    627   TransientWindowToVisibility::iterator visibility_item =
    628       transient_window_to_visibility_.find(window);
    629   DCHECK(visibility_item != transient_window_to_visibility_.end());
    630 
    631   // Remove the window observer.
    632   window->RemoveObserver(this);
    633 
    634   bool unowned_view_state = visibility_item->second;
    635   transient_window_to_visibility_.erase(visibility_item);
    636   if (unowned_view_state && !window->IsVisible()) {
    637     // To prevent these commands from being recorded as any other commands, we
    638     // are suppressing any window entry changes while this is going on.
    639     // Instead of calling SetWindowVisible, only show gets called here since all
    640     // dependents have been shown previously already.
    641     base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true);
    642     window->Show();
    643   }
    644 }
    645 
    646 }  // namespace chrome
    647