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.
5 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
7 #include "ash/shelf/shelf_util.h"
9 #include "ash/wm/window_util.h"
10 #include "base/stl_util.h"
11 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
12 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_finder.h"
15 #include "chrome/browser/ui/browser_list.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/settings_window_manager.h"
18 #include "chrome/browser/ui/settings_window_manager_observer.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chrome/browser/web_applications/web_app.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/browser/web_contents_observer.h"
23 #include "grit/ash_resources.h"
24 #include "grit/generated_resources.h"
25 #include "ui/aura/window.h"
26 #include "ui/aura/window_event_dispatcher.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/gfx/screen.h"
29 #include "ui/wm/public/activation_client.h"
31 // This class monitors the WebContent of the all tab and notifies a navigation
32 // to the BrowserStatusMonitor.
33 class BrowserStatusMonitor::LocalWebContentsObserver
34 : public content::WebContentsObserver {
36 LocalWebContentsObserver(content::WebContents* contents,
37 BrowserStatusMonitor* monitor)
38 : content::WebContentsObserver(contents),
41 ~LocalWebContentsObserver() override {}
43 // content::WebContentsObserver
44 void DidNavigateMainFrame(
45 const content::LoadCommittedDetails& details,
46 const content::FrameNavigateParams& params) override {
47 Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
48 ChromeLauncherController::AppState state =
49 ChromeLauncherController::APP_STATE_INACTIVE;
50 if (browser->window()->IsActive() &&
51 browser->tab_strip_model()->GetActiveWebContents() == web_contents())
52 state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
53 else if (browser->window()->IsActive())
54 state = ChromeLauncherController::APP_STATE_ACTIVE;
56 monitor_->UpdateAppItemState(web_contents(), state);
57 monitor_->UpdateBrowserItemState();
59 // Navigating may change the ShelfID associated with the WebContents.
60 if (browser->tab_strip_model()->GetActiveWebContents() == web_contents())
61 monitor_->SetShelfIDForBrowserWindowContents(browser, web_contents());
64 void WebContentsDestroyed() override {
65 // We can only come here when there was a non standard termination like
66 // an app got un-installed while running, etc.
67 monitor_->WebContentsDestroyed(web_contents());
68 // |this| is gone now.
72 BrowserStatusMonitor* monitor_;
74 DISALLOW_COPY_AND_ASSIGN(LocalWebContentsObserver);
77 // Observes any new settings windows and sets their shelf icon (since they
78 // are excluded from BrowserShortcutLauncherItem).
79 class BrowserStatusMonitor::SettingsWindowObserver
80 : public chrome::SettingsWindowManagerObserver {
82 SettingsWindowObserver() {}
83 ~SettingsWindowObserver() override {}
85 // SettingsWindowManagerObserver
86 void OnNewSettingsWindow(Browser* settings_browser) override {
87 ash::SetShelfItemDetailsForDialogWindow(
88 settings_browser->window()->GetNativeWindow(),
89 IDR_ASH_SHELF_ICON_SETTINGS,
90 l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
94 DISALLOW_COPY_AND_ASSIGN(SettingsWindowObserver);
97 BrowserStatusMonitor::BrowserStatusMonitor(
98 ChromeLauncherController* launcher_controller)
99 : launcher_controller_(launcher_controller),
100 observed_activation_clients_(this),
101 observed_root_windows_(this),
102 settings_window_observer_(new SettingsWindowObserver) {
103 DCHECK(launcher_controller_);
104 BrowserList::AddObserver(this);
105 chrome::SettingsWindowManager::GetInstance()->AddObserver(
106 settings_window_observer_.get());
108 // This check needs for win7_aura. Without this, all tests in
109 // ChromeLauncherController will fail in win7_aura.
110 if (ash::Shell::HasInstance()) {
111 // We can't assume all RootWindows have the same ActivationClient.
112 // Add a RootWindow and its ActivationClient to the observed list.
113 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
114 aura::Window::Windows::const_iterator iter = root_windows.begin();
115 for (; iter != root_windows.end(); ++iter) {
116 // |observed_activation_clients_| can have the same activation client
117 // multiple times - which would be handled by the used
118 // |ScopedObserverWithDuplicatedSources|.
119 observed_activation_clients_.Add(
120 aura::client::GetActivationClient(*iter));
121 observed_root_windows_.Add(static_cast<aura::Window*>(*iter));
123 ash::Shell::GetInstance()->GetScreen()->AddObserver(this);
127 BrowserStatusMonitor::~BrowserStatusMonitor() {
128 // This check needs for win7_aura. Without this, all tests in
129 // ChromeLauncherController will fail in win7_aura.
130 if (ash::Shell::HasInstance())
131 ash::Shell::GetInstance()->GetScreen()->RemoveObserver(this);
133 BrowserList::RemoveObserver(this);
135 BrowserList* browser_list =
136 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
137 for (BrowserList::const_iterator i = browser_list->begin();
138 i != browser_list->end(); ++i) {
139 OnBrowserRemoved(*i);
142 STLDeleteContainerPairSecondPointers(webcontents_to_observer_map_.begin(),
143 webcontents_to_observer_map_.end());
146 void BrowserStatusMonitor::UpdateAppItemState(
147 content::WebContents* contents,
148 ChromeLauncherController::AppState app_state) {
150 // It is possible to come here from Browser::SwapTabContent where the contents
151 // cannot be associated with a browser. A removal however should be properly
153 Browser* browser = chrome::FindBrowserWithWebContents(contents);
154 if (app_state == ChromeLauncherController::APP_STATE_REMOVED ||
155 (browser && launcher_controller_->IsBrowserFromActiveUser(browser)))
156 launcher_controller_->UpdateAppState(contents, app_state);
159 void BrowserStatusMonitor::UpdateBrowserItemState() {
160 launcher_controller_->GetBrowserShortcutLauncherItemController()->
161 UpdateBrowserItemState();
164 void BrowserStatusMonitor::OnWindowActivated(aura::Window* gained_active,
165 aura::Window* lost_active) {
166 Browser* browser = NULL;
167 content::WebContents* contents_from_gained = NULL;
168 content::WebContents* contents_from_lost = NULL;
169 // Update active webcontents's app item state of |lost_active|, if existed.
171 browser = chrome::FindBrowserWithWindow(lost_active);
173 contents_from_lost = browser->tab_strip_model()->GetActiveWebContents();
174 if (contents_from_lost) {
177 ChromeLauncherController::APP_STATE_INACTIVE);
181 // Update active webcontents's app item state of |gained_active|, if existed.
183 browser = chrome::FindBrowserWithWindow(gained_active);
185 contents_from_gained = browser->tab_strip_model()->GetActiveWebContents();
186 if (contents_from_gained) {
188 contents_from_gained,
189 ChromeLauncherController::APP_STATE_WINDOW_ACTIVE);
193 if (contents_from_lost || contents_from_gained)
194 UpdateBrowserItemState();
197 void BrowserStatusMonitor::OnWindowDestroyed(aura::Window* window) {
198 // Remove RootWindow and its ActivationClient from observed list.
199 observed_root_windows_.Remove(window);
200 observed_activation_clients_.Remove(
201 aura::client::GetActivationClient(window));
204 void BrowserStatusMonitor::OnBrowserAdded(Browser* browser) {
205 if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
208 if (browser->is_type_popup() && browser->is_app()) {
209 // Note: A V1 application will set the tab strip observer when the app gets
210 // added to the shelf. This makes sure that in the multi user case we will
211 // only set the observer while the app item exists in the shelf.
212 AddV1AppToShelf(browser);
214 browser->tab_strip_model()->AddObserver(this);
218 void BrowserStatusMonitor::OnBrowserRemoved(Browser* browser) {
219 if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
222 if (browser->is_type_popup() && browser->is_app())
223 RemoveV1AppFromShelf(browser);
225 browser->tab_strip_model()->RemoveObserver(this);
227 UpdateBrowserItemState();
230 void BrowserStatusMonitor::OnDisplayAdded(const gfx::Display& new_display) {
231 // Add a new RootWindow and its ActivationClient to observed list.
232 aura::Window* root_window = ash::Shell::GetInstance()->
233 display_controller()->GetRootWindowForDisplayId(new_display.id());
234 // When the primary root window's display get removed, the existing root
235 // window is taken over by the new display and the observer is already set.
236 if (!observed_root_windows_.IsObserving(root_window)) {
237 observed_root_windows_.Add(static_cast<aura::Window*>(root_window));
238 observed_activation_clients_.Add(
239 aura::client::GetActivationClient(root_window));
243 void BrowserStatusMonitor::OnDisplayRemoved(const gfx::Display& old_display) {
244 // When this is called, RootWindow of |old_display| is already removed.
245 // Instead, we can remove RootWindow and its ActivationClient in the
246 // OnWindowRemoved().
250 void BrowserStatusMonitor::OnDisplayMetricsChanged(const gfx::Display&,
255 void BrowserStatusMonitor::ActiveTabChanged(content::WebContents* old_contents,
256 content::WebContents* new_contents,
259 Browser* browser = NULL;
260 // Use |new_contents|. |old_contents| could be NULL.
261 DCHECK(new_contents);
262 browser = chrome::FindBrowserWithWebContents(new_contents);
264 if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
267 ChromeLauncherController::AppState state =
268 ChromeLauncherController::APP_STATE_INACTIVE;
270 // Update immediately on a tab change.
272 (TabStripModel::kNoTab !=
273 browser->tab_strip_model()->GetIndexOfWebContents(old_contents)))
274 UpdateAppItemState(old_contents, state);
277 state = browser->window()->IsActive() ?
278 ChromeLauncherController::APP_STATE_WINDOW_ACTIVE :
279 ChromeLauncherController::APP_STATE_ACTIVE;
280 UpdateAppItemState(new_contents, state);
281 UpdateBrowserItemState();
282 SetShelfIDForBrowserWindowContents(browser, new_contents);
286 void BrowserStatusMonitor::TabReplacedAt(TabStripModel* tab_strip_model,
287 content::WebContents* old_contents,
288 content::WebContents* new_contents,
290 DCHECK(old_contents && new_contents);
291 Browser* browser = chrome::FindBrowserWithWebContents(new_contents);
293 if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
296 UpdateAppItemState(old_contents,
297 ChromeLauncherController::APP_STATE_REMOVED);
298 RemoveWebContentsObserver(old_contents);
300 ChromeLauncherController::AppState state =
301 ChromeLauncherController::APP_STATE_ACTIVE;
302 if (browser->window()->IsActive() &&
303 (tab_strip_model->GetActiveWebContents() == new_contents))
304 state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
305 UpdateAppItemState(new_contents, state);
306 UpdateBrowserItemState();
308 if (tab_strip_model->GetActiveWebContents() == new_contents)
309 SetShelfIDForBrowserWindowContents(browser, new_contents);
311 AddWebContentsObserver(new_contents);
314 void BrowserStatusMonitor::TabInsertedAt(content::WebContents* contents,
317 // An inserted tab is not active - ActiveTabChanged() will be called to
318 // activate. We initialize therefore with |APP_STATE_INACTIVE|.
319 UpdateAppItemState(contents,
320 ChromeLauncherController::APP_STATE_INACTIVE);
321 AddWebContentsObserver(contents);
324 void BrowserStatusMonitor::TabClosingAt(TabStripModel* tab_strip_mode,
325 content::WebContents* contents,
327 UpdateAppItemState(contents,
328 ChromeLauncherController::APP_STATE_REMOVED);
329 RemoveWebContentsObserver(contents);
332 void BrowserStatusMonitor::WebContentsDestroyed(
333 content::WebContents* contents) {
334 UpdateAppItemState(contents, ChromeLauncherController::APP_STATE_REMOVED);
335 RemoveWebContentsObserver(contents);
338 void BrowserStatusMonitor::AddV1AppToShelf(Browser* browser) {
339 DCHECK(browser->is_type_popup() && browser->is_app());
341 browser->tab_strip_model()->AddObserver(this);
344 web_app::GetExtensionIdFromApplicationName(browser->app_name());
345 if (!app_id.empty()) {
346 browser_to_app_id_map_[browser] = app_id;
347 launcher_controller_->LockV1AppWithID(app_id);
351 void BrowserStatusMonitor::RemoveV1AppFromShelf(Browser* browser) {
352 DCHECK(browser->is_type_popup() && browser->is_app());
354 browser->tab_strip_model()->RemoveObserver(this);
356 if (browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end()) {
357 launcher_controller_->UnlockV1AppWithID(browser_to_app_id_map_[browser]);
358 browser_to_app_id_map_.erase(browser);
362 bool BrowserStatusMonitor::IsV1AppInShelf(Browser* browser) {
363 return browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end();
366 void BrowserStatusMonitor::AddWebContentsObserver(
367 content::WebContents* contents) {
368 if (webcontents_to_observer_map_.find(contents) ==
369 webcontents_to_observer_map_.end()) {
370 webcontents_to_observer_map_[contents] =
371 new LocalWebContentsObserver(contents, this);
375 void BrowserStatusMonitor::RemoveWebContentsObserver(
376 content::WebContents* contents) {
377 DCHECK(webcontents_to_observer_map_.find(contents) !=
378 webcontents_to_observer_map_.end());
379 delete webcontents_to_observer_map_[contents];
380 webcontents_to_observer_map_.erase(contents);
383 ash::ShelfID BrowserStatusMonitor::GetShelfIDForWebContents(
384 content::WebContents* contents) {
385 return launcher_controller_->GetShelfIDForWebContents(contents);
388 void BrowserStatusMonitor::SetShelfIDForBrowserWindowContents(
390 content::WebContents* web_contents) {
391 launcher_controller_->GetBrowserShortcutLauncherItemController()->
392 SetShelfIDForBrowserWindowContents(browser, web_contents);