1// Copyright (c) 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_shortcut_launcher_item_controller.h" 6 7#include <vector> 8 9#include "ash/shelf/shelf.h" 10#include "ash/shelf/shelf_model.h" 11#include "ash/shelf/shelf_util.h" 12#include "ash/shell.h" 13#include "ash/wm/window_util.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_app_menu_item_browser.h" 17#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h" 18#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" 19#include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h" 20#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h" 21#include "chrome/browser/ui/browser.h" 22#include "chrome/browser/ui/browser_finder.h" 23#include "chrome/browser/ui/browser_list.h" 24#include "chrome/browser/ui/browser_window.h" 25#include "chrome/browser/ui/chrome_pages.h" 26#include "chrome/browser/ui/settings_window_manager.h" 27#include "chrome/browser/ui/tabs/tab_strip_model.h" 28#include "chrome/browser/web_applications/web_app.h" 29#include "chrome/common/extensions/extension_constants.h" 30#include "chrome/grit/chromium_strings.h" 31#include "chrome/grit/generated_resources.h" 32#include "content/public/browser/web_contents.h" 33#include "content/public/common/url_constants.h" 34#include "grit/ash_resources.h" 35#include "ui/aura/window.h" 36#include "ui/base/l10n/l10n_util.h" 37#include "ui/base/resource/resource_bundle.h" 38#include "ui/events/event.h" 39#include "ui/gfx/image/image.h" 40#include "ui/wm/core/window_animations.h" 41 42BrowserShortcutLauncherItemController::BrowserShortcutLauncherItemController( 43 ChromeLauncherController* launcher_controller) 44 : LauncherItemController(TYPE_SHORTCUT, 45 extension_misc::kChromeAppId, 46 launcher_controller) { 47} 48 49BrowserShortcutLauncherItemController:: 50 ~BrowserShortcutLauncherItemController() { 51} 52 53void BrowserShortcutLauncherItemController::UpdateBrowserItemState() { 54 // The shell will not be available for win7_aura unittests like 55 // ChromeLauncherControllerTest.BrowserMenuGeneration. 56 if (!ash::Shell::HasInstance()) 57 return; 58 59 ash::ShelfModel* model = launcher_controller()->model(); 60 61 // Determine the new browser's active state and change if necessary. 62 int browser_index = model->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT); 63 DCHECK_GE(browser_index, 0); 64 ash::ShelfItem browser_item = model->items()[browser_index]; 65 ash::ShelfItemStatus browser_status = ash::STATUS_CLOSED; 66 67 aura::Window* window = ash::wm::GetActiveWindow(); 68 if (window) { 69 // Check if the active browser / tab is a browser which is not an app, 70 // a windowed app, a popup or any other item which is not a browser of 71 // interest. 72 Browser* browser = chrome::FindBrowserWithWindow(window); 73 if (IsBrowserRepresentedInBrowserList(browser)) { 74 browser_status = ash::STATUS_ACTIVE; 75 // If an app that has item is running in active WebContents, browser item 76 // status cannot be active. 77 content::WebContents* contents = 78 browser->tab_strip_model()->GetActiveWebContents(); 79 if (contents && 80 (launcher_controller()->GetShelfIDForWebContents(contents) != 81 browser_item.id)) 82 browser_status = ash::STATUS_RUNNING; 83 } 84 } 85 86 if (browser_status == ash::STATUS_CLOSED) { 87 const BrowserList* ash_browser_list = 88 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH); 89 for (BrowserList::const_reverse_iterator it = 90 ash_browser_list->begin_last_active(); 91 it != ash_browser_list->end_last_active() && 92 browser_status == ash::STATUS_CLOSED; ++it) { 93 if (IsBrowserRepresentedInBrowserList(*it)) 94 browser_status = ash::STATUS_RUNNING; 95 } 96 } 97 98 if (browser_status != browser_item.status) { 99 browser_item.status = browser_status; 100 model->Set(browser_index, browser_item); 101 } 102} 103 104void BrowserShortcutLauncherItemController::SetShelfIDForBrowserWindowContents( 105 Browser* browser, 106 content::WebContents* web_contents) { 107 // We need to call SetShelfIDForWindow for V1 applications since they are 108 // content which might change and as such change the application type. 109 if (!browser || 110 !launcher_controller()->IsBrowserFromActiveUser(browser) || 111 browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH || 112 chrome::IsTrustedPopupWindowWithScheme(browser, content::kChromeUIScheme)) 113 return; 114 115 ash::SetShelfIDForWindow( 116 launcher_controller()->GetShelfIDForWebContents(web_contents), 117 browser->window()->GetNativeWindow()); 118} 119 120bool BrowserShortcutLauncherItemController::IsOpen() const { 121 const BrowserList* ash_browser_list = 122 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH); 123 for (BrowserList::const_iterator it = ash_browser_list->begin(); 124 it != ash_browser_list->end(); ++it) { 125 if (launcher_controller()->IsBrowserFromActiveUser(*it)) 126 return true; 127 } 128 return false; 129} 130 131bool BrowserShortcutLauncherItemController::IsVisible() const { 132 Browser* last_browser = chrome::FindTabbedBrowser( 133 launcher_controller()->profile(), 134 true, 135 chrome::HOST_DESKTOP_TYPE_ASH); 136 137 if (!last_browser) { 138 return false; 139 } 140 141 aura::Window* window = last_browser->window()->GetNativeWindow(); 142 return ash::wm::IsActiveWindow(window); 143} 144 145void BrowserShortcutLauncherItemController::Launch(ash::LaunchSource source, 146 int event_flags) { 147} 148 149bool BrowserShortcutLauncherItemController::Activate(ash::LaunchSource source) { 150 Browser* last_browser = chrome::FindTabbedBrowser( 151 launcher_controller()->profile(), 152 true, 153 chrome::HOST_DESKTOP_TYPE_ASH); 154 155 if (!last_browser) { 156 launcher_controller()->CreateNewWindow(); 157 return true; 158 } 159 160 launcher_controller()->ActivateWindowOrMinimizeIfActive( 161 last_browser->window(), GetApplicationList(0).size() == 2); 162 return false; 163} 164 165void BrowserShortcutLauncherItemController::Close() { 166} 167 168ChromeLauncherAppMenuItems 169BrowserShortcutLauncherItemController::GetApplicationList(int event_flags) { 170 ChromeLauncherAppMenuItems items; 171 bool found_tabbed_browser = false; 172 // Add the application name to the menu. 173 items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false)); 174 const BrowserList* ash_browser_list = 175 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH); 176 for (BrowserList::const_iterator it = ash_browser_list->begin(); 177 it != ash_browser_list->end(); ++it) { 178 Browser* browser = *it; 179 // Make sure that the browser was already shown, is from the current user 180 // and has a proper window. 181 if (!launcher_controller()->IsBrowserFromActiveUser(browser) || 182 std::find(ash_browser_list->begin_last_active(), 183 ash_browser_list->end_last_active(), 184 browser) == ash_browser_list->end_last_active() || 185 !browser->window()) 186 continue; 187 if (browser->is_type_tabbed()) 188 found_tabbed_browser = true; 189 else if (!IsBrowserRepresentedInBrowserList(browser)) 190 continue; 191 TabStripModel* tab_strip = browser->tab_strip_model(); 192 if (tab_strip->active_index() == -1) 193 continue; 194 if (!(event_flags & ui::EF_SHIFT_DOWN)) { 195 content::WebContents* web_contents = 196 tab_strip->GetWebContentsAt(tab_strip->active_index()); 197 gfx::Image app_icon = GetBrowserListIcon(web_contents); 198 base::string16 title = GetBrowserListTitle(web_contents); 199 items.push_back(new ChromeLauncherAppMenuItemBrowser( 200 title, &app_icon, browser, items.size() == 1)); 201 } else { 202 for (int index = 0; index < tab_strip->count(); ++index) { 203 content::WebContents* web_contents = 204 tab_strip->GetWebContentsAt(index); 205 gfx::Image app_icon = 206 launcher_controller()->GetAppListIcon(web_contents); 207 base::string16 title = 208 launcher_controller()->GetAppListTitle(web_contents); 209 // Check if we need to insert a separator in front. 210 bool leading_separator = !index; 211 items.push_back(new ChromeLauncherAppMenuItemTab( 212 title, &app_icon, web_contents, leading_separator)); 213 } 214 } 215 } 216 // If only windowed applications are open, we return an empty list to 217 // enforce the creation of a new browser. 218 if (!found_tabbed_browser) 219 items.clear(); 220 return items.Pass(); 221} 222 223bool BrowserShortcutLauncherItemController::ItemSelected( 224 const ui::Event& event) { 225 if (event.flags() & ui::EF_CONTROL_DOWN) { 226 launcher_controller()->CreateNewWindow(); 227 return true; 228 } 229 230 // In case of a keyboard event, we were called by a hotkey. In that case we 231 // activate the next item in line if an item of our list is already active. 232 if (event.type() & ui::ET_KEY_RELEASED) { 233 ActivateOrAdvanceToNextBrowser(); 234 return false; 235 } 236 237 return Activate(ash::LAUNCH_FROM_UNKNOWN); 238} 239 240base::string16 BrowserShortcutLauncherItemController::GetTitle() { 241 return l10n_util::GetStringUTF16(IDS_PRODUCT_NAME); 242} 243 244ui::MenuModel* BrowserShortcutLauncherItemController::CreateContextMenu( 245 aura::Window* root_window) { 246 ash::ShelfItem item = 247 *(launcher_controller()->model()->ItemByID(shelf_id())); 248 return new LauncherContextMenu(launcher_controller(), &item, root_window); 249} 250 251ash::ShelfMenuModel* 252BrowserShortcutLauncherItemController::CreateApplicationMenu(int event_flags) { 253 return new LauncherApplicationMenuItemModel(GetApplicationList(event_flags)); 254} 255 256bool BrowserShortcutLauncherItemController::IsDraggable() { 257 return launcher_controller()->CanPin() ? true : false; 258} 259 260bool BrowserShortcutLauncherItemController::ShouldShowTooltip() { 261 return true; 262} 263 264gfx::Image BrowserShortcutLauncherItemController::GetBrowserListIcon( 265 content::WebContents* web_contents) const { 266 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 267 return rb.GetImageNamed(IsIncognito(web_contents) ? 268 IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER : 269 IDR_ASH_SHELF_LIST_BROWSER); 270} 271 272base::string16 BrowserShortcutLauncherItemController::GetBrowserListTitle( 273 content::WebContents* web_contents) const { 274 base::string16 title = web_contents->GetTitle(); 275 if (!title.empty()) 276 return title; 277 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE); 278} 279 280bool BrowserShortcutLauncherItemController::IsIncognito( 281 content::WebContents* web_contents) const { 282 const Profile* profile = 283 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 284 return profile->IsOffTheRecord() && !profile->IsGuestSession(); 285} 286 287void BrowserShortcutLauncherItemController::ActivateOrAdvanceToNextBrowser() { 288 // Create a list of all suitable running browsers. 289 std::vector<Browser*> items; 290 // We use the list in the order of how the browsers got created - not the LRU 291 // order. 292 const BrowserList* ash_browser_list = 293 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH); 294 for (BrowserList::const_iterator it = 295 ash_browser_list->begin(); 296 it != ash_browser_list->end(); ++it) { 297 if (IsBrowserRepresentedInBrowserList(*it)) 298 items.push_back(*it); 299 } 300 // If there are no suitable browsers we create a new one. 301 if (items.empty()) { 302 launcher_controller()->CreateNewWindow(); 303 return; 304 } 305 Browser* browser = chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow()); 306 if (items.size() == 1) { 307 // If there is only one suitable browser, we can either activate it, or 308 // bounce it (if it is already active). 309 if (browser == items[0]) { 310 AnimateWindow(browser->window()->GetNativeWindow(), 311 wm::WINDOW_ANIMATION_TYPE_BOUNCE); 312 return; 313 } 314 browser = items[0]; 315 } else { 316 // If there is more then one suitable browser, we advance to the next if 317 // |browser| is already active - or - check the last used browser if it can 318 // be used. 319 std::vector<Browser*>::iterator i = 320 std::find(items.begin(), items.end(), browser); 321 if (i != items.end()) { 322 browser = (++i == items.end()) ? items[0] : *i; 323 } else { 324 browser = chrome::FindTabbedBrowser(launcher_controller()->profile(), 325 true, 326 chrome::HOST_DESKTOP_TYPE_ASH); 327 if (!browser || 328 !IsBrowserRepresentedInBrowserList(browser)) 329 browser = items[0]; 330 } 331 } 332 DCHECK(browser); 333 browser->window()->Show(); 334 browser->window()->Activate(); 335} 336 337bool BrowserShortcutLauncherItemController::IsBrowserRepresentedInBrowserList( 338 Browser* browser) { 339 // Only Ash desktop browser windows for the active user are represented. 340 if (!browser || 341 !launcher_controller()->IsBrowserFromActiveUser(browser) || 342 browser->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH) 343 return false; 344 345 // v1 App popup windows with a valid app id have their own icon. 346 if (browser->is_app() && 347 browser->is_type_popup() && 348 launcher_controller()->GetShelfIDForAppID( 349 web_app::GetExtensionIdFromApplicationName(browser->app_name())) > 0) 350 return false; 351 352 // Stand-alone chrome:// windows (e.g. settings) have their own icon. 353 if (chrome::IsTrustedPopupWindowWithScheme(browser, content::kChromeUIScheme)) 354 return false; 355 356 // Tabbed browser and other popup windows are all represented. 357 return true; 358} 359