Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / ash / launcher / browser_status_monitor.cc
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/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"
24
25 BrowserStatusMonitor::LocalWebContentsObserver::LocalWebContentsObserver(
26     content::WebContents* contents,
27     BrowserStatusMonitor* monitor)
28     : content::WebContentsObserver(contents),
29       monitor_(monitor) {
30 }
31
32 BrowserStatusMonitor::LocalWebContentsObserver::~LocalWebContentsObserver() {
33 }
34
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;
46
47   monitor_->UpdateAppItemState(web_contents(), state);
48   monitor_->UpdateBrowserItemState();
49
50   // Navigating may change the ShelfID associated with the WebContents.
51   if (browser->tab_strip_model()->GetActiveWebContents() == web_contents()) {
52     ash::SetShelfIDForWindow(
53         monitor_->GetShelfIDForWebContents(web_contents()),
54         browser->window()->GetNativeWindow());
55   }
56 }
57
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);
65
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));
80     }
81     ash::Shell::GetInstance()->GetScreen()->AddObserver(this);
82   }
83 }
84
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);
90
91   BrowserList::RemoveObserver(this);
92
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) {
97     OnBrowserRemoved(*i);
98   }
99
100   STLDeleteContainerPairSecondPointers(webcontents_to_observer_map_.begin(),
101                                        webcontents_to_observer_map_.end());
102 }
103
104 void BrowserStatusMonitor::UpdateAppItemState(
105     content::WebContents* contents,
106     ChromeLauncherController::AppState app_state) {
107   DCHECK(contents);
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);
113 }
114
115 void BrowserStatusMonitor::UpdateBrowserItemState() {
116   launcher_controller_->GetBrowserShortcutLauncherItemController()->
117       UpdateBrowserItemState();
118 }
119
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.
126   if (lost_active) {
127     browser = chrome::FindBrowserWithWindow(lost_active);
128     if (browser)
129       contents_from_lost = browser->tab_strip_model()->GetActiveWebContents();
130     if (contents_from_lost) {
131       UpdateAppItemState(
132           contents_from_lost,
133           ChromeLauncherController::APP_STATE_INACTIVE);
134     }
135   }
136
137   // Update active webcontents's app item state of |gained_active|, if existed.
138   if (gained_active) {
139     browser = chrome::FindBrowserWithWindow(gained_active);
140     if (browser)
141       contents_from_gained = browser->tab_strip_model()->GetActiveWebContents();
142     if (contents_from_gained) {
143       UpdateAppItemState(
144           contents_from_gained,
145           ChromeLauncherController::APP_STATE_WINDOW_ACTIVE);
146     }
147   }
148
149   if (contents_from_lost || contents_from_gained)
150     UpdateBrowserItemState();
151 }
152
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));
158 }
159
160 void BrowserStatusMonitor::OnBrowserAdded(Browser* browser) {
161   if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
162     return;
163
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);
169   } else {
170     browser->tab_strip_model()->AddObserver(this);
171   }
172 }
173
174 void BrowserStatusMonitor::OnBrowserRemoved(Browser* browser) {
175   if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
176     return;
177
178   if (browser->is_type_popup() && browser->is_app())
179     RemoveV1AppFromShelf(browser);
180   else
181     browser->tab_strip_model()->RemoveObserver(this);
182
183   UpdateBrowserItemState();
184 }
185
186 void BrowserStatusMonitor::OnDisplayBoundsChanged(
187     const gfx::Display& display) {
188   // Do nothing here.
189 }
190
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));
201   }
202 }
203
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().
208   // Do nothing here.
209 }
210
211 void BrowserStatusMonitor::ActiveTabChanged(content::WebContents* old_contents,
212                                             content::WebContents* new_contents,
213                                             int index,
214                                             int reason) {
215   Browser* browser = NULL;
216   // Use |new_contents|. |old_contents| could be NULL.
217   DCHECK(new_contents);
218   browser = chrome::FindBrowserWithWebContents(new_contents);
219
220   if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
221     return;
222
223   ChromeLauncherController::AppState state =
224       ChromeLauncherController::APP_STATE_INACTIVE;
225
226   // Update immediately on a tab change.
227   if (old_contents &&
228       (TabStripModel::kNoTab !=
229            browser->tab_strip_model()->GetIndexOfWebContents(old_contents)))
230     UpdateAppItemState(old_contents, state);
231
232   if (new_contents) {
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::SetShelfIDForWindow(GetShelfIDForWebContents(new_contents),
239                              browser->window()->GetNativeWindow());
240   }
241 }
242
243 void BrowserStatusMonitor::TabReplacedAt(TabStripModel* tab_strip_model,
244                                          content::WebContents* old_contents,
245                                          content::WebContents* new_contents,
246                                          int index) {
247   DCHECK(old_contents && new_contents);
248   Browser* browser = chrome::FindBrowserWithWebContents(new_contents);
249
250   if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
251     return;
252
253   UpdateAppItemState(old_contents,
254                      ChromeLauncherController::APP_STATE_REMOVED);
255   RemoveWebContentsObserver(old_contents);
256
257   ChromeLauncherController::AppState state =
258       ChromeLauncherController::APP_STATE_ACTIVE;
259   if (browser->window()->IsActive() &&
260       (tab_strip_model->GetActiveWebContents() == new_contents))
261     state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
262   UpdateAppItemState(new_contents, state);
263   UpdateBrowserItemState();
264
265   if (tab_strip_model->GetActiveWebContents() == new_contents) {
266     ash::SetShelfIDForWindow(GetShelfIDForWebContents(new_contents),
267                              browser->window()->GetNativeWindow());
268   }
269
270   AddWebContentsObserver(new_contents);
271 }
272
273 void BrowserStatusMonitor::TabInsertedAt(content::WebContents* contents,
274                                          int index,
275                                          bool foreground) {
276   // An inserted tab is not active - ActiveTabChanged() will be called to
277   // activate. We initialize therefore with |APP_STATE_INACTIVE|.
278   UpdateAppItemState(contents,
279                      ChromeLauncherController::APP_STATE_INACTIVE);
280   AddWebContentsObserver(contents);
281 }
282
283 void BrowserStatusMonitor::TabClosingAt(TabStripModel* tab_strip_mode,
284                                         content::WebContents* contents,
285                                         int index) {
286   UpdateAppItemState(contents,
287                      ChromeLauncherController::APP_STATE_REMOVED);
288   RemoveWebContentsObserver(contents);
289 }
290
291 void BrowserStatusMonitor::AddV1AppToShelf(Browser* browser) {
292   DCHECK(browser->is_type_popup() && browser->is_app());
293
294   browser->tab_strip_model()->AddObserver(this);
295
296   std::string app_id =
297       web_app::GetExtensionIdFromApplicationName(browser->app_name());
298   if (!app_id.empty()) {
299     browser_to_app_id_map_[browser] = app_id;
300     launcher_controller_->LockV1AppWithID(app_id);
301   }
302 }
303
304 void BrowserStatusMonitor::RemoveV1AppFromShelf(Browser* browser) {
305   DCHECK(browser->is_type_popup() && browser->is_app());
306
307   browser->tab_strip_model()->RemoveObserver(this);
308
309   if (browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end()) {
310     launcher_controller_->UnlockV1AppWithID(browser_to_app_id_map_[browser]);
311     browser_to_app_id_map_.erase(browser);
312   }
313 }
314
315 bool BrowserStatusMonitor::IsV1AppInShelf(Browser* browser) {
316   return browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end();
317 }
318
319 void BrowserStatusMonitor::AddWebContentsObserver(
320     content::WebContents* contents) {
321   if (webcontents_to_observer_map_.find(contents) ==
322           webcontents_to_observer_map_.end()) {
323     webcontents_to_observer_map_[contents] =
324         new LocalWebContentsObserver(contents, this);
325   }
326 }
327
328 void BrowserStatusMonitor::RemoveWebContentsObserver(
329     content::WebContents* contents) {
330   DCHECK(webcontents_to_observer_map_.find(contents) !=
331       webcontents_to_observer_map_.end());
332   delete webcontents_to_observer_map_[contents];
333   webcontents_to_observer_map_.erase(contents);
334 }
335
336 ash::ShelfID BrowserStatusMonitor::GetShelfIDForWebContents(
337     content::WebContents* contents) {
338   return launcher_controller_->GetShelfIDForWebContents(contents);
339 }