Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / ui / ash / launcher / app_shortcut_launcher_item_controller.cc
blobba88193e79335d4db3c68843c8e0bc3837405d61
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.h"
7 #include "ash/shelf/shelf_model.h"
8 #include "ash/shell.h"
9 #include "ash/wm/window_util.h"
10 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item.h"
11 #include "chrome/browser/ui/ash/launcher/chrome_launcher_app_menu_item_tab.h"
12 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
13 #include "chrome/browser/ui/ash/launcher/launcher_application_menu_item_model.h"
14 #include "chrome/browser/ui/ash/launcher/launcher_context_menu.h"
15 #include "chrome/browser/ui/ash/launcher/launcher_item_controller.h"
16 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
17 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_finder.h"
20 #include "chrome/browser/ui/browser_list.h"
21 #include "chrome/browser/ui/browser_window.h"
22 #include "chrome/browser/ui/host_desktop.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
24 #include "chrome/browser/web_applications/web_app.h"
25 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
26 #include "content/public/browser/navigation_entry.h"
27 #include "content/public/browser/web_contents.h"
28 #include "extensions/browser/app_window/native_app_window.h"
29 #include "extensions/browser/extension_registry.h"
30 #include "extensions/browser/process_manager.h"
31 #include "ui/aura/window.h"
32 #include "ui/events/event.h"
33 #include "ui/wm/core/window_animations.h"
35 using extensions::Extension;
36 using extensions::ExtensionRegistry;
38 namespace {
40 // The time delta between clicks in which clicks to launch V2 apps are ignored.
41 const int kClickSuppressionInMS = 1000;
43 // Check if a browser can be used for activation. This addresses a special use
44 // case in the M31 multi profile mode where a user activates a V1 app which only
45 // exists yet on another users desktop, but he expects to get only his own app
46 // items and not the ones from other users through activation.
47 // TODO(skuhne): Remove this function and replace the call with
48 // launcher_controller()->IsBrowserFromActiveUser(browser) once this experiment
49 // goes away.
50 bool CanBrowserBeUsedForDirectActivation(Browser* browser,
51 ChromeLauncherController* launcher) {
52 if (chrome::MultiUserWindowManager::GetMultiProfileMode() ==
53 chrome::MultiUserWindowManager::MULTI_PROFILE_MODE_OFF)
54 return true;
55 return multi_user_util::IsProfileFromActiveUser(browser->profile());
58 } // namespace
60 // Item controller for an app shortcut. Shortcuts track app and launcher ids,
61 // but do not have any associated windows (opening a shortcut will replace the
62 // item with the appropriate LauncherItemController type).
63 AppShortcutLauncherItemController::AppShortcutLauncherItemController(
64 const std::string& app_id,
65 ChromeLauncherController* controller)
66 : LauncherItemController(TYPE_SHORTCUT, app_id, controller),
67 chrome_launcher_controller_(controller) {
68 // To detect V1 applications we use their domain and match them against the
69 // used URL. This will also work with applications like Google Drive.
70 const Extension* extension =
71 launcher_controller()->GetExtensionForAppID(app_id);
72 // Some unit tests have no real extension.
73 if (extension) {
74 set_refocus_url(GURL(
75 extensions::AppLaunchInfo::GetLaunchWebURL(extension).spec() + "*"));
79 AppShortcutLauncherItemController::~AppShortcutLauncherItemController() {
82 bool AppShortcutLauncherItemController::IsOpen() const {
83 return !chrome_launcher_controller_->
84 GetV1ApplicationsFromAppId(app_id()).empty();
87 bool AppShortcutLauncherItemController::IsVisible() const {
88 // Return true if any browser window associated with the app is visible.
89 std::vector<content::WebContents*> content =
90 chrome_launcher_controller_->GetV1ApplicationsFromAppId(app_id());
91 for (size_t i = 0; i < content.size(); i++) {
92 Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
93 if (browser && browser->window()->GetNativeWindow()->IsVisible())
94 return true;
96 return false;
99 void AppShortcutLauncherItemController::Launch(ash::LaunchSource source,
100 int event_flags) {
101 launcher_controller()->LaunchApp(app_id(), source, event_flags);
104 bool AppShortcutLauncherItemController::Activate(ash::LaunchSource source) {
105 content::WebContents* content = GetLRUApplication();
106 if (!content) {
107 if (IsV2App()) {
108 // Ideally we come here only once. After that ShellLauncherItemController
109 // will take over when the shell window gets opened. However there are
110 // apps which take a lot of time for pre-processing (like the files app)
111 // before they open a window. Since there is currently no other way to
112 // detect if an app was started we suppress any further clicks within a
113 // special time out.
114 if (!AllowNextLaunchAttempt())
115 return false;
117 Launch(source, ui::EF_NONE);
118 return true;
120 ActivateContent(content);
121 return false;
124 void AppShortcutLauncherItemController::Close() {
125 // Close all running 'programs' of this type.
126 std::vector<content::WebContents*> content =
127 launcher_controller()->GetV1ApplicationsFromAppId(app_id());
128 for (size_t i = 0; i < content.size(); i++) {
129 Browser* browser = chrome::FindBrowserWithWebContents(content[i]);
130 if (!browser || !launcher_controller()->IsBrowserFromActiveUser(browser))
131 continue;
132 TabStripModel* tab_strip = browser->tab_strip_model();
133 int index = tab_strip->GetIndexOfWebContents(content[i]);
134 DCHECK(index != TabStripModel::kNoTab);
135 tab_strip->CloseWebContentsAt(index, TabStripModel::CLOSE_NONE);
139 ChromeLauncherAppMenuItems
140 AppShortcutLauncherItemController::GetApplicationList(int event_flags) {
141 ChromeLauncherAppMenuItems items;
142 // Add the application name to the menu.
143 items.push_back(new ChromeLauncherAppMenuItem(GetTitle(), NULL, false));
145 std::vector<content::WebContents*> content_list = GetRunningApplications();
147 for (size_t i = 0; i < content_list.size(); i++) {
148 content::WebContents* web_contents = content_list[i];
149 // Get the icon.
150 gfx::Image app_icon = launcher_controller()->GetAppListIcon(web_contents);
151 base::string16 title = launcher_controller()->GetAppListTitle(web_contents);
152 items.push_back(new ChromeLauncherAppMenuItemTab(
153 title, &app_icon, web_contents, i == 0));
155 return items.Pass();
158 std::vector<content::WebContents*>
159 AppShortcutLauncherItemController::GetRunningApplications() {
160 std::vector<content::WebContents*> items;
162 URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
163 refocus_pattern.SetMatchAllURLs(true);
165 if (!refocus_url_.is_empty()) {
166 refocus_pattern.SetMatchAllURLs(false);
167 refocus_pattern.Parse(refocus_url_.spec());
170 const Extension* extension =
171 launcher_controller()->GetExtensionForAppID(app_id());
173 // It is possible to come here While an extension gets loaded.
174 if (!extension)
175 return items;
177 const BrowserList* ash_browser_list =
178 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
179 for (BrowserList::const_iterator it = ash_browser_list->begin();
180 it != ash_browser_list->end(); ++it) {
181 Browser* browser = *it;
182 if (!launcher_controller()->IsBrowserFromActiveUser(browser))
183 continue;
184 TabStripModel* tab_strip = browser->tab_strip_model();
185 for (int index = 0; index < tab_strip->count(); index++) {
186 content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
187 if (WebContentMatchesApp(
188 extension, refocus_pattern, web_contents, browser))
189 items.push_back(web_contents);
192 return items;
195 bool AppShortcutLauncherItemController::ItemSelected(const ui::Event& event) {
196 // In case of a keyboard event, we were called by a hotkey. In that case we
197 // activate the next item in line if an item of our list is already active.
198 if (event.type() == ui::ET_KEY_RELEASED) {
199 if (AdvanceToNextApp())
200 return false;
202 return Activate(ash::LAUNCH_FROM_UNKNOWN);
205 base::string16 AppShortcutLauncherItemController::GetTitle() {
206 return GetAppTitle();
209 ui::MenuModel* AppShortcutLauncherItemController::CreateContextMenu(
210 aura::Window* root_window) {
211 ash::ShelfItem item =
212 *(launcher_controller()->model()->ItemByID(shelf_id()));
213 return new LauncherContextMenu(launcher_controller(), &item, root_window);
216 ash::ShelfMenuModel* AppShortcutLauncherItemController::CreateApplicationMenu(
217 int event_flags) {
218 return new LauncherApplicationMenuItemModel(GetApplicationList(event_flags));
221 bool AppShortcutLauncherItemController::IsDraggable() {
222 return true;
225 bool AppShortcutLauncherItemController::ShouldShowTooltip() {
226 return true;
229 content::WebContents* AppShortcutLauncherItemController::GetLRUApplication() {
230 URLPattern refocus_pattern(URLPattern::SCHEME_ALL);
231 refocus_pattern.SetMatchAllURLs(true);
233 if (!refocus_url_.is_empty()) {
234 refocus_pattern.SetMatchAllURLs(false);
235 refocus_pattern.Parse(refocus_url_.spec());
238 const Extension* extension =
239 launcher_controller()->GetExtensionForAppID(app_id());
241 // We may get here while the extension is loading (and NULL).
242 if (!extension)
243 return NULL;
245 const BrowserList* ash_browser_list =
246 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
247 for (BrowserList::const_reverse_iterator
248 it = ash_browser_list->begin_last_active();
249 it != ash_browser_list->end_last_active(); ++it) {
250 Browser* browser = *it;
251 if (!CanBrowserBeUsedForDirectActivation(browser, launcher_controller()))
252 continue;
253 TabStripModel* tab_strip = browser->tab_strip_model();
254 // We start to enumerate from the active index.
255 int active_index = tab_strip->active_index();
256 for (int index = 0; index < tab_strip->count(); index++) {
257 content::WebContents* web_contents = tab_strip->GetWebContentsAt(
258 (index + active_index) % tab_strip->count());
259 if (WebContentMatchesApp(
260 extension, refocus_pattern, web_contents, browser))
261 return web_contents;
264 // Coming here our application was not in the LRU list. This could have
265 // happened because it did never get activated yet. So check the browser list
266 // as well.
267 for (BrowserList::const_iterator it = ash_browser_list->begin();
268 it != ash_browser_list->end(); ++it) {
269 Browser* browser = *it;
270 if (!CanBrowserBeUsedForDirectActivation(browser, launcher_controller()))
271 continue;
272 TabStripModel* tab_strip = browser->tab_strip_model();
273 for (int index = 0; index < tab_strip->count(); index++) {
274 content::WebContents* web_contents = tab_strip->GetWebContentsAt(index);
275 if (WebContentMatchesApp(
276 extension, refocus_pattern, web_contents, browser))
277 return web_contents;
280 return NULL;
283 bool AppShortcutLauncherItemController::WebContentMatchesApp(
284 const extensions::Extension* extension,
285 const URLPattern& refocus_pattern,
286 content::WebContents* web_contents,
287 Browser* browser) {
288 // If the browser is an app window and the app name matches the extension.
289 if (browser->is_app()) {
290 const extensions::Extension* browser_extension =
291 ExtensionRegistry::Get(browser->profile())->GetExtensionById(
292 web_app::GetExtensionIdFromApplicationName(browser->app_name()),
293 ExtensionRegistry::EVERYTHING);
294 return browser_extension == extension;
297 // There are three ways to identify the association of a URL with this
298 // extension:
299 // - The refocus pattern is matched (needed for apps like drive).
300 // - The extension's origin + extent gets matched.
301 // - The launcher controller knows that the tab got created for this app.
302 const GURL tab_url = web_contents->GetURL();
303 return ((!refocus_pattern.match_all_urls() &&
304 refocus_pattern.MatchesURL(tab_url)) ||
305 (extension->OverlapsWithOrigin(tab_url) &&
306 extension->web_extent().MatchesURL(tab_url)) ||
307 launcher_controller()->IsWebContentHandledByApplication(web_contents,
308 app_id()));
311 void AppShortcutLauncherItemController::ActivateContent(
312 content::WebContents* content) {
313 Browser* browser = chrome::FindBrowserWithWebContents(content);
314 TabStripModel* tab_strip = browser->tab_strip_model();
315 int index = tab_strip->GetIndexOfWebContents(content);
316 DCHECK_NE(TabStripModel::kNoTab, index);
318 int old_index = tab_strip->active_index();
319 if (index != old_index)
320 tab_strip->ActivateTabAt(index, false);
321 launcher_controller()->ActivateWindowOrMinimizeIfActive(
322 browser->window(),
323 index == old_index && GetRunningApplications().size() == 1);
326 bool AppShortcutLauncherItemController::AdvanceToNextApp() {
327 std::vector<content::WebContents*> items = GetRunningApplications();
328 if (items.size() >= 1) {
329 Browser* browser = chrome::FindBrowserWithWindow(
330 ash::wm::GetActiveWindow());
331 if (browser) {
332 TabStripModel* tab_strip = browser->tab_strip_model();
333 content::WebContents* active = tab_strip->GetWebContentsAt(
334 tab_strip->active_index());
335 std::vector<content::WebContents*>::const_iterator i(
336 std::find(items.begin(), items.end(), active));
337 if (i != items.end()) {
338 if (items.size() == 1) {
339 // If there is only a single item available, we animate it upon key
340 // action.
341 AnimateWindow(browser->window()->GetNativeWindow(),
342 wm::WINDOW_ANIMATION_TYPE_BOUNCE);
343 } else {
344 int index = (static_cast<int>(i - items.begin()) + 1) % items.size();
345 ActivateContent(items[index]);
347 return true;
351 return false;
354 bool AppShortcutLauncherItemController::IsV2App() {
355 const Extension* extension =
356 launcher_controller()->GetExtensionForAppID(app_id());
357 return extension && extension->is_platform_app();
360 bool AppShortcutLauncherItemController::AllowNextLaunchAttempt() {
361 if (last_launch_attempt_.is_null() ||
362 last_launch_attempt_ + base::TimeDelta::FromMilliseconds(
363 kClickSuppressionInMS) < base::Time::Now()) {
364 last_launch_attempt_ = base::Time::Now();
365 return true;
367 return false;