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