Home | History | Annotate | Download | only in launcher
      1 // Copyright (c) 2012 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_launcher_item_controller.h"
      6 
      7 #include "ash/launcher/launcher.h"
      8 #include "ash/launcher/launcher_model.h"
      9 #include "ash/shell.h"
     10 #include "ash/wm/window_util.h"
     11 #include "chrome/browser/extensions/extension_service.h"
     12 #include "chrome/browser/extensions/tab_helper.h"
     13 #include "chrome/browser/favicon/favicon_tab_helper.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
     16 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
     17 #include "chrome/browser/ui/browser.h"
     18 #include "chrome/browser/ui/browser_list.h"
     19 #include "chrome/browser/ui/browser_window.h"
     20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     21 #include "chrome/browser/web_applications/web_app.h"
     22 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
     23 #include "content/public/browser/web_contents.h"
     24 #include "grit/ui_resources.h"
     25 #include "ui/aura/client/aura_constants.h"
     26 #include "ui/aura/window.h"
     27 #include "ui/base/resource/resource_bundle.h"
     28 #include "ui/views/widget/widget.h"
     29 
     30 BrowserLauncherItemController::BrowserLauncherItemController(
     31     Type type,
     32     aura::Window* window,
     33     TabStripModel* tab_model,
     34     ChromeLauncherController* launcher_controller,
     35     const std::string& app_id)
     36     : LauncherItemController(type, app_id, launcher_controller),
     37       window_(window),
     38       tab_model_(tab_model),
     39       is_incognito_(tab_model->profile()->GetOriginalProfile() !=
     40                     tab_model->profile() &&
     41                     !tab_model->profile()->IsGuestSession()) {
     42   DCHECK(window_);
     43   window_->AddObserver(this);
     44 }
     45 
     46 BrowserLauncherItemController::~BrowserLauncherItemController() {
     47   tab_model_->RemoveObserver(this);
     48   window_->RemoveObserver(this);
     49   if (launcher_id() > 0)
     50     launcher_controller()->CloseLauncherItem(launcher_id());
     51   if (type() == TYPE_WINDOWED_APP)
     52     launcher_controller()->UnlockV1AppWithID(LauncherItemController::app_id());
     53 }
     54 
     55 const std::string& BrowserLauncherItemController::app_id() const {
     56   if (type() == TYPE_WINDOWED_APP)
     57     return empty_app_id_;
     58   return LauncherItemController::app_id();
     59 }
     60 
     61 void BrowserLauncherItemController::Init() {
     62   tab_model_->AddObserver(this);
     63   ash::LauncherItemStatus app_status =
     64       ash::wm::IsActiveWindow(window_) ?
     65       ash::STATUS_ACTIVE : ash::STATUS_RUNNING;
     66   if (type() != TYPE_TABBED && type() != TYPE_WINDOWED_APP) {
     67     launcher_controller()->CreateAppLauncherItem(this, app_id(), app_status);
     68   } else {
     69     launcher_controller()->CreateTabbedLauncherItem(
     70         this,
     71         is_incognito_ ? ChromeLauncherController::STATE_INCOGNITO :
     72                         ChromeLauncherController::STATE_NOT_INCOGNITO,
     73         app_status);
     74     if (type() == TYPE_WINDOWED_APP)
     75       launcher_controller()->LockV1AppWithID(LauncherItemController::app_id());
     76   }
     77   // In testing scenarios we can get tab strips with no active contents.
     78   if (tab_model_->active_index() != TabStripModel::kNoTab)
     79     UpdateLauncher(tab_model_->GetActiveWebContents());
     80 }
     81 
     82 // static
     83 BrowserLauncherItemController* BrowserLauncherItemController::Create(
     84     Browser* browser) {
     85   // Under testing this can be called before the controller is created.
     86   if (!ChromeLauncherController::instance())
     87     return NULL;
     88 
     89   Type type;
     90   std::string app_id;
     91   if (browser->is_type_tabbed() || browser->is_type_popup()) {
     92     type = TYPE_TABBED;
     93     if (!browser->is_type_tabbed() &&
     94         browser->is_type_popup() &&
     95         browser->is_app() &&
     96         ChromeLauncherController::instance()->GetPerAppInterface()) {
     97       app_id = web_app::GetExtensionIdFromApplicationName(
     98                     browser->app_name());
     99       // Only allow this for known applications. Some unit tests for example
    100       // do not have one.
    101       if (!app_id.empty())
    102         type = TYPE_WINDOWED_APP;
    103     }
    104   } else if (browser->is_app()) {
    105     type = TYPE_TABBED;
    106     app_id = web_app::GetExtensionIdFromApplicationName(browser->app_name());
    107   } else {
    108     return NULL;
    109   }
    110   BrowserLauncherItemController* controller =
    111       new BrowserLauncherItemController(type,
    112                                         browser->window()->GetNativeWindow(),
    113                                         browser->tab_strip_model(),
    114                                         ChromeLauncherController::instance(),
    115                                         app_id);
    116   controller->Init();
    117   return controller;
    118 }
    119 
    120 void BrowserLauncherItemController::BrowserActivationStateChanged() {
    121   content::WebContents* active_contents = tab_model_->GetActiveWebContents();
    122   if (active_contents)
    123     UpdateAppState(active_contents);
    124   UpdateItemStatus();
    125 }
    126 
    127 string16 BrowserLauncherItemController::GetTitle() {
    128   if (type() == TYPE_TABBED) {
    129     if (tab_model_->active_index() != TabStripModel::kNoTab) {
    130       const content::WebContents* contents = tab_model_->GetActiveWebContents();
    131       if (contents)
    132         return contents->GetTitle();
    133     }
    134   }
    135   return GetAppTitle();
    136 }
    137 
    138 bool BrowserLauncherItemController::HasWindow(aura::Window* window) const {
    139   return window_ == window;
    140 }
    141 
    142 bool BrowserLauncherItemController::IsOpen() const {
    143   return true;
    144 }
    145 
    146 bool BrowserLauncherItemController::IsVisible() const {
    147   return window_->IsVisible();
    148 }
    149 
    150 void BrowserLauncherItemController::Launch(int event_flags) {
    151   DCHECK(!app_id().empty());
    152   launcher_controller()->LaunchApp(app_id(), event_flags);
    153 }
    154 
    155 void BrowserLauncherItemController::Activate() {
    156   window_->Show();
    157   ash::wm::ActivateWindow(window_);
    158 }
    159 
    160 void BrowserLauncherItemController::Close() {
    161   views::Widget* widget = views::Widget::GetWidgetForNativeView(window_);
    162   if (widget)
    163     widget->Close();
    164 }
    165 
    166 void BrowserLauncherItemController::Clicked(const ui::Event& event) {
    167   views::Widget* widget =
    168       views::Widget::GetWidgetForNativeView(window_);
    169   if (widget && widget->IsActive()) {
    170     widget->Minimize();
    171   } else {
    172     Activate();
    173   }
    174 }
    175 
    176 void BrowserLauncherItemController::OnRemoved() {
    177 }
    178 
    179 void BrowserLauncherItemController::LauncherItemChanged(
    180     int index,
    181     const ash::LauncherItem& old_item) {
    182   if (!launcher_controller()->GetPerAppInterface() &&
    183       launcher_model()->items()[index].status == ash::STATUS_ACTIVE &&
    184       old_item.status == ash::STATUS_RUNNING) {
    185     Activate();
    186   }
    187 }
    188 
    189 ChromeLauncherAppMenuItems
    190 BrowserLauncherItemController::GetApplicationList(int event_flags) {
    191   // This will never be called and the entire class will go away.
    192   ChromeLauncherAppMenuItems items;
    193   return items.Pass();
    194 }
    195 
    196 void BrowserLauncherItemController::ActiveTabChanged(
    197     content::WebContents* old_contents,
    198     content::WebContents* new_contents,
    199     int index,
    200     int reason) {
    201   // Update immediately on a tab change.
    202   if (old_contents &&
    203       (!launcher_controller()->GetPerAppInterface() ||
    204        TabStripModel::kNoTab !=
    205            tab_model_->GetIndexOfWebContents(old_contents)))
    206     UpdateAppState(old_contents);
    207   UpdateAppState(new_contents);
    208   UpdateLauncher(new_contents);
    209 }
    210 
    211 void BrowserLauncherItemController::TabInsertedAt(
    212     content::WebContents* contents,
    213     int index,
    214     bool foreground) {
    215   UpdateAppState(contents);
    216 }
    217 
    218 void BrowserLauncherItemController::TabDetachedAt(
    219     content::WebContents* contents,
    220     int index) {
    221   launcher_controller()->UpdateAppState(
    222       contents, ChromeLauncherController::APP_STATE_REMOVED);
    223 }
    224 
    225 void BrowserLauncherItemController::TabChangedAt(
    226     content::WebContents* contents,
    227     int index,
    228     TabStripModelObserver::TabChangeType change_type) {
    229   UpdateAppState(contents);
    230   if (index != tab_model_->active_index() ||
    231       !(change_type != TabStripModelObserver::LOADING_ONLY &&
    232         change_type != TabStripModelObserver::TITLE_NOT_LOADING)) {
    233     return;
    234   }
    235 
    236   UpdateLauncher(contents);
    237 }
    238 
    239 void BrowserLauncherItemController::TabReplacedAt(
    240     TabStripModel* tab_strip_model,
    241     content::WebContents* old_contents,
    242     content::WebContents* new_contents,
    243     int index) {
    244   launcher_controller()->UpdateAppState(
    245       old_contents,
    246       ChromeLauncherController::APP_STATE_REMOVED);
    247   UpdateAppState(new_contents);
    248 }
    249 
    250 void BrowserLauncherItemController::OnWindowPropertyChanged(
    251     aura::Window* window,
    252     const void* key,
    253     intptr_t old) {
    254   if (key == aura::client::kDrawAttentionKey)
    255     UpdateItemStatus();
    256 }
    257 
    258 void BrowserLauncherItemController::UpdateItemStatus() {
    259   if (launcher_controller()->GetPerAppInterface())
    260     return;
    261 
    262   ash::LauncherItemStatus status;
    263   if (ash::wm::IsActiveWindow(window_)) {
    264     // Clear attention state if active.
    265     if (window_->GetProperty(aura::client::kDrawAttentionKey))
    266       window_->SetProperty(aura::client::kDrawAttentionKey, false);
    267     status = ash::STATUS_ACTIVE;
    268   } else if (window_->GetProperty(aura::client::kDrawAttentionKey)) {
    269     status = ash::STATUS_ATTENTION;
    270   } else {
    271     status = ash::STATUS_RUNNING;
    272   }
    273   launcher_controller()->SetItemStatus(launcher_id(), status);
    274 }
    275 
    276 void BrowserLauncherItemController::UpdateLauncher(content::WebContents* tab) {
    277   if (launcher_controller()->GetPerAppInterface())
    278     return;
    279 
    280   if (type() == TYPE_APP_PANEL)
    281     return;  // Maintained entirely by ChromeLauncherController.
    282 
    283   if (!tab)
    284     return;  // Assume the window is going to be closed if there are no tabs.
    285 
    286   int item_index = launcher_model()->ItemIndexByID(launcher_id());
    287   if (item_index == -1)
    288     return;
    289 
    290   ash::LauncherItem item = launcher_model()->items()[item_index];
    291   DCHECK_EQ(TYPE_TABBED, type());
    292   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    293   FaviconTabHelper* favicon_tab_helper =
    294       FaviconTabHelper::FromWebContents(tab);
    295   if (favicon_tab_helper->ShouldDisplayFavicon()) {
    296     item.image = favicon_tab_helper->GetFavicon().AsImageSkia();
    297     if (item.image.isNull()) {
    298       item.image = *rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
    299     }
    300   } else {
    301     item.image = *rb.GetImageSkiaNamed(IDR_DEFAULT_FAVICON);
    302   }
    303   launcher_model()->Set(item_index, item);
    304 }
    305 
    306 void BrowserLauncherItemController::UpdateAppState(content::WebContents* tab) {
    307   ChromeLauncherController::AppState app_state;
    308 
    309   if (!launcher_controller()->GetPerAppInterface() &&
    310       tab_model_->GetIndexOfWebContents(tab) == TabStripModel::kNoTab) {
    311     app_state = ChromeLauncherController::APP_STATE_REMOVED;
    312   } else if (tab_model_->GetActiveWebContents() == tab) {
    313     if (ash::wm::IsActiveWindow(window_))
    314       app_state = ChromeLauncherController::APP_STATE_WINDOW_ACTIVE;
    315     else
    316       app_state = ChromeLauncherController::APP_STATE_ACTIVE;
    317   } else {
    318     app_state = ChromeLauncherController::APP_STATE_INACTIVE;
    319   }
    320   launcher_controller()->UpdateAppState(tab, app_state);
    321 }
    322 
    323 ash::LauncherModel* BrowserLauncherItemController::launcher_model() {
    324   return launcher_controller()->model();
    325 }
    326