• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
6 
7 #include "ash/shelf/shelf_util.h"
8 #include "ash/shell.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 "ui/aura/window.h"
25 #include "ui/aura/window_event_dispatcher.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/wm/public/activation_client.h"
28 
29 // This class monitors the WebContent of the all tab and notifies a navigation
30 // to the BrowserStatusMonitor.
31 class BrowserStatusMonitor::LocalWebContentsObserver
32     : public content::WebContentsObserver {
33  public:
LocalWebContentsObserver(content::WebContents * contents,BrowserStatusMonitor * monitor)34   LocalWebContentsObserver(content::WebContents* contents,
35                            BrowserStatusMonitor* monitor)
36       : content::WebContentsObserver(contents),
37         monitor_(monitor) {}
38 
~LocalWebContentsObserver()39   virtual ~LocalWebContentsObserver() {}
40 
41   // content::WebContentsObserver
DidNavigateMainFrame(const content::LoadCommittedDetails & details,const content::FrameNavigateParams & params)42   virtual void DidNavigateMainFrame(
43       const content::LoadCommittedDetails& details,
44       const content::FrameNavigateParams& params) OVERRIDE {
45     Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
46     ChromeLauncherController::AppState state =
47         ChromeLauncherController::APP_STATE_INACTIVE;
48     if (browser->window()->IsActive() &&
49         browser->tab_strip_model()->GetActiveWebContents() == web_contents())
50       state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
51     else if (browser->window()->IsActive())
52       state = ChromeLauncherController::APP_STATE_ACTIVE;
53 
54     monitor_->UpdateAppItemState(web_contents(), state);
55     monitor_->UpdateBrowserItemState();
56 
57     // Navigating may change the ShelfID associated with the WebContents.
58     if (browser->tab_strip_model()->GetActiveWebContents() == web_contents())
59       monitor_->SetShelfIDForBrowserWindowContents(browser, web_contents());
60   }
61 
WebContentsDestroyed()62   virtual void WebContentsDestroyed() OVERRIDE {
63     // We can only come here when there was a non standard termination like
64     // an app got un-installed while running, etc.
65     monitor_->WebContentsDestroyed(web_contents());
66     // |this| is gone now.
67   }
68 
69  private:
70   BrowserStatusMonitor* monitor_;
71 
72   DISALLOW_COPY_AND_ASSIGN(LocalWebContentsObserver);
73 };
74 
75 // Observes any new settings windows and sets their shelf icon (since they
76 // are excluded from BrowserShortcutLauncherItem).
77 class BrowserStatusMonitor::SettingsWindowObserver
78     : public chrome::SettingsWindowManagerObserver {
79  public:
SettingsWindowObserver()80   SettingsWindowObserver() {}
~SettingsWindowObserver()81   virtual ~SettingsWindowObserver() {}
82 
83   // SettingsWindowManagerObserver
OnNewSettingsWindow(Browser * settings_browser)84   virtual void OnNewSettingsWindow(Browser* settings_browser) OVERRIDE {
85     ash::SetShelfItemDetailsForDialogWindow(
86         settings_browser->window()->GetNativeWindow(),
87         IDR_ASH_SHELF_ICON_SETTINGS);
88   }
89 
90  private:
91   DISALLOW_COPY_AND_ASSIGN(SettingsWindowObserver);
92 };
93 
BrowserStatusMonitor(ChromeLauncherController * launcher_controller)94 BrowserStatusMonitor::BrowserStatusMonitor(
95     ChromeLauncherController* launcher_controller)
96     : launcher_controller_(launcher_controller),
97       observed_activation_clients_(this),
98       observed_root_windows_(this),
99       settings_window_observer_(new SettingsWindowObserver) {
100   DCHECK(launcher_controller_);
101   BrowserList::AddObserver(this);
102   chrome::SettingsWindowManager::GetInstance()->AddObserver(
103       settings_window_observer_.get());
104 
105   // This check needs for win7_aura. Without this, all tests in
106   // ChromeLauncherController will fail in win7_aura.
107   if (ash::Shell::HasInstance()) {
108     // We can't assume all RootWindows have the same ActivationClient.
109     // Add a RootWindow and its ActivationClient to the observed list.
110     aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
111     aura::Window::Windows::const_iterator iter = root_windows.begin();
112     for (; iter != root_windows.end(); ++iter) {
113       // |observed_activation_clients_| can have the same activation client
114       // multiple times - which would be handled by the used
115       // |ScopedObserverWithDuplicatedSources|.
116       observed_activation_clients_.Add(
117           aura::client::GetActivationClient(*iter));
118       observed_root_windows_.Add(static_cast<aura::Window*>(*iter));
119     }
120     ash::Shell::GetInstance()->GetScreen()->AddObserver(this);
121   }
122 }
123 
~BrowserStatusMonitor()124 BrowserStatusMonitor::~BrowserStatusMonitor() {
125   // This check needs for win7_aura. Without this, all tests in
126   // ChromeLauncherController will fail in win7_aura.
127   if (ash::Shell::HasInstance())
128     ash::Shell::GetInstance()->GetScreen()->RemoveObserver(this);
129 
130   BrowserList::RemoveObserver(this);
131 
132   BrowserList* browser_list =
133       BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
134   for (BrowserList::const_iterator i = browser_list->begin();
135        i != browser_list->end(); ++i) {
136     OnBrowserRemoved(*i);
137   }
138 
139   STLDeleteContainerPairSecondPointers(webcontents_to_observer_map_.begin(),
140                                        webcontents_to_observer_map_.end());
141 }
142 
UpdateAppItemState(content::WebContents * contents,ChromeLauncherController::AppState app_state)143 void BrowserStatusMonitor::UpdateAppItemState(
144     content::WebContents* contents,
145     ChromeLauncherController::AppState app_state) {
146   DCHECK(contents);
147   // It is possible to come here from Browser::SwapTabContent where the contents
148   // cannot be associated with a browser. A removal however should be properly
149   // processed.
150   Browser* browser = chrome::FindBrowserWithWebContents(contents);
151   if (app_state == ChromeLauncherController::APP_STATE_REMOVED ||
152       (browser && launcher_controller_->IsBrowserFromActiveUser(browser)))
153     launcher_controller_->UpdateAppState(contents, app_state);
154 }
155 
UpdateBrowserItemState()156 void BrowserStatusMonitor::UpdateBrowserItemState() {
157   launcher_controller_->GetBrowserShortcutLauncherItemController()->
158       UpdateBrowserItemState();
159 }
160 
OnWindowActivated(aura::Window * gained_active,aura::Window * lost_active)161 void BrowserStatusMonitor::OnWindowActivated(aura::Window* gained_active,
162                                              aura::Window* lost_active) {
163   Browser* browser = NULL;
164   content::WebContents* contents_from_gained = NULL;
165   content::WebContents* contents_from_lost = NULL;
166   // Update active webcontents's app item state of |lost_active|, if existed.
167   if (lost_active) {
168     browser = chrome::FindBrowserWithWindow(lost_active);
169     if (browser)
170       contents_from_lost = browser->tab_strip_model()->GetActiveWebContents();
171     if (contents_from_lost) {
172       UpdateAppItemState(
173           contents_from_lost,
174           ChromeLauncherController::APP_STATE_INACTIVE);
175     }
176   }
177 
178   // Update active webcontents's app item state of |gained_active|, if existed.
179   if (gained_active) {
180     browser = chrome::FindBrowserWithWindow(gained_active);
181     if (browser)
182       contents_from_gained = browser->tab_strip_model()->GetActiveWebContents();
183     if (contents_from_gained) {
184       UpdateAppItemState(
185           contents_from_gained,
186           ChromeLauncherController::APP_STATE_WINDOW_ACTIVE);
187     }
188   }
189 
190   if (contents_from_lost || contents_from_gained)
191     UpdateBrowserItemState();
192 }
193 
OnWindowDestroyed(aura::Window * window)194 void BrowserStatusMonitor::OnWindowDestroyed(aura::Window* window) {
195   // Remove RootWindow and its ActivationClient from observed list.
196   observed_root_windows_.Remove(window);
197   observed_activation_clients_.Remove(
198       aura::client::GetActivationClient(window));
199 }
200 
OnBrowserAdded(Browser * browser)201 void BrowserStatusMonitor::OnBrowserAdded(Browser* browser) {
202   if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
203     return;
204 
205   if (browser->is_type_popup() && browser->is_app()) {
206     // Note: A V1 application will set the tab strip observer when the app gets
207     // added to the shelf. This makes sure that in the multi user case we will
208     // only set the observer while the app item exists in the shelf.
209     AddV1AppToShelf(browser);
210   } else {
211     browser->tab_strip_model()->AddObserver(this);
212   }
213 }
214 
OnBrowserRemoved(Browser * browser)215 void BrowserStatusMonitor::OnBrowserRemoved(Browser* browser) {
216   if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
217     return;
218 
219   if (browser->is_type_popup() && browser->is_app())
220     RemoveV1AppFromShelf(browser);
221   else
222     browser->tab_strip_model()->RemoveObserver(this);
223 
224   UpdateBrowserItemState();
225 }
226 
OnDisplayAdded(const gfx::Display & new_display)227 void BrowserStatusMonitor::OnDisplayAdded(const gfx::Display& new_display) {
228   // Add a new RootWindow and its ActivationClient to observed list.
229   aura::Window* root_window = ash::Shell::GetInstance()->
230       display_controller()->GetRootWindowForDisplayId(new_display.id());
231   // When the primary root window's display get removed, the existing root
232   // window is taken over by the new display and the observer is already set.
233   if (!observed_root_windows_.IsObserving(root_window)) {
234     observed_root_windows_.Add(static_cast<aura::Window*>(root_window));
235     observed_activation_clients_.Add(
236         aura::client::GetActivationClient(root_window));
237   }
238 }
239 
OnDisplayRemoved(const gfx::Display & old_display)240 void BrowserStatusMonitor::OnDisplayRemoved(const gfx::Display& old_display) {
241   // When this is called, RootWindow of |old_display| is already removed.
242   // Instead, we can remove RootWindow and its ActivationClient in the
243   // OnWindowRemoved().
244   // Do nothing here.
245 }
246 
OnDisplayMetricsChanged(const gfx::Display &,uint32_t)247 void BrowserStatusMonitor::OnDisplayMetricsChanged(const gfx::Display&,
248                                                    uint32_t) {
249   // Do nothing here.
250 }
251 
ActiveTabChanged(content::WebContents * old_contents,content::WebContents * new_contents,int index,int reason)252 void BrowserStatusMonitor::ActiveTabChanged(content::WebContents* old_contents,
253                                             content::WebContents* new_contents,
254                                             int index,
255                                             int reason) {
256   Browser* browser = NULL;
257   // Use |new_contents|. |old_contents| could be NULL.
258   DCHECK(new_contents);
259   browser = chrome::FindBrowserWithWebContents(new_contents);
260 
261   if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
262     return;
263 
264   ChromeLauncherController::AppState state =
265       ChromeLauncherController::APP_STATE_INACTIVE;
266 
267   // Update immediately on a tab change.
268   if (old_contents &&
269       (TabStripModel::kNoTab !=
270            browser->tab_strip_model()->GetIndexOfWebContents(old_contents)))
271     UpdateAppItemState(old_contents, state);
272 
273   if (new_contents) {
274     state = browser->window()->IsActive() ?
275         ChromeLauncherController::APP_STATE_WINDOW_ACTIVE :
276         ChromeLauncherController::APP_STATE_ACTIVE;
277     UpdateAppItemState(new_contents, state);
278     UpdateBrowserItemState();
279     SetShelfIDForBrowserWindowContents(browser, new_contents);
280   }
281 }
282 
TabReplacedAt(TabStripModel * tab_strip_model,content::WebContents * old_contents,content::WebContents * new_contents,int index)283 void BrowserStatusMonitor::TabReplacedAt(TabStripModel* tab_strip_model,
284                                          content::WebContents* old_contents,
285                                          content::WebContents* new_contents,
286                                          int index) {
287   DCHECK(old_contents && new_contents);
288   Browser* browser = chrome::FindBrowserWithWebContents(new_contents);
289 
290   if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
291     return;
292 
293   UpdateAppItemState(old_contents,
294                      ChromeLauncherController::APP_STATE_REMOVED);
295   RemoveWebContentsObserver(old_contents);
296 
297   ChromeLauncherController::AppState state =
298       ChromeLauncherController::APP_STATE_ACTIVE;
299   if (browser->window()->IsActive() &&
300       (tab_strip_model->GetActiveWebContents() == new_contents))
301     state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
302   UpdateAppItemState(new_contents, state);
303   UpdateBrowserItemState();
304 
305   if (tab_strip_model->GetActiveWebContents() == new_contents)
306     SetShelfIDForBrowserWindowContents(browser, new_contents);
307 
308   AddWebContentsObserver(new_contents);
309 }
310 
TabInsertedAt(content::WebContents * contents,int index,bool foreground)311 void BrowserStatusMonitor::TabInsertedAt(content::WebContents* contents,
312                                          int index,
313                                          bool foreground) {
314   // An inserted tab is not active - ActiveTabChanged() will be called to
315   // activate. We initialize therefore with |APP_STATE_INACTIVE|.
316   UpdateAppItemState(contents,
317                      ChromeLauncherController::APP_STATE_INACTIVE);
318   AddWebContentsObserver(contents);
319 }
320 
TabClosingAt(TabStripModel * tab_strip_mode,content::WebContents * contents,int index)321 void BrowserStatusMonitor::TabClosingAt(TabStripModel* tab_strip_mode,
322                                         content::WebContents* contents,
323                                         int index) {
324   UpdateAppItemState(contents,
325                      ChromeLauncherController::APP_STATE_REMOVED);
326   RemoveWebContentsObserver(contents);
327 }
328 
WebContentsDestroyed(content::WebContents * contents)329 void BrowserStatusMonitor::WebContentsDestroyed(
330     content::WebContents* contents) {
331   UpdateAppItemState(contents, ChromeLauncherController::APP_STATE_REMOVED);
332   RemoveWebContentsObserver(contents);
333 }
334 
AddV1AppToShelf(Browser * browser)335 void BrowserStatusMonitor::AddV1AppToShelf(Browser* browser) {
336   DCHECK(browser->is_type_popup() && browser->is_app());
337 
338   browser->tab_strip_model()->AddObserver(this);
339 
340   std::string app_id =
341       web_app::GetExtensionIdFromApplicationName(browser->app_name());
342   if (!app_id.empty()) {
343     browser_to_app_id_map_[browser] = app_id;
344     launcher_controller_->LockV1AppWithID(app_id);
345   }
346 }
347 
RemoveV1AppFromShelf(Browser * browser)348 void BrowserStatusMonitor::RemoveV1AppFromShelf(Browser* browser) {
349   DCHECK(browser->is_type_popup() && browser->is_app());
350 
351   browser->tab_strip_model()->RemoveObserver(this);
352 
353   if (browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end()) {
354     launcher_controller_->UnlockV1AppWithID(browser_to_app_id_map_[browser]);
355     browser_to_app_id_map_.erase(browser);
356   }
357 }
358 
IsV1AppInShelf(Browser * browser)359 bool BrowserStatusMonitor::IsV1AppInShelf(Browser* browser) {
360   return browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end();
361 }
362 
AddWebContentsObserver(content::WebContents * contents)363 void BrowserStatusMonitor::AddWebContentsObserver(
364     content::WebContents* contents) {
365   if (webcontents_to_observer_map_.find(contents) ==
366           webcontents_to_observer_map_.end()) {
367     webcontents_to_observer_map_[contents] =
368         new LocalWebContentsObserver(contents, this);
369   }
370 }
371 
RemoveWebContentsObserver(content::WebContents * contents)372 void BrowserStatusMonitor::RemoveWebContentsObserver(
373     content::WebContents* contents) {
374   DCHECK(webcontents_to_observer_map_.find(contents) !=
375       webcontents_to_observer_map_.end());
376   delete webcontents_to_observer_map_[contents];
377   webcontents_to_observer_map_.erase(contents);
378 }
379 
GetShelfIDForWebContents(content::WebContents * contents)380 ash::ShelfID BrowserStatusMonitor::GetShelfIDForWebContents(
381     content::WebContents* contents) {
382   return launcher_controller_->GetShelfIDForWebContents(contents);
383 }
384 
SetShelfIDForBrowserWindowContents(Browser * browser,content::WebContents * web_contents)385 void BrowserStatusMonitor::SetShelfIDForBrowserWindowContents(
386     Browser* browser,
387     content::WebContents* web_contents) {
388   launcher_controller_->GetBrowserShortcutLauncherItemController()->
389       SetShelfIDForBrowserWindowContents(browser, web_contents);
390 }
391