app_shortcut_launcher_item_controller.cc revision 3551c9c881056c480085172ff9840cab31610854
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch#include "apps/native_app_window.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/wm/window_util.h"
9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/browser/extensions/extension_process_manager.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/favicon/favicon_tab_helper.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_list.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
22868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/web_contents.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/aura/window.h"
25b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "ui/base/events/event.h"
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/corewm/window_animations.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Extension;
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace {
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// The time delta between clicks in which clicks to launch V2 apps are ignored.
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst int kClickSuppressionInMS = 1000;
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Item controller for an app shortcut. Shortcuts track app and launcher ids,
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// but do not have any associated windows (opening a shortcut will replace the
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// item with the appropriate LauncherItemController type).
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::AppShortcutLauncherItemController(
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& app_id,
423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    ChromeLauncherController* controller)
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : LauncherItemController(TYPE_SHORTCUT, app_id, controller),
443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      chrome_launcher_controller_(controller) {
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To detect V1 applications we use their domain and match them against the
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // used URL. This will also work with applications like Google Drive.
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id);
49868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Some unit tests have no real extension.
50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (extension) {
51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    set_refocus_url(GURL(
52868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        extensions::AppLaunchInfo::GetLaunchWebURL(extension).spec() + "*"));
53868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::~AppShortcutLauncherItemController() {
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)string16 AppShortcutLauncherItemController::GetTitle() {
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetAppTitle();
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool AppShortcutLauncherItemController::IsCurrentlyShownInWindow(
643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    aura::Window* window) const {
653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWindow(window);
663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  content::WebContents* active_content_of_window =
673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      browser ? browser->tab_strip_model()->GetActiveWebContents() : NULL;
683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      chrome_launcher_controller_->GetV1ApplicationsFromAppId(app_id());
713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  std::vector<content::WebContents*>::const_iterator iter =
733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      std::find(content.begin(), content.end(), active_content_of_window);
743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return iter != content.end() ? true : false;
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::IsOpen() const {
793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return !chrome_launcher_controller_->
803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      GetV1ApplicationsFromAppId(app_id()).empty();
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::IsVisible() const {
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Return true if any browser window associated with the app is visible.
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      chrome_launcher_controller_->GetV1ApplicationsFromAppId(app_id());
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (browser && browser->window()->GetNativeWindow()->IsVisible())
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Launch(int event_flags) {
963551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  launcher_controller()->LaunchApp(app_id(), event_flags);
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Activate() {
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::WebContents* content = GetLRUApplication();
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!content) {
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (IsV2App()) {
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // Ideally we come here only once. After that ShellLauncherItemController
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // will take over when the shell window gets opened. However there are
105eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // apps which take a lot of time for pre-processing (like the files app)
106eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // before they open a window. Since there is currently no other way to
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // detect if an app was started we suppress any further clicks within a
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // special time out.
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if (!AllowNextLaunchAttempt())
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        return;
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Launch(ui::EF_NONE);
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ActivateContent(content);
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Close() {
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Close all running 'programs' of this type.
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
1213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      launcher_controller()->GetV1ApplicationsFromAppId(app_id());
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!browser)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int index = tab_strip->GetIndexOfWebContents(content[i]);
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(index != TabStripModel::kNoTab);
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_strip->CloseWebContentsAt(index, TabStripModel::CLOSE_NONE);
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Clicked(const ui::Event& event) {
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // In case of a keyboard event, we were called by a hotkey. In that case we
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // activate the next item in line if an item of our list is already active.
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (event.type() == ui::ET_KEY_RELEASED) {
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (AdvanceToNextApp())
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return;
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Activate();
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::OnRemoved() {
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // AppShortcutLauncherItemController is unowned; delete on removal.
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete this;
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::LauncherItemChanged(
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int model_index,
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ash::LauncherItem& old_item) {
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeLauncherAppMenuItems
15490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AppShortcutLauncherItemController::GetApplicationList(int event_flags) {
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ChromeLauncherAppMenuItems items;
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Add the application name to the menu.
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false));
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> content_list = GetRunningApplications();
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content_list.size(); i++) {
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents = content_list[i];
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Get the icon.
1643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    gfx::Image app_icon = launcher_controller()->GetAppListIcon(web_contents);
1653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    string16 title = launcher_controller()->GetAppListTitle(web_contents);
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    items.push_back(new ChromeLauncherAppMenuItemTab(
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        title, &app_icon, web_contents, i == 0));
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items.Pass();
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::vector<content::WebContents*>
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::GetRunningApplications() {
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> items;
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // It is possible to come here While an extension gets loaded.
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return items;
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_iterator it = ash_browser_list->begin();
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end(); ++it) {
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        items.push_back(web_contents);
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items;
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)content::WebContents* AppShortcutLauncherItemController::GetLRUApplication() {
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We may get here while the extension is loading (and NULL).
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_reverse_iterator
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it = ash_browser_list->begin_last_active();
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end_last_active(); ++it) {
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // We start to enumerate from the active index.
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int active_index = tab_strip->active_index();
231c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (index + active_index) % tab_strip->count());
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
235bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch        return web_contents;
236bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    }
237bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  }
238bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // Coming here our application was not in the LRU list. This could have
239bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // happened because it did never get activated yet. So check the browser list
240bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // as well.
241bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  for (BrowserList::const_iterator it = ash_browser_list->begin();
242bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch       it != ash_browser_list->end(); ++it) {
243bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    Browser* browser = *it;
244bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    TabStripModel* tab_strip = browser->tab_strip_model();
245bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    for (int index = 0; index < tab_strip->count(); index++) {
246bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
247bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return web_contents;
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::WebContentMatchesApp(
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const extensions::Extension* extension,
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const URLPattern& refocus_pattern,
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents) {
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const GURL tab_url = web_contents->GetURL();
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // There are three ways to identify the association of a URL with this
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // extension:
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The refocus pattern is matched (needed for apps like drive).
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The extension's origin + extent gets matched.
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The launcher controller knows that the tab got created for this app.
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ((!refocus_pattern.match_all_urls() &&
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           refocus_pattern.MatchesURL(tab_url)) ||
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (extension->OverlapsWithOrigin(tab_url) &&
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           extension->web_extent().MatchesURL(tab_url)) ||
2683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          launcher_controller()->IsWebContentHandledByApplication(web_contents,
2693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                                                  app_id()));
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void AppShortcutLauncherItemController::ActivateContent(
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* content) {
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWebContents(content);
275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  TabStripModel* tab_strip = browser->tab_strip_model();
276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int index = tab_strip->GetIndexOfWebContents(content);
277c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK_NE(TabStripModel::kNoTab, index);
278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
279c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int old_index = tab_strip->active_index();
280c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (index != old_index)
281c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    tab_strip->ActivateTabAt(index, false);
2823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  launcher_controller()->ActivateWindowOrMinimizeIfActive(
2833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      browser->window(),
284c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      index == old_index && GetRunningApplications().size() == 1);
285c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
287c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool AppShortcutLauncherItemController::AdvanceToNextApp() {
288c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> items = GetRunningApplications();
289c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (items.size() >= 1) {
290c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWindow(
291c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ash::wm::GetActiveWindow());
292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (browser) {
293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      TabStripModel* tab_strip = browser->tab_strip_model();
294c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::WebContents* active = tab_strip->GetWebContentsAt(
295c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          tab_strip->active_index());
296c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::vector<content::WebContents*>::const_iterator i(
297c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          std::find(items.begin(), items.end(), active));
298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (i != items.end()) {
299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (items.size() == 1) {
300c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // If there is only a single item available, we animate it upon key
301c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // action.
302c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          AnimateWindow(browser->window()->GetNativeWindow(),
303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE);
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        } else {
305c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          int index = (static_cast<int>(i - items.begin()) + 1) % items.size();
306c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          ActivateContent(items[index]);
307c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
308c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return true;
309c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
310c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
311c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
312c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return false;
313c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
314868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
315eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool AppShortcutLauncherItemController::IsV2App() {
3163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  const Extension* extension =
3173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
318eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return extension && extension->is_platform_app();
319eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
320eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
321eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool AppShortcutLauncherItemController::AllowNextLaunchAttempt() {
322eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (last_launch_attempt_.is_null() ||
323eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      last_launch_attempt_ + base::TimeDelta::FromMilliseconds(
324eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          kClickSuppressionInMS) < base::Time::Now()) {
325eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    last_launch_attempt_ = base::Time::Now();
326eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return true;
327eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
328eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return false;
329868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
330