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"
46 // Checks if a given event is a user event.
47 bool IsUserEvent(ui::Event
* 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
)
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())
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();
81 if (IsUserEvent((*it
)->GetDispatcher()->current_event()))
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
;
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
;
101 window_type
= ash::MultiProfileUMA::TELEPORT_WINDOW_BROWSER
;
104 // Unit tests might come here without a profile manager.
105 if (!g_browser_process
->profile_manager())
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
);
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
;
124 window_type
= ash::MultiProfileUMA::TELEPORT_WINDOW_V2_APP
;
128 ash::MultiProfileUMA::RecordTeleportWindowType(window_type
);
135 // logic while the user gets switched.
136 class UserChangeActionDisabler
{
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(
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
{
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();
166 MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(window
,
169 virtual void OnShellWindowIconChanged(apps::ShellWindow
* shell_window
)
171 virtual void OnShellWindowRemoved(apps::ShellWindow
* shell_window
)
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
);
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
);
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.
234 DCHECK(!user_id
.empty());
235 if (GetWindowOwner(window
) == user_id
)
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
278 const std::string
& owner
= GetWindowOwner(window
);
280 (owner
== user_id
&& IsWindowOnDesktopOfUser(window
, user_id
)))
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
)
289 // If it is minimized it falls back to the original desktop.
290 ash::MultiProfileUMA::RecordTeleportAction(
291 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_MINIMIZE
);
293 // If the window was transferred without getting minimized, we should record
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);
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())
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())
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
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.
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
);
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
461 if (suppress_visibility_changes_
)
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
);
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_
)
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);
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
);
501 aura::Window
* owned_parent
=
502 GetOwningWindowInTransientChain(transient_window
);
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())
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(
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())
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
)
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
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(
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);
584 ShowWithTransientChildrenRecursive(window
);
586 if (window
->HasFocus())
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
)
606 aura::Window
* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain(
607 aura::Window
* window
) {
608 if (!GetWindowOwner(window
).empty())
610 aura::Window
* parent
= views::corewm::GetTransientParent(window
);
612 if (!GetWindowOwner(parent
).empty())
614 parent
= views::corewm::GetTransientParent(parent
);
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
)
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);
677 } // namespace chrome