| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h" |
| |
| #include "ash/ash_switches.h" |
| #include "ash/multi_profile_uma.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/session/session_state_delegate.h" |
| #include "ash/shelf/shelf.h" |
| #include "ash/shell.h" |
| #include "ash/shell_delegate.h" |
| #include "ash/shell_window_ids.h" |
| #include "ash/system/tray/system_tray_notifier.h" |
| #include "ash/wm/maximize_mode/maximize_mode_controller.h" |
| #include "ash/wm/window_state.h" |
| #include "base/auto_reset.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/strings/string_util.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/chromeos/login/session/user_session_manager.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h" |
| #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" |
| #include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "content/public/browser/notification_service.h" |
| #include "extensions/browser/app_window/app_window.h" |
| #include "extensions/browser/app_window/app_window_registry.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_event_dispatcher.h" |
| #include "ui/base/ui_base_types.h" |
| #include "ui/events/event.h" |
| #include "ui/message_center/message_center.h" |
| #include "ui/wm/core/transient_window_manager.h" |
| #include "ui/wm/core/window_animations.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace { |
| |
| // The animation time in milliseconds for a single window which is fading |
| // in / out. |
| const int kAnimationTimeMS = 100; |
| |
| // The animation time in milliseconds for the fade in and / or out when |
| // switching users. |
| const int kUserFadeTimeMS = 110; |
| |
| // The animation time in ms for a window which get teleported to another screen. |
| const int kTeleportAnimationTimeMS = 300; |
| |
| // Checks if a given event is a user event. |
| bool IsUserEvent(const ui::Event* e) { |
| if (e) { |
| ui::EventType type = e->type(); |
| if (type != ui::ET_CANCEL_MODE && |
| type != ui::ET_UMA_DATA && |
| type != ui::ET_UNKNOWN) |
| return true; |
| } |
| return false; |
| } |
| |
| // Test if we are currently processing a user event which might lead to a |
| // browser / app creation. |
| bool IsProcessingUserEvent() { |
| // When there is a nested message loop (e.g. active menu or drag and drop |
| // operation) - we are in a nested loop and can ignore this. |
| // Note: Unit tests might not have a message loop. |
| base::MessageLoop* message_loop = base::MessageLoop::current(); |
| if (message_loop && message_loop->is_running() && message_loop->IsNested()) |
| return false; |
| |
| // TODO(skuhne): "Open link in new window" will come here after the menu got |
| // closed, executing the command from the nested menu loop. However at that |
| // time there is no active event processed. A solution for that need to be |
| // found past M-32. A global event handler filter (pre and post) might fix |
| // that problem in conjunction with a depth counter - but - for the menu |
| // execution we come here after the loop was finished (so it's not nested |
| // anymore) and the root window should therefore still have the event which |
| // lead to the menu invocation, but it is not. By fixing that problem this |
| // would "magically work". |
| aura::Window::Windows root_window_list = ash::Shell::GetAllRootWindows(); |
| for (aura::Window::Windows::iterator it = root_window_list.begin(); |
| it != root_window_list.end(); |
| ++it) { |
| if (IsUserEvent((*it)->GetHost()->dispatcher()->current_event())) |
| return true; |
| } |
| return false; |
| } |
| |
| // Records the type of window which was transferred to another desktop. |
| void RecordUMAForTransferredWindowType(aura::Window* window) { |
| // We need to figure out what kind of window this is to record the transfer. |
| Browser* browser = chrome::FindBrowserWithWindow(window); |
| ash::MultiProfileUMA::TeleportWindowType window_type = |
| ash::MultiProfileUMA::TELEPORT_WINDOW_UNKNOWN; |
| if (browser) { |
| if (browser->profile()->IsOffTheRecord()) { |
| window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_INCOGNITO_BROWSER; |
| } else if (browser->is_app()) { |
| window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V1_APP; |
| } else if (browser->is_type_popup()) { |
| window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_POPUP; |
| } else { |
| window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_BROWSER; |
| } |
| } else { |
| // Unit tests might come here without a profile manager. |
| if (!g_browser_process->profile_manager()) |
| return; |
| // If it is not a browser, it is probably be a V2 application. In that case |
| // one of the AppWindowRegistry instances should know about it. |
| extensions::AppWindow* app_window = NULL; |
| std::vector<Profile*> profiles = |
| g_browser_process->profile_manager()->GetLoadedProfiles(); |
| for (std::vector<Profile*>::iterator it = profiles.begin(); |
| it != profiles.end() && app_window == NULL; |
| it++) { |
| app_window = extensions::AppWindowRegistry::Get(*it) |
| ->GetAppWindowForNativeWindow(window); |
| } |
| if (app_window) { |
| if (app_window->window_type() == |
| extensions::AppWindow::WINDOW_TYPE_PANEL || |
| app_window->window_type() == |
| extensions::AppWindow::WINDOW_TYPE_V1_PANEL) { |
| window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_PANEL; |
| } else { |
| window_type = ash::MultiProfileUMA::TELEPORT_WINDOW_V2_APP; |
| } |
| } |
| } |
| ash::MultiProfileUMA::RecordTeleportWindowType(window_type); |
| } |
| |
| } // namespace |
| |
| namespace chrome { |
| |
| // A class to temporarily change the animation properties for a window. |
| class AnimationSetter { |
| public: |
| AnimationSetter(aura::Window* window, int animation_time_in_ms) |
| : window_(window), |
| previous_animation_type_( |
| wm::GetWindowVisibilityAnimationType(window_)), |
| previous_animation_time_( |
| wm::GetWindowVisibilityAnimationDuration(*window_)) { |
| wm::SetWindowVisibilityAnimationType( |
| window_, |
| wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE); |
| wm::SetWindowVisibilityAnimationDuration( |
| window_, |
| base::TimeDelta::FromMilliseconds(animation_time_in_ms)); |
| } |
| |
| ~AnimationSetter() { |
| wm::SetWindowVisibilityAnimationType(window_, |
| previous_animation_type_); |
| wm::SetWindowVisibilityAnimationDuration( |
| window_, |
| previous_animation_time_); |
| } |
| |
| private: |
| // The window which gets used. |
| aura::Window* window_; |
| |
| // Previous animation type. |
| const int previous_animation_type_; |
| |
| // Previous animation time. |
| const base::TimeDelta previous_animation_time_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AnimationSetter); |
| }; |
| |
| // This class keeps track of all applications which were started for a user. |
| // When an app gets created, the window will be tagged for that user. Note |
| // that the destruction does not need to be tracked here since the universal |
| // window observer will take care of that. |
| class AppObserver : public extensions::AppWindowRegistry::Observer { |
| public: |
| explicit AppObserver(const std::string& user_id) : user_id_(user_id) {} |
| ~AppObserver() override {} |
| |
| // AppWindowRegistry::Observer overrides: |
| void OnAppWindowAdded(extensions::AppWindow* app_window) override { |
| aura::Window* window = app_window->GetNativeWindow(); |
| DCHECK(window); |
| MultiUserWindowManagerChromeOS::GetInstance()->SetWindowOwner(window, |
| user_id_); |
| } |
| |
| private: |
| std::string user_id_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AppObserver); |
| }; |
| |
| MultiUserWindowManagerChromeOS::MultiUserWindowManagerChromeOS( |
| const std::string& current_user_id) |
| : current_user_id_(current_user_id), |
| notification_blocker_(new MultiUserNotificationBlockerChromeOS( |
| message_center::MessageCenter::Get(), current_user_id)), |
| suppress_visibility_changes_(false), |
| animation_speed_(ANIMATION_SPEED_NORMAL) { |
| // Add a session state observer to be able to monitor session changes. |
| if (ash::Shell::HasInstance()) |
| ash::Shell::GetInstance()->session_state_delegate()-> |
| AddSessionStateObserver(this); |
| |
| // The BrowserListObserver would have been better to use then the old |
| // notification system, but that observer fires before the window got created. |
| registrar_.Add(this, NOTIFICATION_BROWSER_WINDOW_READY, |
| content::NotificationService::AllSources()); |
| |
| // Add an app window observer & all already running apps. |
| Profile* profile = multi_user_util::GetProfileFromUserID(current_user_id); |
| if (profile) |
| AddUser(profile); |
| } |
| |
| MultiUserWindowManagerChromeOS::~MultiUserWindowManagerChromeOS() { |
| // When the MultiUserWindowManager gets destroyed, ash::Shell is mostly gone. |
| // As such we should not try to finalize any outstanding user animations. |
| // Note that the destruction of the object can be done later. |
| if (animation_.get()) |
| animation_->CancelAnimation(); |
| |
| // Remove all window observers. |
| WindowToEntryMap::iterator window = window_to_entry_.begin(); |
| while (window != window_to_entry_.end()) { |
| OnWindowDestroyed(window->first); |
| window = window_to_entry_.begin(); |
| } |
| |
| // Remove all app observers. |
| UserIDToAppWindowObserver::iterator app_observer_iterator = |
| user_id_to_app_observer_.begin(); |
| while (app_observer_iterator != user_id_to_app_observer_.end()) { |
| Profile* profile = multi_user_util::GetProfileFromUserID( |
| app_observer_iterator->first); |
| DCHECK(profile); |
| extensions::AppWindowRegistry::Get(profile) |
| ->RemoveObserver(app_observer_iterator->second); |
| delete app_observer_iterator->second; |
| user_id_to_app_observer_.erase(app_observer_iterator); |
| app_observer_iterator = user_id_to_app_observer_.begin(); |
| } |
| |
| if (ash::Shell::HasInstance()) |
| ash::Shell::GetInstance()->session_state_delegate()-> |
| RemoveSessionStateObserver(this); |
| } |
| |
| void MultiUserWindowManagerChromeOS::SetWindowOwner( |
| aura::Window* window, |
| const std::string& user_id) { |
| // Make sure the window is valid and there was no owner yet. |
| DCHECK(window); |
| DCHECK(!user_id.empty()); |
| if (GetWindowOwner(window) == user_id) |
| return; |
| DCHECK(GetWindowOwner(window).empty()); |
| window_to_entry_[window] = new WindowEntry(user_id); |
| |
| // Remember the initial visibility of the window. |
| window_to_entry_[window]->set_show(window->IsVisible()); |
| |
| // Add observers to track state changes. |
| window->AddObserver(this); |
| wm::TransientWindowManager::Get(window)->AddObserver(this); |
| |
| // Check if this window was created due to a user interaction. If it was, |
| // transfer it to the current user. |
| if (IsProcessingUserEvent()) |
| window_to_entry_[window]->set_show_for_user(current_user_id_); |
| |
| // Add all transient children to our set of windows. Note that the function |
| // will add the children but not the owner to the transient children map. |
| AddTransientOwnerRecursive(window, window); |
| |
| // Notify entry adding. |
| FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryAdded(window)); |
| |
| if (!IsWindowOnDesktopOfUser(window, current_user_id_)) |
| SetWindowVisibility(window, false, 0); |
| } |
| |
| const std::string& MultiUserWindowManagerChromeOS::GetWindowOwner( |
| aura::Window* window) const { |
| WindowToEntryMap::const_iterator it = window_to_entry_.find(window); |
| return it != window_to_entry_.end() ? it->second->owner() |
| : base::EmptyString(); |
| } |
| |
| void MultiUserWindowManagerChromeOS::ShowWindowForUser( |
| aura::Window* window, |
| const std::string& user_id) { |
| std::string previous_owner(GetUserPresentingWindow(window)); |
| if (!ShowWindowForUserIntern(window, user_id)) |
| return; |
| // The window switched to a new desktop and we have to switch to that desktop, |
| // but only when it was on the visible desktop and the the target is not the |
| // visible desktop. |
| if (user_id == current_user_id_ || previous_owner != current_user_id_) |
| return; |
| |
| ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser( |
| user_id); |
| } |
| |
| bool MultiUserWindowManagerChromeOS::AreWindowsSharedAmongUsers() const { |
| WindowToEntryMap::const_iterator it = window_to_entry_.begin(); |
| for (; it != window_to_entry_.end(); ++it) { |
| if (it->second->owner() != it->second->show_for_user()) |
| return true; |
| } |
| return false; |
| } |
| |
| void MultiUserWindowManagerChromeOS::GetOwnersOfVisibleWindows( |
| std::set<std::string>* user_ids) const { |
| for (WindowToEntryMap::const_iterator it = window_to_entry_.begin(); |
| it != window_to_entry_.end(); |
| ++it) { |
| if (it->first->IsVisible()) |
| user_ids->insert(it->second->owner()); |
| } |
| } |
| |
| bool MultiUserWindowManagerChromeOS::IsWindowOnDesktopOfUser( |
| aura::Window* window, |
| const std::string& user_id) const { |
| const std::string& presenting_user = GetUserPresentingWindow(window); |
| return presenting_user.empty() || presenting_user == user_id; |
| } |
| |
| const std::string& MultiUserWindowManagerChromeOS::GetUserPresentingWindow( |
| aura::Window* window) const { |
| WindowToEntryMap::const_iterator it = window_to_entry_.find(window); |
| // If the window is not owned by anyone it is shown on all desktops and we |
| // return the empty string. |
| if (it == window_to_entry_.end()) |
| return base::EmptyString(); |
| // Otherwise we ask the object for its desktop. |
| return it->second->show_for_user(); |
| } |
| |
| void MultiUserWindowManagerChromeOS::AddUser(content::BrowserContext* context) { |
| Profile* profile = Profile::FromBrowserContext(context); |
| const std::string& user_id = multi_user_util::GetUserIDFromProfile(profile); |
| if (user_id_to_app_observer_.find(user_id) != user_id_to_app_observer_.end()) |
| return; |
| |
| user_id_to_app_observer_[user_id] = new AppObserver(user_id); |
| extensions::AppWindowRegistry::Get(profile) |
| ->AddObserver(user_id_to_app_observer_[user_id]); |
| |
| // Account all existing application windows of this user accordingly. |
| const extensions::AppWindowRegistry::AppWindowList& app_windows = |
| extensions::AppWindowRegistry::Get(profile)->app_windows(); |
| extensions::AppWindowRegistry::AppWindowList::const_iterator it = |
| app_windows.begin(); |
| for (; it != app_windows.end(); ++it) |
| user_id_to_app_observer_[user_id]->OnAppWindowAdded(*it); |
| |
| // Account all existing browser windows of this user accordingly. |
| BrowserList* browser_list = BrowserList::GetInstance(HOST_DESKTOP_TYPE_ASH); |
| BrowserList::const_iterator browser_it = browser_list->begin(); |
| for (; browser_it != browser_list->end(); ++browser_it) { |
| if ((*browser_it)->profile()->GetOriginalProfile() == profile) |
| AddBrowserWindow(*browser_it); |
| } |
| // When adding another user to the session, we auto switch users. |
| if (user_id_to_app_observer_.size() == 1) |
| return; |
| |
| // Don't do anything special in case of user session restore after crash. |
| // In that case session restore process will automatically switch to the |
| // last active user session after whole process is complete. |
| if (!chromeos::UserSessionManager::GetInstance()-> |
| UserSessionsRestoreInProgress()) { |
| // Immediately hide the windows of the current user. |
| base::AutoReset<AnimationSpeed> animation_speed(&animation_speed_, |
| ANIMATION_SPEED_DISABLED); |
| ActiveUserChanged(user_id); |
| } |
| } |
| |
| void MultiUserWindowManagerChromeOS::AddObserver(Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void MultiUserWindowManagerChromeOS::RemoveObserver(Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void MultiUserWindowManagerChromeOS::ActiveUserChanged( |
| const std::string& user_id) { |
| // This needs to be set before the animation starts. |
| current_user_id_ = user_id; |
| |
| animation_.reset( |
| new UserSwichAnimatorChromeOS( |
| this, user_id, GetAdjustedAnimationTimeInMS(kUserFadeTimeMS))); |
| // Call notifier here instead of observing ActiveUserChanged because |
| // this must happen after MultiUserWindowManagerChromeOS is notified. |
| ash::Shell::GetInstance() |
| ->system_tray_notifier() |
| ->NotifyMediaCaptureChanged(); |
| } |
| |
| void MultiUserWindowManagerChromeOS::OnWindowDestroyed(aura::Window* window) { |
| if (GetWindowOwner(window).empty()) { |
| // This must be a window in the transient chain - remove it and its |
| // children from the owner. |
| RemoveTransientOwnerRecursive(window); |
| return; |
| } |
| wm::TransientWindowManager::Get(window)->RemoveObserver(this); |
| // Remove the window from the owners list. |
| delete window_to_entry_[window]; |
| window_to_entry_.erase(window); |
| |
| // Notify entry change. |
| FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryRemoved(window)); |
| } |
| |
| void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanging( |
| aura::Window* window, bool visible) { |
| // This command gets called first and immediately when show or hide gets |
| // called. We remember here the desired state for restoration IF we were |
| // not ourselves issuing the call. |
| // Note also that using the OnWindowVisibilityChanged callback cannot be |
| // used for this. |
| if (suppress_visibility_changes_) |
| return; |
| |
| WindowToEntryMap::iterator it = window_to_entry_.find(window); |
| // If the window is not owned by anyone it is shown on all desktops. |
| if (it != window_to_entry_.end()) { |
| // Remember what was asked for so that we can restore this when the user's |
| // desktop gets restored. |
| it->second->set_show(visible); |
| } else { |
| TransientWindowToVisibility::iterator it = |
| transient_window_to_visibility_.find(window); |
| if (it != transient_window_to_visibility_.end()) |
| it->second = visible; |
| } |
| } |
| |
| void MultiUserWindowManagerChromeOS::OnWindowVisibilityChanged( |
| aura::Window* window, bool visible) { |
| if (suppress_visibility_changes_) |
| return; |
| |
| // Don't allow to make the window visible if it shouldn't be. |
| if (visible && !IsWindowOnDesktopOfUser(window, current_user_id_)) { |
| SetWindowVisibility(window, false, 0); |
| return; |
| } |
| aura::Window* owned_parent = GetOwningWindowInTransientChain(window); |
| if (owned_parent && owned_parent != window && visible && |
| !IsWindowOnDesktopOfUser(owned_parent, current_user_id_)) |
| SetWindowVisibility(window, false, 0); |
| } |
| |
| void MultiUserWindowManagerChromeOS::OnTransientChildAdded( |
| aura::Window* window, |
| aura::Window* transient_window) { |
| if (!GetWindowOwner(window).empty()) { |
| AddTransientOwnerRecursive(transient_window, window); |
| return; |
| } |
| aura::Window* owned_parent = |
| GetOwningWindowInTransientChain(transient_window); |
| if (!owned_parent) |
| return; |
| |
| AddTransientOwnerRecursive(transient_window, owned_parent); |
| } |
| |
| void MultiUserWindowManagerChromeOS::OnTransientChildRemoved( |
| aura::Window* window, |
| aura::Window* transient_window) { |
| // Remove the transient child if the window itself is owned, or one of the |
| // windows in its transient parents chain. |
| if (!GetWindowOwner(window).empty() || |
| GetOwningWindowInTransientChain(window)) |
| RemoveTransientOwnerRecursive(transient_window); |
| } |
| |
| void MultiUserWindowManagerChromeOS::Observe( |
| int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| if (type == NOTIFICATION_BROWSER_WINDOW_READY) |
| AddBrowserWindow(content::Source<Browser>(source).ptr()); |
| } |
| |
| void MultiUserWindowManagerChromeOS::SetAnimationSpeedForTest( |
| MultiUserWindowManagerChromeOS::AnimationSpeed speed) { |
| animation_speed_ = speed; |
| } |
| |
| bool MultiUserWindowManagerChromeOS::IsAnimationRunningForTest() { |
| return animation_.get() != NULL && !animation_->IsAnimationFinished(); |
| } |
| |
| const std::string& MultiUserWindowManagerChromeOS::GetCurrentUserForTest() |
| const { |
| return current_user_id_; |
| } |
| |
| bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern( |
| aura::Window* window, |
| const std::string& user_id) { |
| // If there is either no owner, or the owner is the current user, no action |
| // is required. |
| const std::string& owner = GetWindowOwner(window); |
| if (owner.empty() || |
| (owner == user_id && IsWindowOnDesktopOfUser(window, user_id))) |
| return false; |
| |
| bool minimized = ash::wm::GetWindowState(window)->IsMinimized(); |
| // Check that we are not trying to transfer ownership of a minimized window. |
| if (user_id != owner && minimized) |
| return false; |
| |
| if (minimized) { |
| // If it is minimized it falls back to the original desktop. |
| ash::MultiProfileUMA::RecordTeleportAction( |
| ash::MultiProfileUMA::TELEPORT_WINDOW_RETURN_BY_MINIMIZE); |
| } else { |
| // If the window was transferred without getting minimized, we should record |
| // the window type. |
| RecordUMAForTransferredWindowType(window); |
| } |
| |
| WindowToEntryMap::iterator it = window_to_entry_.find(window); |
| it->second->set_show_for_user(user_id); |
| |
| // Show the window if the added user is the current one. |
| if (user_id == current_user_id_) { |
| // Only show the window if it should be shown according to its state. |
| if (it->second->show()) |
| SetWindowVisibility(window, true, kTeleportAnimationTimeMS); |
| } else { |
| SetWindowVisibility(window, false, kTeleportAnimationTimeMS); |
| } |
| |
| // Notify entry change. |
| FOR_EACH_OBSERVER(Observer, observers_, OnOwnerEntryChanged(window)); |
| return true; |
| } |
| |
| void MultiUserWindowManagerChromeOS::SetWindowVisibility( |
| aura::Window* window, bool visible, int animation_time_in_ms) { |
| if (window->IsVisible() == visible) |
| return; |
| |
| // Hiding a system modal dialog should not be allowed. Instead we switch to |
| // the user which is showing the system modal window. |
| // Note that in some cases (e.g. unit test) windows might not have a root |
| // window. |
| if (!visible && window->GetRootWindow()) { |
| // Get the system modal container for the window's root window. |
| aura::Window* system_modal_container = |
| window->GetRootWindow()->GetChildById( |
| ash::kShellWindowId_SystemModalContainer); |
| if (window->parent() == system_modal_container) { |
| // The window is system modal and we need to find the parent which owns |
| // it so that we can switch to the desktop accordingly. |
| std::string user_id = GetUserPresentingWindow(window); |
| if (user_id.empty()) { |
| aura::Window* owning_window = GetOwningWindowInTransientChain(window); |
| DCHECK(owning_window); |
| user_id = GetUserPresentingWindow(owning_window); |
| DCHECK(!user_id.empty()); |
| } |
| ash::Shell::GetInstance()->session_state_delegate()->SwitchActiveUser( |
| user_id); |
| return; |
| } |
| } |
| |
| // To avoid that these commands are recorded as any other commands, we are |
| // suppressing any window entry changes while this is going on. |
| base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true); |
| |
| if (visible) { |
| ShowWithTransientChildrenRecursive(window, animation_time_in_ms); |
| } else { |
| if (window->HasFocus()) |
| window->Blur(); |
| SetWindowVisible(window, false, animation_time_in_ms); |
| } |
| } |
| |
| void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) { |
| // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can |
| // come here with no valid window. |
| if (!browser->window() || !browser->window()->GetNativeWindow()) |
| return; |
| SetWindowOwner(browser->window()->GetNativeWindow(), |
| multi_user_util::GetUserIDFromProfile(browser->profile())); |
| } |
| |
| void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive( |
| aura::Window* window, int animation_time_in_ms) { |
| aura::Window::Windows::const_iterator it = |
| wm::GetTransientChildren(window).begin(); |
| for (; it != wm::GetTransientChildren(window).end(); ++it) |
| ShowWithTransientChildrenRecursive(*it, animation_time_in_ms); |
| |
| // We show all children which were not explicitly hidden. |
| TransientWindowToVisibility::iterator it2 = |
| transient_window_to_visibility_.find(window); |
| if (it2 == transient_window_to_visibility_.end() || it2->second) |
| SetWindowVisible(window, true, animation_time_in_ms); |
| } |
| |
| aura::Window* MultiUserWindowManagerChromeOS::GetOwningWindowInTransientChain( |
| aura::Window* window) const { |
| if (!GetWindowOwner(window).empty()) |
| return NULL; |
| aura::Window* parent = wm::GetTransientParent(window); |
| while (parent) { |
| if (!GetWindowOwner(parent).empty()) |
| return parent; |
| parent = wm::GetTransientParent(parent); |
| } |
| return NULL; |
| } |
| |
| void MultiUserWindowManagerChromeOS::AddTransientOwnerRecursive( |
| aura::Window* window, |
| aura::Window* owned_parent) { |
| // First add all child windows. |
| aura::Window::Windows::const_iterator it = |
| wm::GetTransientChildren(window).begin(); |
| for (; it != wm::GetTransientChildren(window).end(); ++it) |
| AddTransientOwnerRecursive(*it, owned_parent); |
| |
| // If this window is the owned window, we do not have to handle it again. |
| if (window == owned_parent) |
| return; |
| |
| // Remember the current visibility. |
| DCHECK(transient_window_to_visibility_.find(window) == |
| transient_window_to_visibility_.end()); |
| transient_window_to_visibility_[window] = window->IsVisible(); |
| |
| // Add observers to track state changes. |
| window->AddObserver(this); |
| wm::TransientWindowManager::Get(window)->AddObserver(this); |
| |
| // Hide the window if it should not be shown. Note that this hide operation |
| // will hide recursively this and all children - but we have already collected |
| // their initial view state. |
| if (!IsWindowOnDesktopOfUser(owned_parent, current_user_id_)) |
| SetWindowVisibility(window, false, kAnimationTimeMS); |
| } |
| |
| void MultiUserWindowManagerChromeOS::RemoveTransientOwnerRecursive( |
| aura::Window* window) { |
| // First remove all child windows. |
| aura::Window::Windows::const_iterator it = |
| wm::GetTransientChildren(window).begin(); |
| for (; it != wm::GetTransientChildren(window).end(); ++it) |
| RemoveTransientOwnerRecursive(*it); |
| |
| // Find from transient window storage the visibility for the given window, |
| // set the visibility accordingly and delete the window from the map. |
| TransientWindowToVisibility::iterator visibility_item = |
| transient_window_to_visibility_.find(window); |
| DCHECK(visibility_item != transient_window_to_visibility_.end()); |
| |
| window->RemoveObserver(this); |
| wm::TransientWindowManager::Get(window)->RemoveObserver(this); |
| |
| bool unowned_view_state = visibility_item->second; |
| transient_window_to_visibility_.erase(visibility_item); |
| if (unowned_view_state && !window->IsVisible()) { |
| // To prevent these commands from being recorded as any other commands, we |
| // are suppressing any window entry changes while this is going on. |
| // Instead of calling SetWindowVisible, only show gets called here since all |
| // dependents have been shown previously already. |
| base::AutoReset<bool> suppressor(&suppress_visibility_changes_, true); |
| window->Show(); |
| } |
| } |
| |
| void MultiUserWindowManagerChromeOS::SetWindowVisible( |
| aura::Window* window, |
| bool visible, |
| int animation_time_in_ms) { |
| // The MaximizeModeWindowManager will not handle invisible windows since they |
| // are not user activatable. Since invisible windows are not being tracked, |
| // we tell it to maximize / track this window now before it gets shown, to |
| // reduce animation jank from multiple resizes. |
| if (visible) |
| ash::Shell::GetInstance()->maximize_mode_controller()->AddWindow(window); |
| |
| AnimationSetter animation_setter( |
| window, |
| GetAdjustedAnimationTimeInMS(animation_time_in_ms)); |
| |
| if (visible) |
| window->Show(); |
| else |
| window->Hide(); |
| |
| // Make sure that animations have no influence on the window state after the |
| // call. |
| DCHECK_EQ(visible, window->IsVisible()); |
| } |
| |
| int MultiUserWindowManagerChromeOS::GetAdjustedAnimationTimeInMS( |
| int default_time_in_ms) const { |
| return animation_speed_ == ANIMATION_SPEED_NORMAL ? default_time_in_ms : |
| (animation_speed_ == ANIMATION_SPEED_FAST ? 10 : 0); |
| } |
| |
| } // namespace chrome |