app_shortcut_launcher_item_controller.cc revision bb1529ce867d8845a77ec7cdf3e3003ef1771a40
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/chrome_launcher_controller_per_app.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_list.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
23868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/web_contents.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/aura/window.h"
26b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "ui/base/events/event.h"
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/corewm/window_animations.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Extension;
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace {
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// The time delta between clicks in which clicks to launch V2 apps are ignored.
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst int kClickSuppressionInMS = 1000;
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Item controller for an app shortcut. Shortcuts track app and launcher ids,
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// but do not have any associated windows (opening a shortcut will replace the
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// item with the appropriate LauncherItemController type).
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::AppShortcutLauncherItemController(
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& app_id,
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ChromeLauncherControllerPerApp* controller)
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : LauncherItemController(TYPE_SHORTCUT, app_id, controller),
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_controller_(controller) {
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To detect V1 applications we use their domain and match them against the
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // used URL. This will also work with applications like Google Drive.
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id);
50868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Some unit tests have no real extension.
51868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (extension) {
52868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    set_refocus_url(GURL(
53868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        extensions::AppLaunchInfo::GetLaunchWebURL(extension).spec() + "*"));
54868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::~AppShortcutLauncherItemController() {
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)string16 AppShortcutLauncherItemController::GetTitle() {
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetAppTitle();
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::HasWindow(aura::Window* window) const {
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_controller_->GetV1ApplicationsFromAppId(app_id());
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (browser && browser->window()->GetNativeWindow() == window)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::IsOpen() const {
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !app_controller_->GetV1ApplicationsFromAppId(app_id()).empty();
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::IsVisible() const {
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Return true if any browser window associated with the app is visible.
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_controller_->GetV1ApplicationsFromAppId(app_id());
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (browser && browser->window()->GetNativeWindow()->IsVisible())
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Launch(int event_flags) {
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  app_controller_->LaunchApp(app_id(), event_flags);
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Activate() {
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::WebContents* content = GetLRUApplication();
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!content) {
98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (IsV2App()) {
99eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // Ideally we come here only once. After that ShellLauncherItemController
100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // will take over when the shell window gets opened. However there are
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // apps which take a lot of time for pre-processing (like the files app)
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // before they open a window. Since there is currently no other way to
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // detect if an app was started we suppress any further clicks within a
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // special time out.
105eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if (!AllowNextLaunchAttempt())
106eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        return;
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Launch(ui::EF_NONE);
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ActivateContent(content);
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Close() {
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Close all running 'programs' of this type.
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_controller_->GetV1ApplicationsFromAppId(app_id());
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!browser)
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int index = tab_strip->GetIndexOfWebContents(content[i]);
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(index != TabStripModel::kNoTab);
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_strip->CloseWebContentsAt(index, TabStripModel::CLOSE_NONE);
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Clicked(const ui::Event& event) {
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // In case of a keyboard event, we were called by a hotkey. In that case we
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // activate the next item in line if an item of our list is already active.
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (event.type() == ui::ET_KEY_RELEASED) {
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (AdvanceToNextApp())
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return;
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Activate();
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::OnRemoved() {
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // AppShortcutLauncherItemController is unowned; delete on removal.
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete this;
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::LauncherItemChanged(
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int model_index,
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ash::LauncherItem& old_item) {
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeLauncherAppMenuItems
15090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AppShortcutLauncherItemController::GetApplicationList(int event_flags) {
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ChromeLauncherAppMenuItems items;
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Add the application name to the menu.
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false));
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> content_list = GetRunningApplications();
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content_list.size(); i++) {
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents = content_list[i];
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Get the icon.
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Image app_icon = app_controller_->GetAppListIcon(web_contents);
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    string16 title = app_controller_->GetAppListTitle(web_contents);
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    items.push_back(new ChromeLauncherAppMenuItemTab(
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        title, &app_icon, web_contents, i == 0));
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items.Pass();
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::vector<content::WebContents*>
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::GetRunningApplications() {
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> items;
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // It is possible to come here While an extension gets loaded.
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return items;
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_iterator it = ash_browser_list->begin();
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end(); ++it) {
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        items.push_back(web_contents);
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items;
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)content::WebContents* AppShortcutLauncherItemController::GetLRUApplication() {
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We may get here while the extension is loading (and NULL).
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_reverse_iterator
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it = ash_browser_list->begin_last_active();
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end_last_active(); ++it) {
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // We start to enumerate from the active index.
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int active_index = tab_strip->active_index();
227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (index + active_index) % tab_strip->count());
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
231bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch        return web_contents;
232bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    }
233bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  }
234bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // Coming here our application was not in the LRU list. This could have
235bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // happened because it did never get activated yet. So check the browser list
236bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // as well.
237bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  for (BrowserList::const_iterator it = ash_browser_list->begin();
238bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch       it != ash_browser_list->end(); ++it) {
239bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    Browser* browser = *it;
240bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    TabStripModel* tab_strip = browser->tab_strip_model();
241bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    for (int index = 0; index < tab_strip->count(); index++) {
242bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
243bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return web_contents;
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::WebContentMatchesApp(
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const extensions::Extension* extension,
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const URLPattern& refocus_pattern,
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents) {
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const GURL tab_url = web_contents->GetURL();
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // There are three ways to identify the association of a URL with this
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // extension:
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The refocus pattern is matched (needed for apps like drive).
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The extension's origin + extent gets matched.
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The launcher controller knows that the tab got created for this app.
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ((!refocus_pattern.match_all_urls() &&
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           refocus_pattern.MatchesURL(tab_url)) ||
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (extension->OverlapsWithOrigin(tab_url) &&
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           extension->web_extent().MatchesURL(tab_url)) ||
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          launcher_controller()->GetPerAppInterface()->
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             IsWebContentHandledByApplication(web_contents, app_id()));
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
267c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
268c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void AppShortcutLauncherItemController::ActivateContent(
269c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* content) {
270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWebContents(content);
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  TabStripModel* tab_strip = browser->tab_strip_model();
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int index = tab_strip->GetIndexOfWebContents(content);
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK_NE(TabStripModel::kNoTab, index);
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int old_index = tab_strip->active_index();
276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (index != old_index)
277c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    tab_strip->ActivateTabAt(index, false);
278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  app_controller_->ActivateWindowOrMinimizeIfActive(browser->window(),
279c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      index == old_index && GetRunningApplications().size() == 1);
280c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
281c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
282c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool AppShortcutLauncherItemController::AdvanceToNextApp() {
283c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> items = GetRunningApplications();
284c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (items.size() >= 1) {
285c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWindow(
286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ash::wm::GetActiveWindow());
287c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (browser) {
288c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      TabStripModel* tab_strip = browser->tab_strip_model();
289c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::WebContents* active = tab_strip->GetWebContentsAt(
290c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          tab_strip->active_index());
291c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::vector<content::WebContents*>::const_iterator i(
292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          std::find(items.begin(), items.end(), active));
293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (i != items.end()) {
294c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (items.size() == 1) {
295c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // If there is only a single item available, we animate it upon key
296c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // action.
297c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          AnimateWindow(browser->window()->GetNativeWindow(),
298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE);
299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        } else {
300c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          int index = (static_cast<int>(i - items.begin()) + 1) % items.size();
301c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          ActivateContent(items[index]);
302c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return true;
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
305c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
306c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
307c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return false;
308c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
309868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
310eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool AppShortcutLauncherItemController::IsV2App() {
311eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const Extension* extension = app_controller_->GetExtensionForAppID(app_id());
312eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return extension && extension->is_platform_app();
313eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
314eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
315eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool AppShortcutLauncherItemController::AllowNextLaunchAttempt() {
316eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (last_launch_attempt_.is_null() ||
317eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      last_launch_attempt_ + base::TimeDelta::FromMilliseconds(
318eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          kClickSuppressionInMS) < base::Time::Now()) {
319eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    last_launch_attempt_ = base::Time::Now();
320eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return true;
321eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
322eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return false;
323868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
324