blob: 2a925bc60d7ac97f4ea9f6a509632ac8640aae5d [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/launcher/browser_status_monitor.h"
#include "ash/shell.h"
#include "ash/wm/window_util.h"
#include "base/stl_util.h"
#include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.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 "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/web_applications/web_app.h"
#include "content/public/browser/web_contents.h"
#include "ui/aura/client/activation_client.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/gfx/screen.h"
BrowserStatusMonitor::LocalWebContentsObserver::LocalWebContentsObserver(
content::WebContents* contents,
BrowserStatusMonitor* monitor)
: content::WebContentsObserver(contents),
monitor_(monitor) {
}
BrowserStatusMonitor::LocalWebContentsObserver::~LocalWebContentsObserver() {
}
void BrowserStatusMonitor::LocalWebContentsObserver::DidNavigateMainFrame(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
ChromeLauncherController::AppState state =
ChromeLauncherController::APP_STATE_INACTIVE;
if (browser->window()->IsActive() &&
browser->tab_strip_model()->GetActiveWebContents() == web_contents())
state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
else if (browser->window()->IsActive())
state = ChromeLauncherController::APP_STATE_ACTIVE;
monitor_->UpdateAppItemState(web_contents(), state);
monitor_->UpdateBrowserItemState();
}
BrowserStatusMonitor::BrowserStatusMonitor(
ChromeLauncherController* launcher_controller)
: launcher_controller_(launcher_controller),
observed_activation_clients_(this),
observed_root_windows_(this) {
DCHECK(launcher_controller_);
BrowserList::AddObserver(this);
// This check needs for win7_aura. Without this, all tests in
// ChromeLauncherController will fail in win7_aura.
if (ash::Shell::HasInstance()) {
// We can't assume all RootWindows have the same ActivationClient.
// Add a RootWindow and its ActivationClient to the observed list.
ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
ash::Shell::RootWindowList::const_iterator iter = root_windows.begin();
for (; iter != root_windows.end(); ++iter) {
// |observed_activation_clients_| can have the same activation client
// multiple times - which would be handled by the used
// |ScopedObserverWithDuplicatedSources|.
observed_activation_clients_.Add(
aura::client::GetActivationClient(*iter));
observed_root_windows_.Add(static_cast<aura::Window*>(*iter));
}
ash::Shell::GetInstance()->GetScreen()->AddObserver(this);
}
}
BrowserStatusMonitor::~BrowserStatusMonitor() {
// This check needs for win7_aura. Without this, all tests in
// ChromeLauncherController will fail in win7_aura.
if (ash::Shell::HasInstance())
ash::Shell::GetInstance()->GetScreen()->RemoveObserver(this);
BrowserList::RemoveObserver(this);
BrowserList* browser_list =
BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
for (BrowserList::const_iterator i = browser_list->begin();
i != browser_list->end(); ++i) {
OnBrowserRemoved(*i);
}
STLDeleteContainerPairSecondPointers(webcontents_to_observer_map_.begin(),
webcontents_to_observer_map_.end());
}
void BrowserStatusMonitor::UpdateAppItemState(
content::WebContents* contents,
ChromeLauncherController::AppState app_state) {
DCHECK(contents);
if (launcher_controller_->IsBrowserFromActiveUser(
chrome::FindBrowserWithWebContents(contents)))
launcher_controller_->UpdateAppState(contents, app_state);
}
void BrowserStatusMonitor::UpdateBrowserItemState() {
launcher_controller_->GetBrowserShortcutLauncherItemController()->
UpdateBrowserItemState();
}
void BrowserStatusMonitor::OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
Browser* browser = NULL;
content::WebContents* contents_from_gained = NULL;
content::WebContents* contents_from_lost = NULL;
// Update active webcontents's app item state of |lost_active|, if existed.
if (lost_active) {
browser = chrome::FindBrowserWithWindow(lost_active);
if (browser)
contents_from_lost = browser->tab_strip_model()->GetActiveWebContents();
if (contents_from_lost) {
UpdateAppItemState(
contents_from_lost,
ChromeLauncherController::APP_STATE_INACTIVE);
}
}
// Update active webcontents's app item state of |gained_active|, if existed.
if (gained_active) {
browser = chrome::FindBrowserWithWindow(gained_active);
if (browser)
contents_from_gained = browser->tab_strip_model()->GetActiveWebContents();
if (contents_from_gained) {
UpdateAppItemState(
contents_from_gained,
ChromeLauncherController::APP_STATE_WINDOW_ACTIVE);
}
}
if (contents_from_lost || contents_from_gained)
UpdateBrowserItemState();
}
void BrowserStatusMonitor::OnWindowDestroyed(aura::Window* window) {
// Remove RootWindow and its ActivationClient from observed list.
observed_root_windows_.Remove(window);
observed_activation_clients_.Remove(aura::client::GetActivationClient(
static_cast<aura::RootWindow*>(window)));
}
void BrowserStatusMonitor::OnBrowserAdded(Browser* browser) {
if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
return;
if (browser->is_type_popup() && browser->is_app()) {
// Note: A V1 application will set the tab strip observer when the app gets
// added to the shelf. This makes sure that in the multi user case we will
// only set the observer while the app item exists in the shelf.
AddV1AppToShelf(browser);
} else {
browser->tab_strip_model()->AddObserver(this);
}
}
void BrowserStatusMonitor::OnBrowserRemoved(Browser* browser) {
if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
return;
if (browser->is_type_popup() && browser->is_app())
RemoveV1AppFromShelf(browser);
else
browser->tab_strip_model()->RemoveObserver(this);
UpdateBrowserItemState();
}
void BrowserStatusMonitor::OnDisplayBoundsChanged(
const gfx::Display& display) {
// Do nothing here.
}
void BrowserStatusMonitor::OnDisplayAdded(const gfx::Display& new_display) {
// Add a new RootWindow and its ActivationClient to observed list.
aura::Window* root_window = ash::Shell::GetInstance()->
display_controller()->GetRootWindowForDisplayId(new_display.id());
// When the primary root window's display get removed, the existing root
// window is taken over by the new display and the observer is already set.
if (!observed_root_windows_.IsObserving(root_window)) {
observed_root_windows_.Add(static_cast<aura::Window*>(root_window));
observed_activation_clients_.Add(
aura::client::GetActivationClient(root_window));
}
}
void BrowserStatusMonitor::OnDisplayRemoved(const gfx::Display& old_display) {
// When this is called, RootWindow of |old_display| is already removed.
// Instead, we can remove RootWindow and its ActivationClient in the
// OnWindowRemoved().
// Do nothing here.
}
void BrowserStatusMonitor::ActiveTabChanged(content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) {
Browser* browser = NULL;
// Use |new_contents|. |old_contents| could be NULL.
DCHECK(new_contents);
browser = chrome::FindBrowserWithWebContents(new_contents);
if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
return;
ChromeLauncherController::AppState state =
ChromeLauncherController::APP_STATE_INACTIVE;
// Update immediately on a tab change.
if (old_contents &&
(TabStripModel::kNoTab !=
browser->tab_strip_model()->GetIndexOfWebContents(old_contents)))
UpdateAppItemState(old_contents, state);
if (new_contents) {
state = browser->window()->IsActive() ?
ChromeLauncherController::APP_STATE_WINDOW_ACTIVE :
ChromeLauncherController::APP_STATE_ACTIVE;
UpdateAppItemState(new_contents, state);
UpdateBrowserItemState();
}
}
void BrowserStatusMonitor::TabReplacedAt(TabStripModel* tab_strip_model,
content::WebContents* old_contents,
content::WebContents* new_contents,
int index) {
DCHECK(old_contents && new_contents);
Browser* browser = chrome::FindBrowserWithWebContents(new_contents);
if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
return;
UpdateAppItemState(old_contents,
ChromeLauncherController::APP_STATE_REMOVED);
RemoveWebContentsObserver(old_contents);
ChromeLauncherController::AppState state =
ChromeLauncherController::APP_STATE_ACTIVE;
if (browser->window()->IsActive() &&
(tab_strip_model->GetActiveWebContents() == new_contents))
state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
UpdateAppItemState(new_contents, state);
AddWebContentsObserver(new_contents);
}
void BrowserStatusMonitor::TabInsertedAt(content::WebContents* contents,
int index,
bool foreground) {
// An inserted tab is not active - ActiveTabChanged() will be called to
// activate. We initialize therefore with |APP_STATE_INACTIVE|.
UpdateAppItemState(contents,
ChromeLauncherController::APP_STATE_INACTIVE);
AddWebContentsObserver(contents);
}
void BrowserStatusMonitor::TabClosingAt(TabStripModel* tab_strip_mode,
content::WebContents* contents,
int index) {
UpdateAppItemState(contents,
ChromeLauncherController::APP_STATE_REMOVED);
RemoveWebContentsObserver(contents);
}
void BrowserStatusMonitor::AddV1AppToShelf(Browser* browser) {
DCHECK(browser->is_type_popup() && browser->is_app());
browser->tab_strip_model()->AddObserver(this);
std::string app_id =
web_app::GetExtensionIdFromApplicationName(browser->app_name());
if (!app_id.empty()) {
browser_to_app_id_map_[browser] = app_id;
launcher_controller_->LockV1AppWithID(app_id);
}
}
void BrowserStatusMonitor::RemoveV1AppFromShelf(Browser* browser) {
DCHECK(browser->is_type_popup() && browser->is_app());
browser->tab_strip_model()->RemoveObserver(this);
if (browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end()) {
launcher_controller_->UnlockV1AppWithID(browser_to_app_id_map_[browser]);
browser_to_app_id_map_.erase(browser);
}
}
bool BrowserStatusMonitor::IsV1AppInShelf(Browser* browser) {
return browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end();
}
void BrowserStatusMonitor::AddWebContentsObserver(
content::WebContents* contents) {
if (webcontents_to_observer_map_.find(contents) ==
webcontents_to_observer_map_.end()) {
webcontents_to_observer_map_[contents] =
new LocalWebContentsObserver(contents, this);
}
}
void BrowserStatusMonitor::RemoveWebContentsObserver(
content::WebContents* contents) {
DCHECK(webcontents_to_observer_map_.find(contents) !=
webcontents_to_observer_map_.end());
delete webcontents_to_observer_map_[contents];
webcontents_to_observer_map_.erase(contents);
}