app_shortcut_launcher_item_controller.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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)
74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "apps/ui/native_app_window.h"
84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ash/launcher/launcher_model.h"
94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ash/shell.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/wm/window_util.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/browser/extensions/extension_process_manager.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/favicon/favicon_tab_helper.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h"
184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_list.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_window.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
26868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/web_contents.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/aura/window.h"
29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h"
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "ui/views/corewm/window_animations.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#if defined(OS_CHROMEOS)
334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#endif
354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Extension;
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
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Item controller for an app shortcut. Shortcuts track app and launcher ids,
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// but do not have any associated windows (opening a shortcut will replace the
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// item with the appropriate LauncherItemController type).
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::AppShortcutLauncherItemController(
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& app_id,
503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    ChromeLauncherController* controller)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : LauncherItemController(TYPE_SHORTCUT, app_id, controller),
523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      chrome_launcher_controller_(controller) {
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // To detect V1 applications we use their domain and match them against the
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // used URL. This will also work with applications like Google Drive.
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id);
57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Some unit tests have no real extension.
58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (extension) {
59868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    set_refocus_url(GURL(
60868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        extensions::AppLaunchInfo::GetLaunchWebURL(extension).spec() + "*"));
61868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::~AppShortcutLauncherItemController() {
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool AppShortcutLauncherItemController::IsCurrentlyShownInWindow(
683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    aura::Window* window) const {
693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWindow(window);
703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  content::WebContents* active_content_of_window =
713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      browser ? browser->tab_strip_model()->GetActiveWebContents() : NULL;
723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      chrome_launcher_controller_->GetV1ApplicationsFromAppId(app_id());
753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  std::vector<content::WebContents*>::const_iterator iter =
773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      std::find(content.begin(), content.end(), active_content_of_window);
783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return iter != content.end() ? true : false;
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)
10468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void 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())
115eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        return;
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
11768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    Launch(source, ui::EF_NONE);
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ActivateContent(content);
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void AppShortcutLauncherItemController::Close() {
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Close all running 'programs' of this type.
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> content =
1263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      launcher_controller()->GetV1ApplicationsFromAppId(app_id());
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content.size(); i++) {
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
1291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!browser || !launcher_controller()->IsBrowserFromActiveUser(browser))
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int index = tab_strip->GetIndexOfWebContents(content[i]);
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(index != TabStripModel::kNoTab);
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_strip->CloseWebContentsAt(index, TabStripModel::CLOSE_NONE);
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeLauncherAppMenuItems
13990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)AppShortcutLauncherItemController::GetApplicationList(int event_flags) {
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ChromeLauncherAppMenuItems items;
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Add the application name to the menu.
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false));
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> content_list = GetRunningApplications();
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < content_list.size(); i++) {
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents = content_list[i];
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Get the icon.
1493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    gfx::Image app_icon = launcher_controller()->GetAppListIcon(web_contents);
1503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    string16 title = launcher_controller()->GetAppListTitle(web_contents);
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    items.push_back(new ChromeLauncherAppMenuItemTab(
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        title, &app_icon, web_contents, i == 0));
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items.Pass();
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::vector<content::WebContents*>
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AppShortcutLauncherItemController::GetRunningApplications() {
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<content::WebContents*> items;
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // It is possible to come here While an extension gets loaded.
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return items;
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_iterator it = ash_browser_list->begin();
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end(); ++it) {
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
1811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!launcher_controller()->IsBrowserFromActiveUser(browser))
1821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      continue;
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        items.push_back(web_contents);
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return items;
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void AppShortcutLauncherItemController::ItemSelected(const ui::Event& event) {
1944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#if defined(OS_CHROMEOS)
1954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!app_id().empty())
1964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    chromeos::default_pinned_apps_field_trial::RecordShelfAppClick(app_id());
1974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#endif
1984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // In case of a keyboard event, we were called by a hotkey. In that case we
1994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // activate the next item in line if an item of our list is already active.
2004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (event.type() == ui::ET_KEY_RELEASED) {
2014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (AdvanceToNextApp())
2024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      return;
2034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
2044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Activate(ash::LAUNCH_FROM_UNKNOWN);
2054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)base::string16 AppShortcutLauncherItemController::GetTitle() {
2084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return GetAppTitle();
2094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)ui::MenuModel* AppShortcutLauncherItemController::CreateContextMenu(
2121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    aura::Window* root_window) {
2134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  ash::LauncherItem item =
2144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      *(launcher_controller()->model()->ItemByID(launcher_id()));
2154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return new LauncherContextMenu(launcher_controller(), &item, root_window);
2164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)ash::LauncherMenuModel*
2194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)AppShortcutLauncherItemController::CreateApplicationMenu(int event_flags) {
2204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return new LauncherApplicationMenuItemModel(GetApplicationList(event_flags));
2214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool AppShortcutLauncherItemController::IsDraggable() {
2244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return true;
2254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool AppShortcutLauncherItemController::ShouldShowTooltip() {
2284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return true;
2294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
2304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)content::WebContents* AppShortcutLauncherItemController::GetLRUApplication() {
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  refocus_pattern.SetMatchAllURLs(true);
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!refocus_url_.is_empty()) {
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.SetMatchAllURLs(false);
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    refocus_pattern.Parse(refocus_url_.spec());
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension =
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We may get here while the extension is loading (and NULL).
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extension)
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const BrowserList* ash_browser_list =
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (BrowserList::const_reverse_iterator
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it = ash_browser_list->begin_last_active();
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != ash_browser_list->end_last_active(); ++it) {
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
2531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!launcher_controller()->IsBrowserFromActiveUser(browser))
2541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      continue;
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStripModel* tab_strip = browser->tab_strip_model();
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // We start to enumerate from the active index.
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int active_index = tab_strip->active_index();
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for (int index = 0; index < tab_strip->count(); index++) {
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::WebContents* web_contents = tab_strip->GetWebContentsAt(
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (index + active_index) % tab_strip->count());
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
262bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch        return web_contents;
263bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    }
264bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  }
265bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // Coming here our application was not in the LRU list. This could have
266bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // happened because it did never get activated yet. So check the browser list
267bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  // as well.
268bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch  for (BrowserList::const_iterator it = ash_browser_list->begin();
269bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch       it != ash_browser_list->end(); ++it) {
270bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    Browser* browser = *it;
2711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!launcher_controller()->IsBrowserFromActiveUser(browser))
2721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      continue;
273bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    TabStripModel* tab_strip = browser->tab_strip_model();
274bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch    for (int index = 0; index < tab_strip->count(); index++) {
275bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
276bb1529ce867d8845a77ec7cdf3e3003ef1771a40Ben Murdoch      if (WebContentMatchesApp(extension, refocus_pattern, web_contents))
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,
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::WebContents* web_contents) {
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const GURL tab_url = web_contents->GetURL();
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // There are three ways to identify the association of a URL with this
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // extension:
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The refocus pattern is matched (needed for apps like drive).
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The extension's origin + extent gets matched.
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // - The launcher controller knows that the tab got created for this app.
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return ((!refocus_pattern.match_all_urls() &&
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           refocus_pattern.MatchesURL(tab_url)) ||
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          (extension->OverlapsWithOrigin(tab_url) &&
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           extension->web_extent().MatchesURL(tab_url)) ||
2973551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          launcher_controller()->IsWebContentHandledByApplication(web_contents,
2983551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                                                  app_id()));
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
300c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
301c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void AppShortcutLauncherItemController::ActivateContent(
302c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    content::WebContents* content) {
303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWebContents(content);
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  TabStripModel* tab_strip = browser->tab_strip_model();
305c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int index = tab_strip->GetIndexOfWebContents(content);
306c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK_NE(TabStripModel::kNoTab, index);
307c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
308c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int old_index = tab_strip->active_index();
309c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (index != old_index)
310c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    tab_strip->ActivateTabAt(index, false);
3113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  launcher_controller()->ActivateWindowOrMinimizeIfActive(
3123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      browser->window(),
313c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      index == old_index && GetRunningApplications().size() == 1);
314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
315c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool AppShortcutLauncherItemController::AdvanceToNextApp() {
317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<content::WebContents*> items = GetRunningApplications();
318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (items.size() >= 1) {
319c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Browser* browser = chrome::FindBrowserWithWindow(
320c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ash::wm::GetActiveWindow());
321c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (browser) {
322c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      TabStripModel* tab_strip = browser->tab_strip_model();
323c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::WebContents* active = tab_strip->GetWebContentsAt(
324c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          tab_strip->active_index());
325c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      std::vector<content::WebContents*>::const_iterator i(
326c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          std::find(items.begin(), items.end(), active));
327c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (i != items.end()) {
328c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (items.size() == 1) {
329c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // If there is only a single item available, we animate it upon key
330c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          // action.
331c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          AnimateWindow(browser->window()->GetNativeWindow(),
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE);
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        } else {
334c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          int index = (static_cast<int>(i - items.begin()) + 1) % items.size();
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          ActivateContent(items[index]);
336c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        }
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return true;
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return false;
342c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
343868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
344eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool AppShortcutLauncherItemController::IsV2App() {
3453551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  const Extension* extension =
3463551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      launcher_controller()->GetExtensionForAppID(app_id());
347eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return extension && extension->is_platform_app();
348eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
349eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
350eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool AppShortcutLauncherItemController::AllowNextLaunchAttempt() {
351eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (last_launch_attempt_.is_null() ||
352eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      last_launch_attempt_ + base::TimeDelta::FromMilliseconds(
353eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          kClickSuppressionInMS) < base::Time::Now()) {
354eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    last_launch_attempt_ = base::Time::Now();
355eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return true;
356eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
357eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return false;
358868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
359