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