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