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
40namespace {
41
42// Checks if a given event is a user event.
43bool 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.
56bool 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.
84void 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
129namespace chrome {
130
131// logic while the user gets switched.
132class 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.
153class 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
176MultiUserWindowManagerChromeOS::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
196MultiUserWindowManagerChromeOS::~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
223void 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
254const 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
261void 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
299bool 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
308bool 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
315const 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
326void 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
352void 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
410void 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
424void 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
448void 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
464void 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
479void 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
489void 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
502void 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
510void 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
519void 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
562void 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
576aura::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
589void 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
617void 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