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