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