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)
7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ash/shelf/shelf_model.h"
84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ash/shell.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/wm/window_util.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h"
144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_list.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/web_applications/web_app.h"
25868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/browser/navigation_entry.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/web_contents.h"
2803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "extensions/browser/app_window/native_app_window.h"
29116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "extensions/browser/extension_registry.h"
30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/browser/process_manager.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/aura/window.h"
32d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h"
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/wm/core/window_animations.h"
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Extension;
36116680a4aac90f2aa7413d9095a592090648e557Ben Murdochusing extensions::ExtensionRegistry;
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace {
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// The time delta between clicks in which clicks to launch V2 apps are ignored.
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst int kClickSuppressionInMS = 1000;
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Check if a browser can be used for activation. This addresses a special use
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// case in the M31 multi profile mode where a user activates a V1 app which only
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// exists yet on another users desktop, but he expects to get only his own app
46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// items and not the ones from other users through activation.
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// TODO(skuhne): Remove this function and replace the call with
48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// launcher_controller()->IsBrowserFromActiveUser(browser) once this experiment
49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// goes away.
50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool CanBrowserBeUsedForDirectActivation(Browser* browser,
51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                         ChromeLauncherController* launcher) {
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF)
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return multi_user_util::IsProfileFromActiveUser(browser->profile());
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Item controller for an app shortcut. Shortcuts track app and launcher ids,
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// but do not have any associated windows (opening a shortcut will replace the
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// item with the appropriate LauncherItemController type).
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::AppShortcutLauncherItemController(
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& app_id,
653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    ChromeLauncherController* controller)
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : LauncherItemController(TYPE_SHORTCUT, app_id, controller),
673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      chrome_launcher_controller_(controller) {
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To detect V1 applications we use their domain and match them against the
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // used URL. This will also work with applications like Google Drive.
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id);
72868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Some unit tests have no real extension.
73868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (extension) {
74868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    set_refocus_url(GURL(
75868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        extensions::AppLaunchInfo::GetLaunchWebURL(extension).spec() + "*"));
76868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::~AppShortcutLauncherItemController() {
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::IsOpen() const {
833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return !chrome_launcher_controller_->
843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      GetV1ApplicationsFromAppId(app_id()).empty();
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::IsVisible() const {
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Return true if any browser window associated with the app is visible.
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      chrome_launcher_controller_->GetV1ApplicationsFromAppId(app_id());
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (browser && browser->window()->GetNativeWindow()->IsVisible())
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return true;
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return false;
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void AppShortcutLauncherItemController::Launch(ash::LaunchSource source,
10068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                                               int event_flags) {
10168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  launcher_controller()->LaunchApp(app_id(), source, event_flags);
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool AppShortcutLauncherItemController::Activate(ash::LaunchSource source) {
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  content::WebContents* content = GetLRUApplication();
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!content) {
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (IsV2App()) {
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // Ideally we come here only once. After that ShellLauncherItemController
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // will take over when the shell window gets opened. However there are
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // apps which take a lot of time for pre-processing (like the files app)
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // before they open a window. Since there is currently no other way to
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // detect if an app was started we suppress any further clicks within a
113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // special time out.
114eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if (!AllowNextLaunchAttempt())
115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return false;
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
11768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    Launch(source, ui::EF_NONE);
118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ActivateContent(content);
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return false;
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Close() {
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Close all running 'programs' of this type.
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
1273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      launcher_controller()->GetV1ApplicationsFromAppId(app_id());
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
1301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!browser || !launcher_controller()->IsBrowserFromActiveUser(browser))
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int index = tab_strip->GetIndexOfWebContents(content[i]);
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(index != TabStripModel::kNoTab);
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_strip->CloseWebContentsAt(index, TabStripModel::CLOSE_NONE);
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeLauncherAppMenuItems
14090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AppShortcutLauncherItemController::GetApplicationList(int event_flags) {
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ChromeLauncherAppMenuItems items;
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Add the application name to the menu.
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false));
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> content_list = GetRunningApplications();
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content_list.size(); i++) {
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents = content_list[i];
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Get the icon.
1503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    gfx::Image app_icon = launcher_controller()->GetAppListIcon(web_contents);
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 title = launcher_controller()->GetAppListTitle(web_contents);
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    items.push_back(new ChromeLauncherAppMenuItemTab(
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        title, &app_icon, web_contents, i == 0));
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items.Pass();
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::vector<content::WebContents*>
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::GetRunningApplications() {
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> items;
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // It is possible to come here While an extension gets loaded.
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return items;
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_iterator it = ash_browser_list->begin();
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end(); ++it) {
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
1821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!launcher_controller()->IsBrowserFromActiveUser(browser))
1831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      continue;
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (WebContentMatchesApp(
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              extension, refocus_pattern, web_contents, browser))
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        items.push_back(web_contents);
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items;
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool AppShortcutLauncherItemController::ItemSelected(const ui::Event& event) {
1964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // In case of a keyboard event, we were called by a hotkey. In that case we
1974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // activate the next item in line if an item of our list is already active.
1984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (event.type() == ui::ET_KEY_RELEASED) {
1994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (AdvanceToNextApp())
200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      return false;
2014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
202f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return Activate(ash::LAUNCH_FROM_UNKNOWN);
2034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)base::string16 AppShortcutLauncherItemController::GetTitle() {
2064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return GetAppTitle();
2074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)ui::MenuModel* AppShortcutLauncherItemController::CreateContextMenu(
2101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    aura::Window* root_window) {
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ash::ShelfItem item =
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      *(launcher_controller()->model()->ItemByID(shelf_id()));
2134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return new LauncherContextMenu(launcher_controller(), &item, root_window);
2144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
216a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ash::ShelfMenuModel* AppShortcutLauncherItemController::CreateApplicationMenu(
217a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    int event_flags) {
2184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return new LauncherApplicationMenuItemModel(GetApplicationList(event_flags));
2194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool AppShortcutLauncherItemController::IsDraggable() {
2224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return true;
2234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool AppShortcutLauncherItemController::ShouldShowTooltip() {
2264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return true;
2274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)content::WebContents* AppShortcutLauncherItemController::GetLRUApplication() {
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We may get here while the extension is loading (and NULL).
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_reverse_iterator
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it = ash_browser_list->begin_last_active();
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end_last_active(); ++it) {
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (!CanBrowserBeUsedForDirectActivation(browser, launcher_controller()))
2521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      continue;
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // We start to enumerate from the active index.
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int active_index = tab_strip->active_index();
256c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (index + active_index) % tab_strip->count());
259f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (WebContentMatchesApp(
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              extension, refocus_pattern, web_contents, browser))
261bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch        return web_contents;
262bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    }
263bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  }
264bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // Coming here our application was not in the LRU list. This could have
265bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // happened because it did never get activated yet. So check the browser list
266bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // as well.
267bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  for (BrowserList::const_iterator it = ash_browser_list->begin();
268bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch       it != ash_browser_list->end(); ++it) {
269bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    Browser* browser = *it;
270f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (!CanBrowserBeUsedForDirectActivation(browser, launcher_controller()))
2711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      continue;
272bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    TabStripModel* tab_strip = browser->tab_strip_model();
273bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    for (int index = 0; index < tab_strip->count(); index++) {
274bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
275f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (WebContentMatchesApp(
276a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              extension, refocus_pattern, web_contents, browser))
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return web_contents;
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool AppShortcutLauncherItemController::WebContentMatchesApp(
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const extensions::Extension* extension,
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const URLPattern& refocus_pattern,
286f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    content::WebContents* web_contents,
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    Browser* browser) {
288a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // If the browser is an app window and the app name matches the extension.
289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (browser->is_app()) {
290116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const extensions::Extension* browser_extension =
291116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        ExtensionRegistry::Get(browser->profile())->GetExtensionById(
292116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            web_app::GetExtensionIdFromApplicationName(browser->app_name()),
293116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            ExtensionRegistry::EVERYTHING);
294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return browser_extension == extension;
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
296a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // There are three ways to identify the association of a URL with this
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // extension:
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The refocus pattern is matched (needed for apps like drive).
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The extension's origin + extent gets matched.
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The launcher controller knows that the tab got created for this app.
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const GURL tab_url = web_contents->GetURL();
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ((!refocus_pattern.match_all_urls() &&
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           refocus_pattern.MatchesURL(tab_url)) ||
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (extension->OverlapsWithOrigin(tab_url) &&
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           extension->web_extent().MatchesURL(tab_url)) ||
3073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          launcher_controller()->IsWebContentHandledByApplication(web_contents,
3083551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                                                  app_id()));
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
310c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
311c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void AppShortcutLauncherItemController::ActivateContent(
312c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* content) {
313c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWebContents(content);
314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  TabStripModel* tab_strip = browser->tab_strip_model();
315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int index = tab_strip->GetIndexOfWebContents(content);
316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK_NE(TabStripModel::kNoTab, index);
317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int old_index = tab_strip->active_index();
319c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (index != old_index)
320c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    tab_strip->ActivateTabAt(index, false);
3213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  launcher_controller()->ActivateWindowOrMinimizeIfActive(
3223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      browser->window(),
323c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      index == old_index && GetRunningApplications().size() == 1);
324c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
325c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
326c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool AppShortcutLauncherItemController::AdvanceToNextApp() {
327c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> items = GetRunningApplications();
328c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (items.size() >= 1) {
329c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWindow(
330c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ash::wm::GetActiveWindow());
331c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (browser) {
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      TabStripModel* tab_strip = browser->tab_strip_model();
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::WebContents* active = tab_strip->GetWebContentsAt(
334c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          tab_strip->active_index());
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::vector<content::WebContents*>::const_iterator i(
336c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          std::find(items.begin(), items.end(), active));
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (i != items.end()) {
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (items.size() == 1) {
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // If there is only a single item available, we animate it upon key
340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // action.
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          AnimateWindow(browser->window()->GetNativeWindow(),
342a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              wm::WINDOW_ANIMATION_TYPE_BOUNCE);
343c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        } else {
344c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          int index = (static_cast<int>(i - items.begin()) + 1) % items.size();
345c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          ActivateContent(items[index]);
346c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
347c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return true;
348c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
349c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
350c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
351c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return false;
352c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
353868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
354eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool AppShortcutLauncherItemController::IsV2App() {
3553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  const Extension* extension =
3563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
357eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return extension && extension->is_platform_app();
358eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
359eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
360eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool AppShortcutLauncherItemController::AllowNextLaunchAttempt() {
361eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (last_launch_attempt_.is_null() ||
362eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      last_launch_attempt_ + base::TimeDelta::FromMilliseconds(
363eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          kClickSuppressionInMS) < base::Time::Now()) {
364eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    last_launch_attempt_ = base::Time::Now();
365eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return true;
366eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
367eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return false;
368868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
369