Update To 11.40.268.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/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"
30
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 {
35  public:
36   LocalWebContentsObserver(content::WebContents* contents,
37                            BrowserStatusMonitor* monitor)
38       : content::WebContentsObserver(contents),
39         monitor_(monitor) {}
40
41   ~LocalWebContentsObserver() override {}
42
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;
55
56     monitor_->UpdateAppItemState(web_contents(), state);
57     monitor_->UpdateBrowserItemState();
58
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());
62   }
63
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.
69   }
70
71  private:
72   BrowserStatusMonitor* monitor_;
73
74   DISALLOW_COPY_AND_ASSIGN(LocalWebContentsObserver);
75 };
76
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 {
81  public:
82   SettingsWindowObserver() {}
83   ~SettingsWindowObserver() override {}
84
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));
91   }
92
93  private:
94   DISALLOW_COPY_AND_ASSIGN(SettingsWindowObserver);
95 };
96
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());
107
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));
122     }
123     ash::Shell::GetInstance()->GetScreen()->AddObserver(this);
124   }
125 }
126
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);
132
133   BrowserList::RemoveObserver(this);
134
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);
140   }
141
142   STLDeleteContainerPairSecondPointers(webcontents_to_observer_map_.begin(),
143                                        webcontents_to_observer_map_.end());
144 }
145
146 void BrowserStatusMonitor::UpdateAppItemState(
147     content::WebContents* contents,
148     ChromeLauncherController::AppState app_state) {
149   DCHECK(contents);
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
152   // processed.
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);
157 }
158
159 void BrowserStatusMonitor::UpdateBrowserItemState() {
160   launcher_controller_->GetBrowserShortcutLauncherItemController()->
161       UpdateBrowserItemState();
162 }
163
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.
170   if (lost_active) {
171     browser = chrome::FindBrowserWithWindow(lost_active);
172     if (browser)
173       contents_from_lost = browser->tab_strip_model()->GetActiveWebContents();
174     if (contents_from_lost) {
175       UpdateAppItemState(
176           contents_from_lost,
177           ChromeLauncherController::APP_STATE_INACTIVE);
178     }
179   }
180
181   // Update active webcontents's app item state of |gained_active|, if existed.
182   if (gained_active) {
183     browser = chrome::FindBrowserWithWindow(gained_active);
184     if (browser)
185       contents_from_gained = browser->tab_strip_model()->GetActiveWebContents();
186     if (contents_from_gained) {
187       UpdateAppItemState(
188           contents_from_gained,
189           ChromeLauncherController::APP_STATE_WINDOW_ACTIVE);
190     }
191   }
192
193   if (contents_from_lost || contents_from_gained)
194     UpdateBrowserItemState();
195 }
196
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));
202 }
203
204 void BrowserStatusMonitor::OnBrowserAdded(Browser* browser) {
205   if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
206     return;
207
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);
213   } else {
214     browser->tab_strip_model()->AddObserver(this);
215   }
216 }
217
218 void BrowserStatusMonitor::OnBrowserRemoved(Browser* browser) {
219   if (browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
220     return;
221
222   if (browser->is_type_popup() && browser->is_app())
223     RemoveV1AppFromShelf(browser);
224   else
225     browser->tab_strip_model()->RemoveObserver(this);
226
227   UpdateBrowserItemState();
228 }
229
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));
240   }
241 }
242
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().
247   // Do nothing here.
248 }
249
250 void BrowserStatusMonitor::OnDisplayMetricsChanged(const gfx::Display&,
251                                                    uint32_t) {
252   // Do nothing here.
253 }
254
255 void BrowserStatusMonitor::ActiveTabChanged(content::WebContents* old_contents,
256                                             content::WebContents* new_contents,
257                                             int index,
258                                             int reason) {
259   Browser* browser = NULL;
260   // Use |new_contents|. |old_contents| could be NULL.
261   DCHECK(new_contents);
262   browser = chrome::FindBrowserWithWebContents(new_contents);
263
264   if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
265     return;
266
267   ChromeLauncherController::AppState state =
268       ChromeLauncherController::APP_STATE_INACTIVE;
269
270   // Update immediately on a tab change.
271   if (old_contents &&
272       (TabStripModel::kNoTab !=
273            browser->tab_strip_model()->GetIndexOfWebContents(old_contents)))
274     UpdateAppItemState(old_contents, state);
275
276   if (new_contents) {
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);
283   }
284 }
285
286 void BrowserStatusMonitor::TabReplacedAt(TabStripModel* tab_strip_model,
287                                          content::WebContents* old_contents,
288                                          content::WebContents* new_contents,
289                                          int index) {
290   DCHECK(old_contents && new_contents);
291   Browser* browser = chrome::FindBrowserWithWebContents(new_contents);
292
293   if (browser && browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH)
294     return;
295
296   UpdateAppItemState(old_contents,
297                      ChromeLauncherController::APP_STATE_REMOVED);
298   RemoveWebContentsObserver(old_contents);
299
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();
307
308   if (tab_strip_model->GetActiveWebContents() == new_contents)
309     SetShelfIDForBrowserWindowContents(browser, new_contents);
310
311   AddWebContentsObserver(new_contents);
312 }
313
314 void BrowserStatusMonitor::TabInsertedAt(content::WebContents* contents,
315                                          int index,
316                                          bool foreground) {
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);
322 }
323
324 void BrowserStatusMonitor::TabClosingAt(TabStripModel* tab_strip_mode,
325                                         content::WebContents* contents,
326                                         int index) {
327   UpdateAppItemState(contents,
328                      ChromeLauncherController::APP_STATE_REMOVED);
329   RemoveWebContentsObserver(contents);
330 }
331
332 void BrowserStatusMonitor::WebContentsDestroyed(
333     content::WebContents* contents) {
334   UpdateAppItemState(contents, ChromeLauncherController::APP_STATE_REMOVED);
335   RemoveWebContentsObserver(contents);
336 }
337
338 void BrowserStatusMonitor::AddV1AppToShelf(Browser* browser) {
339   DCHECK(browser->is_type_popup() && browser->is_app());
340
341   browser->tab_strip_model()->AddObserver(this);
342
343   std::string app_id =
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);
348   }
349 }
350
351 void BrowserStatusMonitor::RemoveV1AppFromShelf(Browser* browser) {
352   DCHECK(browser->is_type_popup() && browser->is_app());
353
354   browser->tab_strip_model()->RemoveObserver(this);
355
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);
359   }
360 }
361
362 bool BrowserStatusMonitor::IsV1AppInShelf(Browser* browser) {
363   return browser_to_app_id_map_.find(browser) != browser_to_app_id_map_.end();
364 }
365
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);
372   }
373 }
374
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);
381 }
382
383 ash::ShelfID BrowserStatusMonitor::GetShelfIDForWebContents(
384     content::WebContents* contents) {
385   return launcher_controller_->GetShelfIDForWebContents(contents);
386 }
387
388 void BrowserStatusMonitor::SetShelfIDForBrowserWindowContents(
389     Browser* browser,
390     content::WebContents* web_contents) {
391   launcher_controller_->GetBrowserShortcutLauncherItemController()->
392       SetShelfIDForBrowserWindowContents(browser, web_contents);
393 }