app_shortcut_launcher_item_controller.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/wm/window_util.h"
8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/browser/extensions/extension_process_manager.h"
9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/favicon/favicon_tab_helper.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller_per_app.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/extensions/native_app_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)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Item controller for an app shortcut. Shortcuts track app and launcher ids,
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// but do not have any associated windows (opening a shortcut will replace the
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// item with the appropriate LauncherItemController type).
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::AppShortcutLauncherItemController(
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& app_id,
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ChromeLauncherControllerPerApp* controller)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : LauncherItemController(TYPE_SHORTCUT, app_id, controller),
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_controller_(controller) {
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To detect V1 applications we use their domain and match them against the
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // used URL. This will also work with applications like Google Drive.
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id);
43868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Some unit tests have no real extension.
44868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (extension) {
45868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    set_refocus_url(GURL(
46868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        extensions::AppLaunchInfo::GetLaunchWebURL(extension).spec() + "*"));
47868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::~AppShortcutLauncherItemController() {
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)string16 AppShortcutLauncherItemController::GetTitle() {
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetAppTitle();
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::HasWindow(aura::Window* window) const {
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_controller_->GetV1ApplicationsFromAppId(app_id());
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (browser && browser->window()->GetNativeWindow() == window)
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::IsOpen() const {
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return !app_controller_->GetV1ApplicationsFromAppId(app_id()).empty();
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::IsVisible() const {
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Return true if any browser window associated with the app is visible.
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_controller_->GetV1ApplicationsFromAppId(app_id());
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (browser && browser->window()->GetNativeWindow()->IsVisible())
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Launch(int event_flags) {
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  app_controller_->LaunchApp(app_id(), event_flags);
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Activate() {
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::WebContents* content = GetLRUApplication();
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!content) {
91868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // The initial launch of a V2 app will be done by this controller. Starting
92868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // a hosted application takes time and if the user clicks fast, he can come
93868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // here multiple times before the ShellLauncherItemController takes control.
94868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    // To avoid that situation we check for running applications.
95868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (!IsV2AppAndRunning())
96868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      Launch(ui::EF_NONE);
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ActivateContent(content);
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Close() {
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Close all running 'programs' of this type.
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_controller_->GetV1ApplicationsFromAppId(app_id());
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!browser)
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int index = tab_strip->GetIndexOfWebContents(content[i]);
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(index != TabStripModel::kNoTab);
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_strip->CloseWebContentsAt(index, TabStripModel::CLOSE_NONE);
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Clicked(const ui::Event& event) {
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // In case of a keyboard event, we were called by a hotkey. In that case we
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // activate the next item in line if an item of our list is already active.
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (event.type() == ui::ET_KEY_RELEASED) {
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (AdvanceToNextApp())
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return;
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Activate();
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::OnRemoved() {
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // AppShortcutLauncherItemController is unowned; delete on removal.
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete this;
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::LauncherItemChanged(
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int model_index,
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ash::LauncherItem& old_item) {
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeLauncherAppMenuItems
13890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AppShortcutLauncherItemController::GetApplicationList(int event_flags) {
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ChromeLauncherAppMenuItems items;
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Add the application name to the menu.
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false));
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> content_list = GetRunningApplications();
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content_list.size(); i++) {
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents = content_list[i];
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Get the icon.
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Image app_icon = app_controller_->GetAppListIcon(web_contents);
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    string16 title = app_controller_->GetAppListTitle(web_contents);
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    items.push_back(new ChromeLauncherAppMenuItemTab(
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        title, &app_icon, web_contents, i == 0));
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items.Pass();
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::vector<content::WebContents*>
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::GetRunningApplications() {
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> items;
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // It is possible to come here While an extension gets loaded.
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return items;
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_iterator it = ash_browser_list->begin();
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end(); ++it) {
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        items.push_back(web_contents);
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items;
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)content::WebContents* AppShortcutLauncherItemController::GetLRUApplication() {
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We may get here while the extension is loading (and NULL).
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_reverse_iterator
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it = ash_browser_list->begin_last_active();
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end_last_active(); ++it) {
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // We start to enumerate from the active index.
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int active_index = tab_strip->active_index();
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (index + active_index) % tab_strip->count());
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return web_contents;
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::WebContentMatchesApp(
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const extensions::Extension* extension,
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const URLPattern& refocus_pattern,
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents) {
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const GURL tab_url = web_contents->GetURL();
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // There are three ways to identify the association of a URL with this
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // extension:
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The refocus pattern is matched (needed for apps like drive).
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The extension's origin + extent gets matched.
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The launcher controller knows that the tab got created for this app.
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ((!refocus_pattern.match_all_urls() &&
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           refocus_pattern.MatchesURL(tab_url)) ||
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (extension->OverlapsWithOrigin(tab_url) &&
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           extension->web_extent().MatchesURL(tab_url)) ||
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          launcher_controller()->GetPerAppInterface()->
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             IsWebContentHandledByApplication(web_contents, app_id()));
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
242c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
243c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void AppShortcutLauncherItemController::ActivateContent(
244c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* content) {
245c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWebContents(content);
246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  TabStripModel* tab_strip = browser->tab_strip_model();
247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int index = tab_strip->GetIndexOfWebContents(content);
248c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK_NE(TabStripModel::kNoTab, index);
249c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int old_index = tab_strip->active_index();
251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (index != old_index)
252c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    tab_strip->ActivateTabAt(index, false);
253c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  app_controller_->ActivateWindowOrMinimizeIfActive(browser->window(),
254c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      index == old_index && GetRunningApplications().size() == 1);
255c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
256c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
257c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool AppShortcutLauncherItemController::AdvanceToNextApp() {
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> items = GetRunningApplications();
259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (items.size() >= 1) {
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWindow(
261c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ash::wm::GetActiveWindow());
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (browser) {
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      TabStripModel* tab_strip = browser->tab_strip_model();
264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::WebContents* active = tab_strip->GetWebContentsAt(
265c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          tab_strip->active_index());
266c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::vector<content::WebContents*>::const_iterator i(
267c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          std::find(items.begin(), items.end(), active));
268c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (i != items.end()) {
269c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (items.size() == 1) {
270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // If there is only a single item available, we animate it upon key
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // action.
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          AnimateWindow(browser->window()->GetNativeWindow(),
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE);
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        } else {
275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          int index = (static_cast<int>(i - items.begin()) + 1) % items.size();
276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          ActivateContent(items[index]);
277c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return true;
279c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
280c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
281c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
282c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return false;
283c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
284868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
285868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)bool AppShortcutLauncherItemController::IsV2AppAndRunning() {
286868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  const Extension* extension =
287868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      app_controller_->GetExtensionForAppID(app_id());
288868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  extensions::ExtensionSystem* extension_system =
289868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      extensions::ExtensionSystem::Get(app_controller_->profile());
290868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return extension && extension->is_platform_app() && extension_system &&
291868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)         extension_system->process_manager()->GetBackgroundHostForExtension(
292868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)             app_id());
293868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
294