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/tabs/tab_strip_model.h"
18 #include "chrome/browser/web_applications/web_app.h"
19 #include "content/public/browser/web_contents.h"
20 #include "ui/aura/client/activation_client.h"
21 #include "ui/aura/root_window.h"
22 #include "ui/aura/window.h"
23 #include "ui/gfx/screen.h"
25 BrowserStatusMonitor::LocalWebContentsObserver::LocalWebContentsObserver(
26 content::WebContents
* contents
,
27 BrowserStatusMonitor
* monitor
)
28 : content::WebContentsObserver(contents
),
32 BrowserStatusMonitor::LocalWebContentsObserver::~LocalWebContentsObserver() {
35 void BrowserStatusMonitor::LocalWebContentsObserver::DidNavigateMainFrame(
36 const content::LoadCommittedDetails
& details
,
37 const content::FrameNavigateParams
& params
) {
38 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents());
39 ChromeLauncherController::AppState state
=
40 ChromeLauncherController::APP_STATE_INACTIVE
;
41 if (browser
->window()->IsActive() &&
42 browser
->tab_strip_model()->GetActiveWebContents() == web_contents())
43 state
= ChromeLauncherController::APP_STATE_WINDOW_ACTIVE
;
44 else if (browser
->window()->IsActive())
45 state
= ChromeLauncherController::APP_STATE_ACTIVE
;
47 monitor_
->UpdateAppItemState(web_contents(), state
);
48 monitor_
->UpdateBrowserItemState();
50 // Navigating may change the LauncherID associated with the WebContents.
51 if (browser
->tab_strip_model()->GetActiveWebContents() == web_contents()) {
52 ash::SetLauncherIDForWindow(
53 monitor_
->GetLauncherIDForWebContents(web_contents()),
54 browser
->window()->GetNativeWindow());
58 BrowserStatusMonitor::BrowserStatusMonitor(
59 ChromeLauncherController
* launcher_controller
)
60 : launcher_controller_(launcher_controller
),
61 observed_activation_clients_(this),
62 observed_root_windows_(this) {
63 DCHECK(launcher_controller_
);
64 BrowserList::AddObserver(this);
66 // This check needs for win7_aura. Without this, all tests in
67 // ChromeLauncherController will fail in win7_aura.
68 if (ash::Shell::HasInstance()) {
69 // We can't assume all RootWindows have the same ActivationClient.
70 // Add a RootWindow and its ActivationClient to the observed list.
71 aura::Window::Windows root_windows
= ash::Shell::GetAllRootWindows();
72 aura::Window::Windows::const_iterator iter
= root_windows
.begin();
73 for (; iter
!= root_windows
.end(); ++iter
) {
74 // |observed_activation_clients_| can have the same activation client
75 // multiple times - which would be handled by the used
76 // |ScopedObserverWithDuplicatedSources|.
77 observed_activation_clients_
.Add(
78 aura::client::GetActivationClient(*iter
));
79 observed_root_windows_
.Add(static_cast<aura::Window
*>(*iter
));
81 ash::Shell::GetInstance()->GetScreen()->AddObserver(this);
85 BrowserStatusMonitor::~BrowserStatusMonitor() {
86 // This check needs for win7_aura. Without this, all tests in
87 // ChromeLauncherController will fail in win7_aura.
88 if (ash::Shell::HasInstance())
89 ash::Shell::GetInstance()->GetScreen()->RemoveObserver(this);
91 BrowserList::RemoveObserver(this);
93 BrowserList
* browser_list
=
94 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
95 for (BrowserList::const_iterator i
= browser_list
->begin();
96 i
!= browser_list
->end(); ++i
) {
100 STLDeleteContainerPairSecondPointers(webcontents_to_observer_map_
.begin(),
101 webcontents_to_observer_map_
.end());
104 void BrowserStatusMonitor::UpdateAppItemState(
105 content::WebContents
* contents
,
106 ChromeLauncherController::AppState app_state
) {
108 // It is possible to come here from Browser::SwapTabContent where the contents
109 // cannot be associated with a browser.
110 Browser
* browser
= chrome::FindBrowserWithWebContents(contents
);
111 if (browser
&& launcher_controller_
->IsBrowserFromActiveUser(browser
))
112 launcher_controller_
->UpdateAppState(contents
, app_state
);
115 void BrowserStatusMonitor::UpdateBrowserItemState() {
116 launcher_controller_
->GetBrowserShortcutLauncherItemController()->
117 UpdateBrowserItemState();
120 void BrowserStatusMonitor::OnWindowActivated(aura::Window
* gained_active
,
121 aura::Window
* lost_active
) {
122 Browser
* browser
= NULL
;
123 content::WebContents
* contents_from_gained
= NULL
;
124 content::WebContents
* contents_from_lost
= NULL
;
125 // Update active webcontents's app item state of |lost_active|, if existed.
127 browser
= chrome::FindBrowserWithWindow(lost_active
);
129 contents_from_lost
= browser
->tab_strip_model()->GetActiveWebContents();
130 if (contents_from_lost
) {
133 ChromeLauncherController::APP_STATE_INACTIVE
);
137 // Update active webcontents's app item state of |gained_active|, if existed.
139 browser
= chrome::FindBrowserWithWindow(gained_active
);
141 contents_from_gained
= browser
->tab_strip_model()->GetActiveWebContents();
142 if (contents_from_gained
) {
144 contents_from_gained
,
145 ChromeLauncherController::APP_STATE_WINDOW_ACTIVE
);
149 if (contents_from_lost
|| contents_from_gained
)
150 UpdateBrowserItemState();
153 void BrowserStatusMonitor::OnWindowDestroyed(aura::Window
* window
) {
154 // Remove RootWindow and its ActivationClient from observed list.
155 observed_root_windows_
.Remove(window
);
156 observed_activation_clients_
.Remove(
157 aura::client::GetActivationClient(window
));
160 void BrowserStatusMonitor::OnBrowserAdded(Browser
* browser
) {
161 if (browser
->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH
)
164 if (browser
->is_type_popup() && browser
->is_app()) {
165 // Note: A V1 application will set the tab strip observer when the app gets
166 // added to the shelf. This makes sure that in the multi user case we will
167 // only set the observer while the app item exists in the shelf.
168 AddV1AppToShelf(browser
);
170 browser
->tab_strip_model()->AddObserver(this);
174 void BrowserStatusMonitor::OnBrowserRemoved(Browser
* browser
) {
175 if (browser
->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH
)
178 if (browser
->is_type_popup() && browser
->is_app())
179 RemoveV1AppFromShelf(browser
);
181 browser
->tab_strip_model()->RemoveObserver(this);
183 UpdateBrowserItemState();
186 void BrowserStatusMonitor::OnDisplayBoundsChanged(
187 const gfx::Display
& display
) {
191 void BrowserStatusMonitor::OnDisplayAdded(const gfx::Display
& new_display
) {
192 // Add a new RootWindow and its ActivationClient to observed list.
193 aura::Window
* root_window
= ash::Shell::GetInstance()->
194 display_controller()->GetRootWindowForDisplayId(new_display
.id());
195 // When the primary root window's display get removed, the existing root
196 // window is taken over by the new display and the observer is already set.
197 if (!observed_root_windows_
.IsObserving(root_window
)) {
198 observed_root_windows_
.Add(static_cast<aura::Window
*>(root_window
));
199 observed_activation_clients_
.Add(
200 aura::client::GetActivationClient(root_window
));
204 void BrowserStatusMonitor::OnDisplayRemoved(const gfx::Display
& old_display
) {
205 // When this is called, RootWindow of |old_display| is already removed.
206 // Instead, we can remove RootWindow and its ActivationClient in the
207 // OnWindowRemoved().
211 void BrowserStatusMonitor::ActiveTabChanged(content::WebContents
* old_contents
,
212 content::WebContents
* new_contents
,
215 Browser
* browser
= NULL
;
216 // Use |new_contents|. |old_contents| could be NULL.
217 DCHECK(new_contents
);
218 browser
= chrome::FindBrowserWithWebContents(new_contents
);
220 if (browser
&& browser
->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH
)
223 ChromeLauncherController::AppState state
=
224 ChromeLauncherController::APP_STATE_INACTIVE
;
226 // Update immediately on a tab change.
228 (TabStripModel::kNoTab
!=
229 browser
->tab_strip_model()->GetIndexOfWebContents(old_contents
)))
230 UpdateAppItemState(old_contents
, state
);
233 state
= browser
->window()->IsActive() ?
234 ChromeLauncherController::APP_STATE_WINDOW_ACTIVE
:
235 ChromeLauncherController::APP_STATE_ACTIVE
;
236 UpdateAppItemState(new_contents
, state
);
237 UpdateBrowserItemState();
238 ash::SetLauncherIDForWindow(
239 GetLauncherIDForWebContents(new_contents
),
240 browser
->window()->GetNativeWindow());
244 void BrowserStatusMonitor::TabReplacedAt(TabStripModel
* tab_strip_model
,
245 content::WebContents
* old_contents
,
246 content::WebContents
* new_contents
,
248 DCHECK(old_contents
&& new_contents
);
249 Browser
* browser
= chrome::FindBrowserWithWebContents(new_contents
);
251 if (browser
&& browser
->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH
)
254 UpdateAppItemState(old_contents
,
255 ChromeLauncherController::APP_STATE_REMOVED
);
256 RemoveWebContentsObserver(old_contents
);
258 ChromeLauncherController::AppState state
=
259 ChromeLauncherController::APP_STATE_ACTIVE
;
260 if (browser
->window()->IsActive() &&
261 (tab_strip_model
->GetActiveWebContents() == new_contents
))
262 state
= ChromeLauncherController::APP_STATE_WINDOW_ACTIVE
;
263 UpdateAppItemState(new_contents
, state
);
264 UpdateBrowserItemState();
266 if (tab_strip_model
->GetActiveWebContents() == new_contents
) {
267 ash::SetLauncherIDForWindow(
268 GetLauncherIDForWebContents(new_contents
),
269 browser
->window()->GetNativeWindow());
272 AddWebContentsObserver(new_contents
);
275 void BrowserStatusMonitor::TabInsertedAt(content::WebContents
* contents
,
278 // An inserted tab is not active - ActiveTabChanged() will be called to
279 // activate. We initialize therefore with |APP_STATE_INACTIVE|.
280 UpdateAppItemState(contents
,
281 ChromeLauncherController::APP_STATE_INACTIVE
);
282 AddWebContentsObserver(contents
);
285 void BrowserStatusMonitor::TabClosingAt(TabStripModel
* tab_strip_mode
,
286 content::WebContents
* contents
,
288 UpdateAppItemState(contents
,
289 ChromeLauncherController::APP_STATE_REMOVED
);
290 RemoveWebContentsObserver(contents
);
293 void BrowserStatusMonitor::AddV1AppToShelf(Browser
* browser
) {
294 DCHECK(browser
->is_type_popup() && browser
->is_app());
296 browser
->tab_strip_model()->AddObserver(this);
299 web_app::GetExtensionIdFromApplicationName(browser
->app_name());
300 if (!app_id
.empty()) {
301 browser_to_app_id_map_
[browser
] = app_id
;
302 launcher_controller_
->LockV1AppWithID(app_id
);
306 void BrowserStatusMonitor::RemoveV1AppFromShelf(Browser
* browser
) {
307 DCHECK(browser
->is_type_popup() && browser
->is_app());
309 browser
->tab_strip_model()->RemoveObserver(this);
311 if (browser_to_app_id_map_
.find(browser
) != browser_to_app_id_map_
.end()) {
312 launcher_controller_
->UnlockV1AppWithID(browser_to_app_id_map_
[browser
]);
313 browser_to_app_id_map_
.erase(browser
);
317 bool BrowserStatusMonitor::IsV1AppInShelf(Browser
* browser
) {
318 return browser_to_app_id_map_
.find(browser
) != browser_to_app_id_map_
.end();
321 void BrowserStatusMonitor::AddWebContentsObserver(
322 content::WebContents
* contents
) {
323 if (webcontents_to_observer_map_
.find(contents
) ==
324 webcontents_to_observer_map_
.end()) {
325 webcontents_to_observer_map_
[contents
] =
326 new LocalWebContentsObserver(contents
, this);
330 void BrowserStatusMonitor::RemoveWebContentsObserver(
331 content::WebContents
* contents
) {
332 DCHECK(webcontents_to_observer_map_
.find(contents
) !=
333 webcontents_to_observer_map_
.end());
334 delete webcontents_to_observer_map_
[contents
];
335 webcontents_to_observer_map_
.erase(contents
);
338 ash::LauncherID
BrowserStatusMonitor::GetLauncherIDForWebContents(
339 content::WebContents
* contents
) {
340 return launcher_controller_
->GetLauncherIDForWebContents(contents
);