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.
5 #include "chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.h"
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"
44 bool IsSettingsBrowser(Browser
* browser
) {
45 // Normally this test is sufficient. TODO(stevenjb): Replace this with a
46 // better mechanism (Settings WebUI or Browser type).
47 if (chrome::IsTrustedPopupWindowWithScheme(browser
, content::kChromeUIScheme
))
49 // If a settings window navigates away from a kChromeUIScheme (e.g. after a
50 // crash), the above may not be true, so also test against the known list
51 // of settings browsers (which will not be valid during chrome::Navigate
52 // which is why we still need the above test).
53 if (chrome::SettingsWindowManager::GetInstance()->IsSettingsBrowser(browser
))
60 BrowserShortcutLauncherItemController::BrowserShortcutLauncherItemController(
61 ChromeLauncherController
* launcher_controller
)
62 : LauncherItemController(TYPE_SHORTCUT
,
63 extension_misc::kChromeAppId
,
64 launcher_controller
) {
67 BrowserShortcutLauncherItemController::
68 ~BrowserShortcutLauncherItemController() {
71 void BrowserShortcutLauncherItemController::UpdateBrowserItemState() {
72 // The shell will not be available for win7_aura unittests like
73 // ChromeLauncherControllerTest.BrowserMenuGeneration.
74 if (!ash::Shell::HasInstance())
77 ash::ShelfModel
* model
= launcher_controller()->model();
79 // Determine the new browser's active state and change if necessary.
80 int browser_index
= model
->GetItemIndexForType(ash::TYPE_BROWSER_SHORTCUT
);
81 DCHECK_GE(browser_index
, 0);
82 ash::ShelfItem browser_item
= model
->items()[browser_index
];
83 ash::ShelfItemStatus browser_status
= ash::STATUS_CLOSED
;
85 aura::Window
* window
= ash::wm::GetActiveWindow();
87 // Check if the active browser / tab is a browser which is not an app,
88 // a windowed app, a popup or any other item which is not a browser of
90 Browser
* browser
= chrome::FindBrowserWithWindow(window
);
91 if (IsBrowserRepresentedInBrowserList(browser
)) {
92 browser_status
= ash::STATUS_ACTIVE
;
93 // If an app that has item is running in active WebContents, browser item
94 // status cannot be active.
95 content::WebContents
* contents
=
96 browser
->tab_strip_model()->GetActiveWebContents();
98 (launcher_controller()->GetShelfIDForWebContents(contents
) !=
100 browser_status
= ash::STATUS_RUNNING
;
104 if (browser_status
== ash::STATUS_CLOSED
) {
105 const BrowserList
* ash_browser_list
=
106 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
107 for (BrowserList::const_reverse_iterator it
=
108 ash_browser_list
->begin_last_active();
109 it
!= ash_browser_list
->end_last_active() &&
110 browser_status
== ash::STATUS_CLOSED
; ++it
) {
111 if (IsBrowserRepresentedInBrowserList(*it
))
112 browser_status
= ash::STATUS_RUNNING
;
116 if (browser_status
!= browser_item
.status
) {
117 browser_item
.status
= browser_status
;
118 model
->Set(browser_index
, browser_item
);
122 void BrowserShortcutLauncherItemController::SetShelfIDForBrowserWindowContents(
124 content::WebContents
* web_contents
) {
125 // We need to call SetShelfIDForWindow for V1 applications since they are
126 // content which might change and as such change the application type.
128 !launcher_controller()->IsBrowserFromActiveUser(browser
) ||
129 browser
->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH
||
130 IsSettingsBrowser(browser
))
133 ash::SetShelfIDForWindow(
134 launcher_controller()->GetShelfIDForWebContents(web_contents
),
135 browser
->window()->GetNativeWindow());
138 bool BrowserShortcutLauncherItemController::IsOpen() const {
139 const BrowserList
* ash_browser_list
=
140 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
141 for (BrowserList::const_iterator it
= ash_browser_list
->begin();
142 it
!= ash_browser_list
->end(); ++it
) {
143 if (launcher_controller()->IsBrowserFromActiveUser(*it
))
149 bool BrowserShortcutLauncherItemController::IsVisible() const {
150 Browser
* last_browser
= chrome::FindTabbedBrowser(
151 launcher_controller()->profile(),
153 chrome::HOST_DESKTOP_TYPE_ASH
);
159 aura::Window
* window
= last_browser
->window()->GetNativeWindow();
160 return ash::wm::IsActiveWindow(window
);
163 void BrowserShortcutLauncherItemController::Launch(ash::LaunchSource source
,
167 ash::ShelfItemDelegate::PerformedAction
168 BrowserShortcutLauncherItemController::Activate(ash::LaunchSource source
) {
169 Browser
* last_browser
= chrome::FindTabbedBrowser(
170 launcher_controller()->profile(),
172 chrome::HOST_DESKTOP_TYPE_ASH
);
175 launcher_controller()->CreateNewWindow();
176 return kNewWindowCreated
;
179 return launcher_controller()->ActivateWindowOrMinimizeIfActive(
180 last_browser
->window(), GetApplicationList(0).size() == 2);
183 void BrowserShortcutLauncherItemController::Close() {
186 ChromeLauncherAppMenuItems
187 BrowserShortcutLauncherItemController::GetApplicationList(int event_flags
) {
188 ChromeLauncherAppMenuItems items
;
189 bool found_tabbed_browser
= false;
190 // Add the application name to the menu.
191 items
.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL
, false));
192 const BrowserList
* ash_browser_list
=
193 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
194 for (BrowserList::const_iterator it
= ash_browser_list
->begin();
195 it
!= ash_browser_list
->end(); ++it
) {
196 Browser
* browser
= *it
;
197 // Make sure that the browser is from the current user, has a proper window,
198 // and the window was already shown.
199 if (!launcher_controller()->IsBrowserFromActiveUser(browser
))
201 if (!(browser
->window() && browser
->window()->GetNativeWindow()))
203 if (!(browser
->window()->GetNativeWindow()->IsVisible() ||
204 browser
->window()->IsMinimized())) {
207 if (browser
->is_type_tabbed())
208 found_tabbed_browser
= true;
209 else if (!IsBrowserRepresentedInBrowserList(browser
))
211 TabStripModel
* tab_strip
= browser
->tab_strip_model();
212 if (tab_strip
->active_index() == -1)
214 if (!(event_flags
& ui::EF_SHIFT_DOWN
)) {
215 content::WebContents
* web_contents
=
216 tab_strip
->GetWebContentsAt(tab_strip
->active_index());
217 gfx::Image app_icon
= GetBrowserListIcon(web_contents
);
218 base::string16 title
= GetBrowserListTitle(web_contents
);
219 items
.push_back(new ChromeLauncherAppMenuItemBrowser(
220 title
, &app_icon
, browser
, items
.size() == 1));
222 for (int index
= 0; index
< tab_strip
->count(); ++index
) {
223 content::WebContents
* web_contents
=
224 tab_strip
->GetWebContentsAt(index
);
225 gfx::Image app_icon
=
226 launcher_controller()->GetAppListIcon(web_contents
);
227 base::string16 title
=
228 launcher_controller()->GetAppListTitle(web_contents
);
229 // Check if we need to insert a separator in front.
230 bool leading_separator
= !index
;
231 items
.push_back(new ChromeLauncherAppMenuItemTab(
232 title
, &app_icon
, web_contents
, leading_separator
));
236 // If only windowed applications are open, we return an empty list to
237 // enforce the creation of a new browser.
238 if (!found_tabbed_browser
)
243 ash::ShelfItemDelegate::PerformedAction
244 BrowserShortcutLauncherItemController::ItemSelected(const ui::Event
& event
) {
245 if (event
.flags() & ui::EF_CONTROL_DOWN
) {
246 launcher_controller()->CreateNewWindow();
247 return kNewWindowCreated
;
250 // In case of a keyboard event, we were called by a hotkey. In that case we
251 // activate the next item in line if an item of our list is already active.
252 if (event
.type() & ui::ET_KEY_RELEASED
) {
253 return ActivateOrAdvanceToNextBrowser();
256 return Activate(ash::LAUNCH_FROM_UNKNOWN
);
259 base::string16
BrowserShortcutLauncherItemController::GetTitle() {
260 return l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
);
263 ui::MenuModel
* BrowserShortcutLauncherItemController::CreateContextMenu(
264 aura::Window
* root_window
) {
265 ash::ShelfItem item
=
266 *(launcher_controller()->model()->ItemByID(shelf_id()));
267 return new LauncherContextMenu(launcher_controller(), &item
, root_window
);
271 BrowserShortcutLauncherItemController::CreateApplicationMenu(int event_flags
) {
272 return new LauncherApplicationMenuItemModel(GetApplicationList(event_flags
));
275 bool BrowserShortcutLauncherItemController::IsDraggable() {
276 return launcher_controller()->CanPin() ? true : false;
279 bool BrowserShortcutLauncherItemController::ShouldShowTooltip() {
283 gfx::Image
BrowserShortcutLauncherItemController::GetBrowserListIcon(
284 content::WebContents
* web_contents
) const {
285 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
286 return rb
.GetImageNamed(IsIncognito(web_contents
) ?
287 IDR_ASH_SHELF_LIST_INCOGNITO_BROWSER
:
288 IDR_ASH_SHELF_LIST_BROWSER
);
291 base::string16
BrowserShortcutLauncherItemController::GetBrowserListTitle(
292 content::WebContents
* web_contents
) const {
293 base::string16 title
= web_contents
->GetTitle();
296 return l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE
);
299 bool BrowserShortcutLauncherItemController::IsIncognito(
300 content::WebContents
* web_contents
) const {
301 const Profile
* profile
=
302 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
303 return profile
->IsOffTheRecord() && !profile
->IsGuestSession();
306 ash::ShelfItemDelegate::PerformedAction
307 BrowserShortcutLauncherItemController::ActivateOrAdvanceToNextBrowser() {
308 // Create a list of all suitable running browsers.
309 std::vector
<Browser
*> items
;
310 // We use the list in the order of how the browsers got created - not the LRU
312 const BrowserList
* ash_browser_list
=
313 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH
);
314 for (BrowserList::const_iterator it
=
315 ash_browser_list
->begin();
316 it
!= ash_browser_list
->end(); ++it
) {
317 if (IsBrowserRepresentedInBrowserList(*it
))
318 items
.push_back(*it
);
320 // If there are no suitable browsers we create a new one.
322 launcher_controller()->CreateNewWindow();
323 return kNewWindowCreated
;
325 Browser
* browser
= chrome::FindBrowserWithWindow(ash::wm::GetActiveWindow());
326 if (items
.size() == 1) {
327 // If there is only one suitable browser, we can either activate it, or
328 // bounce it (if it is already active).
329 if (browser
== items
[0]) {
330 AnimateWindow(browser
->window()->GetNativeWindow(),
331 wm::WINDOW_ANIMATION_TYPE_BOUNCE
);
336 // If there is more then one suitable browser, we advance to the next if
337 // |browser| is already active - or - check the last used browser if it can
339 std::vector
<Browser
*>::iterator i
=
340 std::find(items
.begin(), items
.end(), browser
);
341 if (i
!= items
.end()) {
342 browser
= (++i
== items
.end()) ? items
[0] : *i
;
344 browser
= chrome::FindTabbedBrowser(launcher_controller()->profile(),
346 chrome::HOST_DESKTOP_TYPE_ASH
);
348 !IsBrowserRepresentedInBrowserList(browser
))
353 browser
->window()->Show();
354 browser
->window()->Activate();
355 return kExistingWindowActivated
;
358 bool BrowserShortcutLauncherItemController::IsBrowserRepresentedInBrowserList(
360 // Only Ash desktop browser windows for the active user are represented.
362 !launcher_controller()->IsBrowserFromActiveUser(browser
) ||
363 browser
->host_desktop_type() != chrome::HOST_DESKTOP_TYPE_ASH
)
366 // v1 App popup windows with a valid app id have their own icon.
367 if (browser
->is_app() &&
368 browser
->is_type_popup() &&
369 launcher_controller()->GetShelfIDForAppID(
370 web_app::GetExtensionIdFromApplicationName(browser
->app_name())) > 0)
373 // Settings browsers have their own icon.
374 if (IsSettingsBrowser(browser
))
377 // Tabbed browser and other popup windows are all represented.