Home | History | Annotate | Download | only in launcher
      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   virtual ~LocalWebContentsObserver() {}
     42 
     43   // content::WebContentsObserver
     44   virtual 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   virtual 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   virtual ~SettingsWindowObserver() {}
     84 
     85   // SettingsWindowManagerObserver
     86   virtual 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 }
    394