blob: 126a4694084f49a0af933602049840c23d9f429c [file] [log] [blame]
// 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 "apps/app_window.h"
#include "apps/app_window_registry.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/users/user_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 "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.
apps::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 = apps::AppWindowRegistry::Get(*it)
->GetAppWindowForNativeWindow(window);
}
if (app_window) {
if (app_window->window_type() == apps::AppWindow::WINDOW_TYPE_PANEL ||
app_window->window_type() == apps::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 apps::AppWindowRegistry::Observer {
public:
explicit AppObserver(const std::string& user_id) : user_id_(user_id) {}
virtual ~AppObserver() {}
// AppWindowRegistry::Observer overrides:
virtual void OnAppWindowAdded(apps::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);
apps::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);
apps::AppWindowRegistry::Get(profile)
->AddObserver(user_id_to_app_observer_[user_id]);
// Account all existing application windows of this user accordingly.
const apps::AppWindowRegistry::AppWindowList& app_windows =
apps::AppWindowRegistry::Get(profile)->app_windows();
apps::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);
}
}
void MultiUserWindowManagerChromeOS::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void MultiUserWindowManagerChromeOS::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void MultiUserWindowManagerChromeOS::ActiveUserChanged(
const std::string& user_id) {
DCHECK(user_id != current_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