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