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/app_window.h" 8 #include "apps/app_window_registry.h" 9 #include "ash/ash_switches.h" 10 #include "ash/multi_profile_uma.h" 11 #include "ash/root_window_controller.h" 12 #include "ash/session/session_state_delegate.h" 13 #include "ash/shelf/shelf.h" 14 #include "ash/shell.h" 15 #include "ash/shell_delegate.h" 16 #include "ash/shell_window_ids.h" 17 #include "ash/system/tray/system_tray_notifier.h" 18 #include "ash/wm/maximize_mode/maximize_mode_controller.h" 19 #include "ash/wm/window_state.h" 20 #include "base/auto_reset.h" 21 #include "base/message_loop/message_loop.h" 22 #include "base/strings/string_util.h" 23 #include "chrome/browser/browser_process.h" 24 #include "chrome/browser/chrome_notification_types.h" 25 #include "chrome/browser/chromeos/login/users/user_manager.h" 26 #include "chrome/browser/profiles/profile.h" 27 #include "chrome/browser/profiles/profile_manager.h" 28 #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h" 29 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" 30 #include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h" 31 #include "chrome/browser/ui/browser.h" 32 #include "chrome/browser/ui/browser_finder.h" 33 #include "chrome/browser/ui/browser_list.h" 34 #include "chrome/browser/ui/browser_window.h" 35 #include "content/public/browser/notification_service.h" 36 #include "google_apis/gaia/gaia_auth_util.h" 37 #include "ui/aura/client/aura_constants.h" 38 #include "ui/aura/window.h" 39 #include "ui/aura/window_event_dispatcher.h" 40 #include "ui/base/ui_base_types.h" 41 #include "ui/events/event.h" 42 #include "ui/message_center/message_center.h" 43 #include "ui/wm/core/transient_window_manager.h" 44 #include "ui/wm/core/window_animations.h" 45 #include "ui/wm/core/window_util.h" 46 47 namespace { 48 49 // The animation time in milliseconds for a single window which is fading 50 // in / out. 51 const int kAnimationTimeMS = 100; 52 53 // The animation time in milliseconds for the fade in and / or out when 54 // switching users. 55 const int kUserFadeTimeMS = 110; 56 57 // The animation time in ms for a window which get teleported to another screen. 58 const int kTeleportAnimationTimeMS = 300; 59 60 // Checks if a given event is a user event. 61 bool IsUserEvent(const ui::Event* e) { 62 if (e) { 63 ui::EventType type = e->type(); 64 if (type != ui::ET_CANCEL_MODE && 65 type != ui::ET_UMA_DATA && 66 type != ui::ET_UNKNOWN) 67 return true; 68 } 69 return false; 70 } 71 72 // Test if we are currently processing a user event which might lead to a 73 // browser / app creation. 74 bool IsProcessingUserEvent() { 75 // When there is a nested message loop (e.g. active menu or drag and drop 76 // operation) - we are in a nested loop and can ignore this. 77 // Note: Unit tests might not have a message loop. 78 base::MessageLoop* message_loop = base::MessageLoop::current(); 79 if (message_loop && message_loop->is_running() && message_loop->IsNested()) 80 return false; 81 82 // TODO(skuhne): "Open link in new window" will come here after the menu got 83 // closed, executing the command from the nested menu loop. However at that 84 // time there is no active event processed. A solution for that need to be 85 // found past M-32. A global event handler filter (pre and post) might fix 86 // that problem in conjunction with a depth counter - but - for the menu 87 // execution we come here after the loop was finished (so it's not nested 88 // anymore) and the root window should therefore still have the event which 89 // lead to the menu invocation, but it is not. By fixing that problem this 90 // would "magically work". 91 aura::Window::Windows root_window_list = ash::Shell::GetAllRootWindows(); 92 for (aura::Window::Windows::iterator it = root_window_list.begin(); 93 it != root_window_list.end(); 94 ++it) { 95 if (IsUserEvent((*it)->GetHost()->dispatcher()->current_event())) 96 return true; 97 } 98 return false; 99 } 100 101 // Records the type of window which was transferred to another desktop. 102 void RecordUMAForTransferredWindowType(aura::Window* window) { 103 // We need to figure out what kind of window this is to record the transfer. 104 Browser* browser = chrome::FindBrowserWithWindow(window); 105 ash::MultiProfileUMA::TeleportWindowType window_type = 106 ash::MultiProfileUMA::TELEPORT_WINDOW_UNKNOWN; 107 if (browser) { 108 if (browser->profile()->IsOffTheRecord()) { 109 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_INCOGNITO_BROWSER; 110 } else if (browser->is_app()) { 111 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V1_APP; 112 } else if (browser->is_type_popup()) { 113 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_POPUP; 114 } else { 115 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_BROWSER; 116 } 117 } else { 118 // Unit tests might come here without a profile manager. 119 if (!g_browser_process->profile_manager()) 120 return; 121 // If it is not a browser, it is probably be a V2 application. In that case 122 // one of the AppWindowRegistry instances should know about it. 123 apps::AppWindow* app_window = NULL; 124 std::vector<Profile*> profiles = 125 g_browser_process->profile_manager()->GetLoadedProfiles(); 126 for (std::vector<Profile*>::iterator it = profiles.begin(); 127 it != profiles.end() && app_window == NULL; 128 it++) { 129 app_window = apps::AppWindowRegistry::Get(*it) 130 ->GetAppWindowForNativeWindow(window); 131 } 132 if (app_window) { 133 if (app_window->window_type() == apps::AppWindow::WINDOW_TYPE_PANEL || 134 app_window->window_type() == apps::AppWindow::WINDOW_TYPE_V1_PANEL) { 135 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_PANEL; 136 } else { 137 window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V2_APP; 138 } 139 } 140 } 141 ash::MultiProfileUMA::RecordTeleportWindowType(window_type); 142 } 143 144 } // namespace 145 146 namespace chrome { 147 148 // A class to temporarily change the animation properties for a window. 149 class AnimationSetter { 150 public: 151 AnimationSetter(aura::Window* window, int animation_time_in_ms) 152 : window_(window), 153 previous_animation_type_( 154 wm::GetWindowVisibilityAnimationType(window_)), 155 previous_animation_time_( 156 wm::GetWindowVisibilityAnimationDuration(*window_)) { 157 wm::SetWindowVisibilityAnimationType( 158 window_, 159 wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE); 160 wm::SetWindowVisibilityAnimationDuration( 161 window_, 162 base::TimeDelta::FromMilliseconds(animation_time_in_ms)); 163 } 164 165 ~AnimationSetter() { 166 wm::SetWindowVisibilityAnimationType(window_, 167 previous_animation_type_); 168 wm::SetWindowVisibilityAnimationDuration( 169 window_, 170 previous_animation_time_); 171 } 172 173 private: 174 // The window which gets used. 175 aura::Window* window_; 176 177 // Previous animation type. 178 const int previous_animation_type_; 179 180 // Previous animation time. 181 const base::TimeDelta previous_animation_time_; 182 183 DISALLOW_COPY_AND_ASSIGN(AnimationSetter); 184 }; 185 186 // This class keeps track of all applications which were started for a user. 187 // When an app gets created, the window will be tagged for that user. Note 188 // that the destruction does not need to be tracked here since the universal 189 // window observer will take care of that. 190 class AppObserver : public apps::AppWindowRegistry::Observer { 191 public: 192 explicit AppObserver(const std::string& user_id) : user_id_(user_id) {} 193 virtual ~AppObserver() {} 194 195 // AppWindowRegistry::Observer overrides: 196 virtual void OnAppWindowAdded(apps::AppWindow* app_window) OVERRIDE { 197 aura::Window* window = app_window->GetNativeWindow(); 198 DCHECK(window); 199 MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(window, 200 user_id_); 201 } 202 203 private: 204 std::string user_id_; 205 206 DISALLOW_COPY_AND_ASSIGN(AppObserver); 207 }; 208 209 MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS( 210 const std::string& current_user_id) 211 : current_user_id_(current_user_id), 212 notification_blocker_(new MultiUserNotificationBlockerChromeOS( 213 message_center::MessageCenter::Get(), current_user_id)), 214 suppress_visibility_changes_(false), 215 animation_speed_(ANIMATION_SPEED_NORMAL) { 216 // Add a session state observer to be able to monitor session changes. 217 if (ash::Shell::HasInstance()) 218 ash::Shell::GetInstance()->session_state_delegate()-> 219 AddSessionStateObserver(this); 220 221 // The BrowserListObserver would have been better to use then the old 222 // notification system, but that observer fires before the window got created. 223 registrar_.Add(this, NOTIFICATION_BROWSER_WINDOW_READY, 224 content::NotificationService::AllSources()); 225 226 // Add an app window observer & all already running apps. 227 Profile* profile = multi_user_util::GetProfileFromUserID(current_user_id); 228 if (profile) 229 AddUser(profile); 230 } 231 232 MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() { 233 // When the MultiUserWindowManager gets destroyed, ash::Shell is mostly gone. 234 // As such we should not try to finalize any outstanding user animations. 235 // Note that the destruction of the object can be done later. 236 if (animation_.get()) 237 animation_->CancelAnimation(); 238 239 // Remove all window observers. 240 WindowToEntryMap::iterator window = window_to_entry_.begin(); 241 while (window != window_to_entry_.end()) { 242 OnWindowDestroyed(window->first); 243 window = window_to_entry_.begin(); 244 } 245 246 // Remove all app observers. 247 UserIDToAppWindowObserver::iterator app_observer_iterator = 248 user_id_to_app_observer_.begin(); 249 while (app_observer_iterator != user_id_to_app_observer_.end()) { 250 Profile* profile = multi_user_util::GetProfileFromUserID( 251 app_observer_iterator->first); 252 DCHECK(profile); 253 apps::AppWindowRegistry::Get(profile) 254 ->RemoveObserver(app_observer_iterator->second); 255 delete app_observer_iterator->second; 256 user_id_to_app_observer_.erase(app_observer_iterator); 257 app_observer_iterator = user_id_to_app_observer_.begin(); 258 } 259 260 if (ash::Shell::HasInstance()) 261 ash::Shell::GetInstance()->session_state_delegate()-> 262 RemoveSessionStateObserver(this); 263 } 264 265 void MultiUserWindowManagerChromeOS::SetWindowOwner( 266 aura::Window* window, 267 const std::string& user_id) { 268 // Make sure the window is valid and there was no owner yet. 269 DCHECK(window); 270 DCHECK(!user_id.empty()); 271 if (GetWindowOwner(window) == user_id) 272 return; 273 DCHECK(GetWindowOwner(window).empty()); 274 window_to_entry_[window] = new WindowEntry(user_id); 275 276 // Remember the initial visibility of the window. 277 window_to_entry_[window]->set_show(window->IsVisible()); 278 279 // Add observers to track state changes. 280 window->AddObserver(this); 281 wm::TransientWindowManager::Get(window)->AddObserver(this); 282 283 // Check if this window was created due to a user interaction. If it was, 284 // transfer it to the current user. 285 if (IsProcessingUserEvent()) 286 window_to_entry_[window]->set_show_for_user(current_user_id_); 287 288 // Add all transient children to our set of windows. Note that the function 289 // will add the children but not the owner to the transient children map. 290 AddTransientOwnerRecursive(window, window); 291 292 // Notify entry adding. 293 FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryAdded(window)); 294 295 if (!IsWindowOnDesktopOfUser(window, current_user_id_)) 296 SetWindowVisibility(window, false, 0); 297 } 298 299 const std::string& MultiUserWindowManagerChromeOS::GetWindowOwner( 300 aura::Window* window) const { 301 WindowToEntryMap::const_iterator it = window_to_entry_.find(window); 302 return it != window_to_entry_.end() ? it->second->owner() 303 : base::EmptyString(); 304 } 305 306 void MultiUserWindowManagerChromeOS::ShowWindowForUser( 307 aura::Window* window, 308 const std::string& user_id) { 309 std::string previous_owner(GetUserPresentingWindow(window)); 310 if (!ShowWindowForUserIntern(window, user_id)) 311 return; 312 // The window switched to a new desktop and we have to switch to that desktop, 313 // but only when it was on the visible desktop and the the target is not the 314 // visible desktop. 315 if (user_id == current_user_id_ || previous_owner != current_user_id_) 316 return; 317 318 ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser( 319 user_id); 320 } 321 322 bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() const { 323 WindowToEntryMap::const_iterator it = window_to_entry_.begin(); 324 for (; it != window_to_entry_.end(); ++it) { 325 if (it->second->owner() != it->second->show_for_user()) 326 return true; 327 } 328 return false; 329 } 330 331 void MultiUserWindowManagerChromeOS::GetOwnersOfVisibleWindows( 332 std::set<std::string>* user_ids) const { 333 for (WindowToEntryMap::const_iterator it = window_to_entry_.begin(); 334 it != window_to_entry_.end(); 335 ++it) { 336 if (it->first->IsVisible()) 337 user_ids->insert(it->second->owner()); 338 } 339 } 340 341 bool MultiUserWindowManagerChromeOS::IsWindowOnDesktopOfUser( 342 aura::Window* window, 343 const std::string& user_id) const { 344 const std::string& presenting_user = GetUserPresentingWindow(window); 345 return presenting_user.empty() || presenting_user == user_id; 346 } 347 348 const std::string& MultiUserWindowManagerChromeOS::GetUserPresentingWindow( 349 aura::Window* window) const { 350 WindowToEntryMap::const_iterator it = window_to_entry_.find(window); 351 // If the window is not owned by anyone it is shown on all desktops and we 352 // return the empty string. 353 if (it == window_to_entry_.end()) 354 return base::EmptyString(); 355 // Otherwise we ask the object for its desktop. 356 return it->second->show_for_user(); 357 } 358 359 void MultiUserWindowManagerChromeOS::AddUser(content::BrowserContext* context) { 360 Profile* profile = Profile::FromBrowserContext(context); 361 const std::string& user_id = multi_user_util::GetUserIDFromProfile(profile); 362 if (user_id_to_app_observer_.find(user_id) != user_id_to_app_observer_.end()) 363 return; 364 365 user_id_to_app_observer_[user_id] = new AppObserver(user_id); 366 apps::AppWindowRegistry::Get(profile) 367 ->AddObserver(user_id_to_app_observer_[user_id]); 368 369 // Account all existing application windows of this user accordingly. 370 const apps::AppWindowRegistry::AppWindowList& app_windows = 371 apps::AppWindowRegistry::Get(profile)->app_windows(); 372 apps::AppWindowRegistry::AppWindowList::const_iterator it = 373 app_windows.begin(); 374 for (; it != app_windows.end(); ++it) 375 user_id_to_app_observer_[user_id]->OnAppWindowAdded(*it); 376 377 // Account all existing browser windows of this user accordingly. 378 BrowserList* browser_list = BrowserList::GetInstance(HOST_DESKTOP_TYPE_ASH); 379 BrowserList::const_iterator browser_it = browser_list->begin(); 380 for (; browser_it != browser_list->end(); ++browser_it) { 381 if ((*browser_it)->profile()->GetOriginalProfile() == profile) 382 AddBrowserWindow(*browser_it); 383 } 384 } 385 386 void MultiUserWindowManagerChromeOS::AddObserver(Observer* observer) { 387 observers_.AddObserver(observer); 388 } 389 390 void MultiUserWindowManagerChromeOS::RemoveObserver(Observer* observer) { 391 observers_.RemoveObserver(observer); 392 } 393 394 void MultiUserWindowManagerChromeOS::ActiveUserChanged( 395 const std::string& user_id) { 396 DCHECK(user_id != current_user_id_); 397 // This needs to be set before the animation starts. 398 current_user_id_ = user_id; 399 400 animation_.reset( 401 new UserSwichAnimatorChromeOS( 402 this, user_id, GetAdjustedAnimationTimeInMS(kUserFadeTimeMS))); 403 // Call notifier here instead of observing ActiveUserChanged because 404 // this must happen after MultiUserWindowManagerChromeOS is notified. 405 ash::Shell::GetInstance() 406 ->system_tray_notifier() 407 ->NotifyMediaCaptureChanged(); 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 wm::TransientWindowManager::Get(window)->RemoveObserver(this); 418 // Remove the window from the owners list. 419 delete window_to_entry_[window]; 420 window_to_entry_.erase(window); 421 422 // Notify entry change. 423 FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryRemoved(window)); 424 } 425 426 void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanging( 427 aura::Window* window, bool visible) { 428 // This command gets called first and immediately when show or hide gets 429 // called. We remember here the desired state for restoration IF we were 430 // not ourselves issuing the call. 431 // Note also that using the OnWindowVisibilityChanged callback cannot be 432 // used for this. 433 if (suppress_visibility_changes_) 434 return; 435 436 WindowToEntryMap::iterator it = window_to_entry_.find(window); 437 // If the window is not owned by anyone it is shown on all desktops. 438 if (it != window_to_entry_.end()) { 439 // Remember what was asked for so that we can restore this when the user's 440 // desktop gets restored. 441 it->second->set_show(visible); 442 } else { 443 TransientWindowToVisibility::iterator it = 444 transient_window_to_visibility_.find(window); 445 if (it != transient_window_to_visibility_.end()) 446 it->second = visible; 447 } 448 } 449 450 void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanged( 451 aura::Window* window, bool visible) { 452 if (suppress_visibility_changes_) 453 return; 454 455 // Don't allow to make the window visible if it shouldn't be. 456 if (visible && !IsWindowOnDesktopOfUser(window, current_user_id_)) { 457 SetWindowVisibility(window, false, 0); 458 return; 459 } 460 aura::Window* owned_parent = GetOwningWindowInTransientChain(window); 461 if (owned_parent && owned_parent != window && visible && 462 !IsWindowOnDesktopOfUser(owned_parent, current_user_id_)) 463 SetWindowVisibility(window, false, 0); 464 } 465 466 void MultiUserWindowManagerChromeOS::OnTransientChildAdded( 467 aura::Window* window, 468 aura::Window* transient_window) { 469 if (!GetWindowOwner(window).empty()) { 470 AddTransientOwnerRecursive(transient_window, window); 471 return; 472 } 473 aura::Window* owned_parent = 474 GetOwningWindowInTransientChain(transient_window); 475 if (!owned_parent) 476 return; 477 478 AddTransientOwnerRecursive(transient_window, owned_parent); 479 } 480 481 void MultiUserWindowManagerChromeOS::OnTransientChildRemoved( 482 aura::Window* window, 483 aura::Window* transient_window) { 484 // Remove the transient child if the window itself is owned, or one of the 485 // windows in its transient parents chain. 486 if (!GetWindowOwner(window).empty() || 487 GetOwningWindowInTransientChain(window)) 488 RemoveTransientOwnerRecursive(transient_window); 489 } 490 491 void MultiUserWindowManagerChromeOS::Observe( 492 int type, 493 const content::NotificationSource& source, 494 const content::NotificationDetails& details) { 495 if (type == NOTIFICATION_BROWSER_WINDOW_READY) 496 AddBrowserWindow(content::Source<Browser>(source).ptr()); 497 } 498 499 void MultiUserWindowManagerChromeOS::SetAnimationSpeedForTest( 500 MultiUserWindowManagerChromeOS::AnimationSpeed speed) { 501 animation_speed_ = speed; 502 } 503 504 bool MultiUserWindowManagerChromeOS::IsAnimationRunningForTest() { 505 return animation_.get() != NULL && !animation_->IsAnimationFinished(); 506 } 507 508 const std::string& MultiUserWindowManagerChromeOS::GetCurrentUserForTest() 509 const { 510 return current_user_id_; 511 } 512 513 bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern( 514 aura::Window* window, 515 const std::string& user_id) { 516 // If there is either no owner, or the owner is the current user, no action 517 // is required. 518 const std::string& owner = GetWindowOwner(window); 519 if (owner.empty() || 520 (owner == user_id && IsWindowOnDesktopOfUser(window, user_id))) 521 return false; 522 523 bool minimized = ash::wm::GetWindowState(window)->IsMinimized(); 524 // Check that we are not trying to transfer ownership of a minimized window. 525 if (user_id != owner && minimized) 526 return false; 527 528 if (minimized) { 529 // If it is minimized it falls back to the original desktop. 530 ash::MultiProfileUMA::RecordTeleportAction( 531 ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_MINIMIZE); 532 } else { 533 // If the window was transferred without getting minimized, we should record 534 // the window type. 535 RecordUMAForTransferredWindowType(window); 536 } 537 538 WindowToEntryMap::iterator it = window_to_entry_.find(window); 539 it->second->set_show_for_user(user_id); 540 541 // Show the window if the added user is the current one. 542 if (user_id == current_user_id_) { 543 // Only show the window if it should be shown according to its state. 544 if (it->second->show()) 545 SetWindowVisibility(window, true, kTeleportAnimationTimeMS); 546 } else { 547 SetWindowVisibility(window, false, kTeleportAnimationTimeMS); 548 } 549 550 // Notify entry change. 551 FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryChanged(window)); 552 return true; 553 } 554 555 void MultiUserWindowManagerChromeOS::SetWindowVisibility( 556 aura::Window* window, bool visible, int animation_time_in_ms) { 557 if (window->IsVisible() == visible) 558 return; 559 560 // Hiding a system modal dialog should not be allowed. Instead we switch to 561 // the user which is showing the system modal window. 562 // Note that in some cases (e.g. unit test) windows might not have a root 563 // window. 564 if (!visible && window->GetRootWindow()) { 565 // Get the system modal container for the window's root window. 566 aura::Window* system_modal_container = 567 window->GetRootWindow()->GetChildById( 568 ash::kShellWindowId_SystemModalContainer); 569 if (window->parent() == system_modal_container) { 570 // The window is system modal and we need to find the parent which owns 571 // it so that we can switch to the desktop accordingly. 572 std::string user_id = GetUserPresentingWindow(window); 573 if (user_id.empty()) { 574 aura::Window* owning_window = GetOwningWindowInTransientChain(window); 575 DCHECK(owning_window); 576 user_id = GetUserPresentingWindow(owning_window); 577 DCHECK(!user_id.empty()); 578 } 579 ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser( 580 user_id); 581 return; 582 } 583 } 584 585 // To avoid that these commands are recorded as any other commands, we are 586 // suppressing any window entry changes while this is going on. 587 base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true); 588 589 if (visible) { 590 ShowWithTransientChildrenRecursive(window, animation_time_in_ms); 591 } else { 592 if (window->HasFocus()) 593 window->Blur(); 594 SetWindowVisible(window, false, animation_time_in_ms); 595 } 596 } 597 598 void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) { 599 // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can 600 // come here with no valid window. 601 if (!browser->window() || !browser->window()->GetNativeWindow()) 602 return; 603 SetWindowOwner(browser->window()->GetNativeWindow(), 604 multi_user_util::GetUserIDFromProfile(browser->profile())); 605 } 606 607 void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive( 608 aura::Window* window, int animation_time_in_ms) { 609 aura::Window::Windows::const_iterator it = 610 wm::GetTransientChildren(window).begin(); 611 for (; it != wm::GetTransientChildren(window).end(); ++it) 612 ShowWithTransientChildrenRecursive(*it, animation_time_in_ms); 613 614 // We show all children which were not explicitly hidden. 615 TransientWindowToVisibility::iterator it2 = 616 transient_window_to_visibility_.find(window); 617 if (it2 == transient_window_to_visibility_.end() || it2->second) 618 SetWindowVisible(window, true, animation_time_in_ms); 619 } 620 621 aura::Window* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain( 622 aura::Window* window) const { 623 if (!GetWindowOwner(window).empty()) 624 return NULL; 625 aura::Window* parent = wm::GetTransientParent(window); 626 while (parent) { 627 if (!GetWindowOwner(parent).empty()) 628 return parent; 629 parent = wm::GetTransientParent(parent); 630 } 631 return NULL; 632 } 633 634 void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive( 635 aura::Window* window, 636 aura::Window* owned_parent) { 637 // First add all child windows. 638 aura::Window::Windows::const_iterator it = 639 wm::GetTransientChildren(window).begin(); 640 for (; it != wm::GetTransientChildren(window).end(); ++it) 641 AddTransientOwnerRecursive(*it, owned_parent); 642 643 // If this window is the owned window, we do not have to handle it again. 644 if (window == owned_parent) 645 return; 646 647 // Remember the current visibility. 648 DCHECK(transient_window_to_visibility_.find(window) == 649 transient_window_to_visibility_.end()); 650 transient_window_to_visibility_[window] = window->IsVisible(); 651 652 // Add observers to track state changes. 653 window->AddObserver(this); 654 wm::TransientWindowManager::Get(window)->AddObserver(this); 655 656 // Hide the window if it should not be shown. Note that this hide operation 657 // will hide recursively this and all children - but we have already collected 658 // their initial view state. 659 if (!IsWindowOnDesktopOfUser(owned_parent, current_user_id_)) 660 SetWindowVisibility(window, false, kAnimationTimeMS); 661 } 662 663 void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive( 664 aura::Window* window) { 665 // First remove all child windows. 666 aura::Window::Windows::const_iterator it = 667 wm::GetTransientChildren(window).begin(); 668 for (; it != wm::GetTransientChildren(window).end(); ++it) 669 RemoveTransientOwnerRecursive(*it); 670 671 // Find from transient window storage the visibility for the given window, 672 // set the visibility accordingly and delete the window from the map. 673 TransientWindowToVisibility::iterator visibility_item = 674 transient_window_to_visibility_.find(window); 675 DCHECK(visibility_item != transient_window_to_visibility_.end()); 676 677 window->RemoveObserver(this); 678 wm::TransientWindowManager::Get(window)->RemoveObserver(this); 679 680 bool unowned_view_state = visibility_item->second; 681 transient_window_to_visibility_.erase(visibility_item); 682 if (unowned_view_state && !window->IsVisible()) { 683 // To prevent these commands from being recorded as any other commands, we 684 // are suppressing any window entry changes while this is going on. 685 // Instead of calling SetWindowVisible, only show gets called here since all 686 // dependents have been shown previously already. 687 base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true); 688 window->Show(); 689 } 690 } 691 692 void MultiUserWindowManagerChromeOS::SetWindowVisible( 693 aura::Window* window, 694 bool visible, 695 int animation_time_in_ms) { 696 // The MaximizeModeWindowManager will not handle invisible windows since they 697 // are not user activatable. Since invisible windows are not being tracked, 698 // we tell it to maximize / track this window now before it gets shown, to 699 // reduce animation jank from multiple resizes. 700 if (visible) 701 ash::Shell::GetInstance()->maximize_mode_controller()->AddWindow(window); 702 703 AnimationSetter animation_setter( 704 window, 705 GetAdjustedAnimationTimeInMS(animation_time_in_ms)); 706 707 if (visible) 708 window->Show(); 709 else 710 window->Hide(); 711 712 // Make sure that animations have no influence on the window state after the 713 // call. 714 DCHECK_EQ(visible, window->IsVisible()); 715 } 716 717 int MultiUserWindowManagerChromeOS::GetAdjustedAnimationTimeInMS( 718 int default_time_in_ms) const { 719 return animation_speed_ == ANIMATION_SPEED_NORMAL ? default_time_in_ms : 720 (animation_speed_ == ANIMATION_SPEED_FAST ? 10 : 0); 721 } 722 723 } // namespace chrome 724