NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / ui / webui / ntp / app_launcher_handler.cc
blob02a49e050699dc59fd2aa2cb46da0ef6881c7499
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/webui/ntp/app_launcher_handler.h"
7 #include <vector>
9 #include "apps/metrics_names.h"
10 #include "base/auto_reset.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/i18n/rtl.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/metrics/histogram.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/prefs/scoped_user_pref_update.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/extensions/crx_installer.h"
23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/browser/extensions/launch_util.h"
25 #include "chrome/browser/favicon/favicon_service_factory.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/ui/app_list/app_list_util.h"
28 #include "chrome/browser/ui/browser_dialogs.h"
29 #include "chrome/browser/ui/browser_finder.h"
30 #include "chrome/browser/ui/browser_tabstrip.h"
31 #include "chrome/browser/ui/browser_window.h"
32 #include "chrome/browser/ui/extensions/application_launch.h"
33 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
34 #include "chrome/browser/ui/tabs/tab_strip_model.h"
35 #include "chrome/browser/ui/webui/extensions/extension_basic_info.h"
36 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
37 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h"
38 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
39 #include "chrome/common/extensions/extension_constants.h"
40 #include "chrome/common/extensions/extension_icon_set.h"
41 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
42 #include "chrome/common/favicon/favicon_types.h"
43 #include "chrome/common/pref_names.h"
44 #include "chrome/common/url_constants.h"
45 #include "chrome/common/web_application_info.h"
46 #include "content/public/browser/notification_service.h"
47 #include "content/public/browser/web_ui.h"
48 #include "content/public/common/favicon_url.h"
49 #include "extensions/browser/app_sorting.h"
50 #include "extensions/browser/extension_registry.h"
51 #include "extensions/browser/extension_system.h"
52 #include "extensions/browser/management_policy.h"
53 #include "extensions/browser/pref_names.h"
54 #include "extensions/common/constants.h"
55 #include "extensions/common/extension.h"
56 #include "extensions/common/extension_set.h"
57 #include "grit/browser_resources.h"
58 #include "grit/generated_resources.h"
59 #include "ui/base/l10n/l10n_util.h"
60 #include "ui/base/webui/web_ui_util.h"
61 #include "ui/gfx/favicon_size.h"
62 #include "url/gurl.h"
64 using content::WebContents;
65 using extensions::AppSorting;
66 using extensions::CrxInstaller;
67 using extensions::Extension;
68 using extensions::ExtensionPrefs;
69 using extensions::ExtensionRegistry;
70 using extensions::ExtensionSet;
71 using extensions::UnloadedExtensionInfo;
73 namespace {
75 bool ShouldDisplayInNewTabPage(const Extension* app, PrefService* prefs) {
76 bool blocked_by_policy =
77 (app->id() == extension_misc::kWebStoreAppId ||
78 app->id() == extension_misc::kEnterpriseWebStoreAppId) &&
79 prefs->GetBoolean(prefs::kHideWebStoreIcon);
80 return app->ShouldDisplayInNewTabPage() && !blocked_by_policy;
83 void RecordAppLauncherPromoHistogram(
84 apps::AppLauncherPromoHistogramValues value) {
85 DCHECK_LT(value, apps::APP_LAUNCHER_PROMO_MAX);
86 UMA_HISTOGRAM_ENUMERATION(
87 "Apps.AppLauncherPromo", value, apps::APP_LAUNCHER_PROMO_MAX);
90 // This is used to avoid a DCHECK due to an unhandled WebUI callback. The
91 // JavaScript used to switch between pages sends "pageSelected" which is used
92 // in the context of the NTP for recording metrics we don't need here.
93 void NoOpCallback(const base::ListValue* args) {}
95 } // namespace
97 AppLauncherHandler::AppInstallInfo::AppInstallInfo() {}
99 AppLauncherHandler::AppInstallInfo::~AppInstallInfo() {}
101 AppLauncherHandler::AppLauncherHandler(ExtensionService* extension_service)
102 : extension_service_(extension_service),
103 ignore_changes_(false),
104 attempted_bookmark_app_install_(false),
105 has_loaded_apps_(false) {
106 if (IsAppLauncherEnabled())
107 RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_ALREADY_INSTALLED);
108 else if (ShouldShowAppLauncherPromo())
109 RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_SHOWN);
112 AppLauncherHandler::~AppLauncherHandler() {}
114 void AppLauncherHandler::CreateAppInfo(
115 const Extension* extension,
116 ExtensionService* service,
117 base::DictionaryValue* value) {
118 value->Clear();
120 // The Extension class 'helpfully' wraps bidi control characters that
121 // impede our ability to determine directionality.
122 base::string16 short_name = base::UTF8ToUTF16(extension->short_name());
123 base::i18n::UnadjustStringForLocaleDirection(&short_name);
124 NewTabUI::SetUrlTitleAndDirection(
125 value,
126 short_name,
127 extensions::AppLaunchInfo::GetFullLaunchURL(extension));
129 base::string16 name = base::UTF8ToUTF16(extension->name());
130 base::i18n::UnadjustStringForLocaleDirection(&name);
131 NewTabUI::SetFullNameAndDirection(name, value);
133 bool enabled = service->IsExtensionEnabled(extension->id()) &&
134 !service->GetTerminatedExtension(extension->id());
135 extensions::GetExtensionBasicInfo(extension, enabled, value);
137 value->SetBoolean("mayDisable", extensions::ExtensionSystem::Get(
138 service->profile())->management_policy()->UserMayModifySettings(
139 extension, NULL));
141 bool icon_big_exists = true;
142 // Instead of setting grayscale here, we do it in apps_page.js.
143 GURL icon_big = extensions::ExtensionIconSource::GetIconURL(
144 extension,
145 extension_misc::EXTENSION_ICON_LARGE,
146 ExtensionIconSet::MATCH_BIGGER,
147 false,
148 &icon_big_exists);
149 value->SetString("icon_big", icon_big.spec());
150 value->SetBoolean("icon_big_exists", icon_big_exists);
151 bool icon_small_exists = true;
152 GURL icon_small = extensions::ExtensionIconSource::GetIconURL(
153 extension,
154 extension_misc::EXTENSION_ICON_BITTY,
155 ExtensionIconSet::MATCH_BIGGER,
156 false,
157 &icon_small_exists);
158 value->SetString("icon_small", icon_small.spec());
159 value->SetBoolean("icon_small_exists", icon_small_exists);
160 value->SetInteger("launch_container",
161 extensions::AppLaunchInfo::GetLaunchContainer(extension));
162 ExtensionPrefs* prefs = service->extension_prefs();
163 value->SetInteger("launch_type", extensions::GetLaunchType(prefs, extension));
164 value->SetBoolean("is_component",
165 extension->location() == extensions::Manifest::COMPONENT);
166 value->SetBoolean("is_webstore",
167 extension->id() == extension_misc::kWebStoreAppId);
169 AppSorting* sorting = prefs->app_sorting();
170 syncer::StringOrdinal page_ordinal = sorting->GetPageOrdinal(extension->id());
171 if (!page_ordinal.IsValid()) {
172 // Make sure every app has a page ordinal (some predate the page ordinal).
173 // The webstore app should be on the first page.
174 page_ordinal = extension->id() == extension_misc::kWebStoreAppId ?
175 sorting->CreateFirstAppPageOrdinal() :
176 sorting->GetNaturalAppPageOrdinal();
177 sorting->SetPageOrdinal(extension->id(), page_ordinal);
179 value->SetInteger("page_index",
180 sorting->PageStringOrdinalAsInteger(page_ordinal));
182 syncer::StringOrdinal app_launch_ordinal =
183 sorting->GetAppLaunchOrdinal(extension->id());
184 if (!app_launch_ordinal.IsValid()) {
185 // Make sure every app has a launch ordinal (some predate the launch
186 // ordinal). The webstore's app launch ordinal is always set to the first
187 // position.
188 app_launch_ordinal = extension->id() == extension_misc::kWebStoreAppId ?
189 sorting->CreateFirstAppLaunchOrdinal(page_ordinal) :
190 sorting->CreateNextAppLaunchOrdinal(page_ordinal);
191 sorting->SetAppLaunchOrdinal(extension->id(), app_launch_ordinal);
193 value->SetString("app_launch_ordinal", app_launch_ordinal.ToInternalValue());
196 void AppLauncherHandler::RegisterMessages() {
197 registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_NTP,
198 content::Source<WebContents>(web_ui()->GetWebContents()));
200 // Some tests don't have a local state.
201 #if defined(ENABLE_APP_LIST)
202 if (g_browser_process->local_state()) {
203 local_state_pref_change_registrar_.Init(g_browser_process->local_state());
204 local_state_pref_change_registrar_.Add(
205 prefs::kShowAppLauncherPromo,
206 base::Bind(&AppLauncherHandler::OnLocalStatePreferenceChanged,
207 base::Unretained(this)));
209 #endif
210 web_ui()->RegisterMessageCallback("getApps",
211 base::Bind(&AppLauncherHandler::HandleGetApps,
212 base::Unretained(this)));
213 web_ui()->RegisterMessageCallback("launchApp",
214 base::Bind(&AppLauncherHandler::HandleLaunchApp,
215 base::Unretained(this)));
216 web_ui()->RegisterMessageCallback("setLaunchType",
217 base::Bind(&AppLauncherHandler::HandleSetLaunchType,
218 base::Unretained(this)));
219 web_ui()->RegisterMessageCallback("uninstallApp",
220 base::Bind(&AppLauncherHandler::HandleUninstallApp,
221 base::Unretained(this)));
222 web_ui()->RegisterMessageCallback("createAppShortcut",
223 base::Bind(&AppLauncherHandler::HandleCreateAppShortcut,
224 base::Unretained(this)));
225 web_ui()->RegisterMessageCallback("reorderApps",
226 base::Bind(&AppLauncherHandler::HandleReorderApps,
227 base::Unretained(this)));
228 web_ui()->RegisterMessageCallback("setPageIndex",
229 base::Bind(&AppLauncherHandler::HandleSetPageIndex,
230 base::Unretained(this)));
231 web_ui()->RegisterMessageCallback("saveAppPageName",
232 base::Bind(&AppLauncherHandler::HandleSaveAppPageName,
233 base::Unretained(this)));
234 web_ui()->RegisterMessageCallback("generateAppForLink",
235 base::Bind(&AppLauncherHandler::HandleGenerateAppForLink,
236 base::Unretained(this)));
237 web_ui()->RegisterMessageCallback("stopShowingAppLauncherPromo",
238 base::Bind(&AppLauncherHandler::StopShowingAppLauncherPromo,
239 base::Unretained(this)));
240 web_ui()->RegisterMessageCallback("onLearnMore",
241 base::Bind(&AppLauncherHandler::OnLearnMore,
242 base::Unretained(this)));
243 web_ui()->RegisterMessageCallback("pageSelected", base::Bind(&NoOpCallback));
246 void AppLauncherHandler::Observe(int type,
247 const content::NotificationSource& source,
248 const content::NotificationDetails& details) {
249 if (type == chrome::NOTIFICATION_APP_INSTALLED_TO_NTP) {
250 highlight_app_id_ = *content::Details<const std::string>(details).ptr();
251 if (has_loaded_apps_)
252 SetAppToBeHighlighted();
253 return;
256 if (ignore_changes_ || !has_loaded_apps_)
257 return;
259 switch (type) {
260 case chrome::NOTIFICATION_EXTENSION_LOADED: {
261 const Extension* extension =
262 content::Details<const Extension>(details).ptr();
263 if (!extension->is_app())
264 return;
266 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
267 if (!ShouldDisplayInNewTabPage(extension, prefs))
268 return;
270 scoped_ptr<base::DictionaryValue> app_info(GetAppInfo(extension));
271 if (app_info.get()) {
272 visible_apps_.insert(extension->id());
274 ExtensionPrefs* prefs = extension_service_->extension_prefs();
275 scoped_ptr<base::FundamentalValue> highlight(
276 base::Value::CreateBooleanValue(
277 prefs->IsFromBookmark(extension->id()) &&
278 attempted_bookmark_app_install_));
279 attempted_bookmark_app_install_ = false;
280 web_ui()->CallJavascriptFunction(
281 "ntp.appAdded", *app_info, *highlight);
284 break;
286 case chrome::NOTIFICATION_EXTENSION_UNLOADED:
287 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
288 const Extension* extension = NULL;
289 bool uninstalled = false;
290 if (type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED) {
291 extension = content::Details<const Extension>(details).ptr();
292 uninstalled = true;
293 } else { // NOTIFICATION_EXTENSION_UNLOADED
294 if (content::Details<UnloadedExtensionInfo>(details)->reason ==
295 UnloadedExtensionInfo::REASON_UNINSTALL) {
296 // Uninstalls are tracked by NOTIFICATION_EXTENSION_UNINSTALLED.
297 return;
299 extension = content::Details<extensions::UnloadedExtensionInfo>(
300 details)->extension;
301 uninstalled = false;
303 if (!extension->is_app())
304 return;
306 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
307 if (!ShouldDisplayInNewTabPage(extension, prefs))
308 return;
310 scoped_ptr<base::DictionaryValue> app_info(GetAppInfo(extension));
311 if (app_info.get()) {
312 if (uninstalled)
313 visible_apps_.erase(extension->id());
315 scoped_ptr<base::FundamentalValue> uninstall_value(
316 base::Value::CreateBooleanValue(uninstalled));
317 scoped_ptr<base::FundamentalValue> from_page(
318 base::Value::CreateBooleanValue(!extension_id_prompting_.empty()));
319 web_ui()->CallJavascriptFunction(
320 "ntp.appRemoved", *app_info, *uninstall_value, *from_page);
322 break;
324 case chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED: {
325 const std::string* id =
326 content::Details<const std::string>(details).ptr();
327 if (id) {
328 const Extension* extension =
329 extension_service_->GetInstalledExtension(*id);
330 if (!extension) {
331 // Extension could still be downloading or installing.
332 return;
335 base::DictionaryValue app_info;
336 CreateAppInfo(extension,
337 extension_service_,
338 &app_info);
339 web_ui()->CallJavascriptFunction("ntp.appMoved", app_info);
340 } else {
341 HandleGetApps(NULL);
343 break;
345 case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
346 CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr();
347 if (!Profile::FromWebUI(web_ui())->IsSameProfile(
348 crx_installer->profile())) {
349 return;
351 // Fall through.
353 case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR: {
354 attempted_bookmark_app_install_ = false;
355 break;
357 default:
358 NOTREACHED();
362 void AppLauncherHandler::FillAppDictionary(base::DictionaryValue* dictionary) {
363 // CreateAppInfo and ClearOrdinals can change the extension prefs.
364 base::AutoReset<bool> auto_reset(&ignore_changes_, true);
366 base::ListValue* list = new base::ListValue();
367 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
369 for (std::set<std::string>::iterator it = visible_apps_.begin();
370 it != visible_apps_.end(); ++it) {
371 const Extension* extension = extension_service_->GetInstalledExtension(*it);
372 if (extension && ShouldDisplayInNewTabPage(extension, prefs)) {
373 base::DictionaryValue* app_info = GetAppInfo(extension);
374 list->Append(app_info);
378 dictionary->Set("apps", list);
380 // TODO(estade): remove these settings when the old NTP is removed. The new
381 // NTP does it in js.
382 #if defined(OS_MACOSX)
383 // App windows are not yet implemented on mac.
384 dictionary->SetBoolean("disableAppWindowLaunch", true);
385 dictionary->SetBoolean("disableCreateAppShortcut", true);
386 #endif
388 #if defined(OS_CHROMEOS)
389 // Making shortcut does not make sense on ChromeOS because it does not have
390 // a desktop.
391 dictionary->SetBoolean("disableCreateAppShortcut", true);
392 #endif
394 const base::ListValue* app_page_names =
395 prefs->GetList(prefs::kNtpAppPageNames);
396 if (!app_page_names || !app_page_names->GetSize()) {
397 ListPrefUpdate update(prefs, prefs::kNtpAppPageNames);
398 base::ListValue* list = update.Get();
399 list->Set(0, new base::StringValue(
400 l10n_util::GetStringUTF16(IDS_APP_DEFAULT_PAGE_NAME)));
401 dictionary->Set("appPageNames",
402 static_cast<base::ListValue*>(list->DeepCopy()));
403 } else {
404 dictionary->Set("appPageNames",
405 static_cast<base::ListValue*>(app_page_names->DeepCopy()));
409 base::DictionaryValue* AppLauncherHandler::GetAppInfo(
410 const Extension* extension) {
411 base::DictionaryValue* app_info = new base::DictionaryValue();
412 // CreateAppInfo can change the extension prefs.
413 base::AutoReset<bool> auto_reset(&ignore_changes_, true);
414 CreateAppInfo(extension,
415 extension_service_,
416 app_info);
417 return app_info;
420 void AppLauncherHandler::HandleGetApps(const base::ListValue* args) {
421 base::DictionaryValue dictionary;
423 // Tell the client whether to show the promo for this view. We don't do this
424 // in the case of PREF_CHANGED because:
426 // a) At that point in time, depending on the pref that changed, it can look
427 // like the set of apps installed has changed, and we will mark the promo
428 // expired.
429 // b) Conceptually, it doesn't really make sense to count a
430 // prefchange-triggered refresh as a promo 'view'.
431 Profile* profile = Profile::FromWebUI(web_ui());
433 // The first time we load the apps we must add all current app to the list
434 // of apps visible on the NTP.
435 if (!has_loaded_apps_) {
436 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
437 const ExtensionSet& enabled_set = registry->enabled_extensions();
438 for (extensions::ExtensionSet::const_iterator it = enabled_set.begin();
439 it != enabled_set.end(); ++it) {
440 visible_apps_.insert((*it)->id());
443 const ExtensionSet& disabled_set = registry->disabled_extensions();
444 for (ExtensionSet::const_iterator it = disabled_set.begin();
445 it != disabled_set.end(); ++it) {
446 visible_apps_.insert((*it)->id());
449 const ExtensionSet& terminated_set = registry->terminated_extensions();
450 for (ExtensionSet::const_iterator it = terminated_set.begin();
451 it != terminated_set.end(); ++it) {
452 visible_apps_.insert((*it)->id());
456 SetAppToBeHighlighted();
457 FillAppDictionary(&dictionary);
458 web_ui()->CallJavascriptFunction("ntp.getAppsCallback", dictionary);
460 // First time we get here we set up the observer so that we can tell update
461 // the apps as they change.
462 if (!has_loaded_apps_) {
463 base::Closure callback = base::Bind(
464 &AppLauncherHandler::OnExtensionPreferenceChanged,
465 base::Unretained(this));
466 extension_pref_change_registrar_.Init(
467 extension_service_->extension_prefs()->pref_service());
468 extension_pref_change_registrar_.Add(
469 extensions::pref_names::kExtensions, callback);
470 extension_pref_change_registrar_.Add(prefs::kNtpAppPageNames, callback);
472 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
473 content::Source<Profile>(profile));
474 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
475 content::Source<Profile>(profile));
476 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
477 content::Source<Profile>(profile));
478 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
479 content::Source<AppSorting>(
480 extension_service_->extension_prefs()->app_sorting()));
481 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
482 content::Source<CrxInstaller>(NULL));
483 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOAD_ERROR,
484 content::Source<Profile>(profile));
487 has_loaded_apps_ = true;
490 void AppLauncherHandler::HandleLaunchApp(const base::ListValue* args) {
491 std::string extension_id;
492 CHECK(args->GetString(0, &extension_id));
493 double source = -1.0;
494 CHECK(args->GetDouble(1, &source));
495 std::string url;
496 if (args->GetSize() > 2)
497 CHECK(args->GetString(2, &url));
499 extension_misc::AppLaunchBucket launch_bucket =
500 static_cast<extension_misc::AppLaunchBucket>(
501 static_cast<int>(source));
502 CHECK(launch_bucket >= 0 &&
503 launch_bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
505 const Extension* extension =
506 extension_service_->GetExtensionById(extension_id, false);
508 // Prompt the user to re-enable the application if disabled.
509 if (!extension) {
510 PromptToEnableApp(extension_id);
511 return;
514 Profile* profile = extension_service_->profile();
516 WindowOpenDisposition disposition = args->GetSize() > 3 ?
517 webui::GetDispositionFromClick(args, 3) : CURRENT_TAB;
518 if (extension_id != extension_misc::kWebStoreAppId) {
519 CHECK_NE(launch_bucket, extension_misc::APP_LAUNCH_BUCKET_INVALID);
520 CoreAppLauncherHandler::RecordAppLaunchType(launch_bucket,
521 extension->GetType());
522 } else {
523 CoreAppLauncherHandler::RecordWebStoreLaunch();
526 if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB ||
527 disposition == NEW_WINDOW) {
528 // TODO(jamescook): Proper support for background tabs.
529 AppLaunchParams params(profile, extension,
530 disposition == NEW_WINDOW ?
531 extensions::LAUNCH_CONTAINER_WINDOW :
532 extensions::LAUNCH_CONTAINER_TAB,
533 disposition);
534 params.override_url = GURL(url);
535 OpenApplication(params);
536 } else {
537 // To give a more "launchy" experience when using the NTP launcher, we close
538 // it automatically.
539 Browser* browser = chrome::FindBrowserWithWebContents(
540 web_ui()->GetWebContents());
541 WebContents* old_contents = NULL;
542 if (browser)
543 old_contents = browser->tab_strip_model()->GetActiveWebContents();
545 AppLaunchParams params(profile, extension,
546 old_contents ? CURRENT_TAB : NEW_FOREGROUND_TAB);
547 params.override_url = GURL(url);
548 WebContents* new_contents = OpenApplication(params);
550 // This will also destroy the handler, so do not perform any actions after.
551 if (new_contents != old_contents && browser &&
552 browser->tab_strip_model()->count() > 1) {
553 chrome::CloseWebContents(browser, old_contents, true);
558 void AppLauncherHandler::HandleSetLaunchType(const base::ListValue* args) {
559 std::string extension_id;
560 double launch_type;
561 CHECK(args->GetString(0, &extension_id));
562 CHECK(args->GetDouble(1, &launch_type));
564 const Extension* extension =
565 extension_service_->GetExtensionById(extension_id, true);
566 if (!extension)
567 return;
569 // Don't update the page; it already knows about the launch type change.
570 base::AutoReset<bool> auto_reset(&ignore_changes_, true);
572 extensions::SetLaunchType(
573 extension_service_,
574 extension_id,
575 static_cast<extensions::LaunchType>(static_cast<int>(launch_type)));
578 void AppLauncherHandler::HandleUninstallApp(const base::ListValue* args) {
579 std::string extension_id;
580 CHECK(args->GetString(0, &extension_id));
582 const Extension* extension = extension_service_->GetInstalledExtension(
583 extension_id);
584 if (!extension)
585 return;
587 if (!extensions::ExtensionSystem::Get(extension_service_->profile())->
588 management_policy()->UserMayModifySettings(extension, NULL)) {
589 LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable "
590 << "was made. Extension id : " << extension->id();
591 return;
593 if (!extension_id_prompting_.empty())
594 return; // Only one prompt at a time.
596 extension_id_prompting_ = extension_id;
598 bool dont_confirm = false;
599 if (args->GetBoolean(1, &dont_confirm) && dont_confirm) {
600 base::AutoReset<bool> auto_reset(&ignore_changes_, true);
601 ExtensionUninstallAccepted();
602 } else {
603 GetExtensionUninstallDialog()->ConfirmUninstall(extension);
607 void AppLauncherHandler::HandleCreateAppShortcut(const base::ListValue* args) {
608 std::string extension_id;
609 CHECK(args->GetString(0, &extension_id));
611 const Extension* extension =
612 extension_service_->GetExtensionById(extension_id, true);
613 if (!extension)
614 return;
616 Browser* browser = chrome::FindBrowserWithWebContents(
617 web_ui()->GetWebContents());
618 chrome::ShowCreateChromeAppShortcutsDialog(
619 browser->window()->GetNativeWindow(), browser->profile(), extension,
620 base::Closure());
623 void AppLauncherHandler::HandleReorderApps(const base::ListValue* args) {
624 CHECK(args->GetSize() == 2);
626 std::string dragged_app_id;
627 const base::ListValue* app_order;
628 CHECK(args->GetString(0, &dragged_app_id));
629 CHECK(args->GetList(1, &app_order));
631 std::string predecessor_to_moved_ext;
632 std::string successor_to_moved_ext;
633 for (size_t i = 0; i < app_order->GetSize(); ++i) {
634 std::string value;
635 if (app_order->GetString(i, &value) && value == dragged_app_id) {
636 if (i > 0)
637 CHECK(app_order->GetString(i - 1, &predecessor_to_moved_ext));
638 if (i + 1 < app_order->GetSize())
639 CHECK(app_order->GetString(i + 1, &successor_to_moved_ext));
640 break;
644 // Don't update the page; it already knows the apps have been reordered.
645 base::AutoReset<bool> auto_reset(&ignore_changes_, true);
646 extension_service_->extension_prefs()->SetAppDraggedByUser(dragged_app_id);
647 extension_service_->OnExtensionMoved(dragged_app_id,
648 predecessor_to_moved_ext,
649 successor_to_moved_ext);
652 void AppLauncherHandler::HandleSetPageIndex(const base::ListValue* args) {
653 AppSorting* app_sorting =
654 extension_service_->extension_prefs()->app_sorting();
656 std::string extension_id;
657 double page_index;
658 CHECK(args->GetString(0, &extension_id));
659 CHECK(args->GetDouble(1, &page_index));
660 const syncer::StringOrdinal& page_ordinal =
661 app_sorting->PageIntegerAsStringOrdinal(static_cast<size_t>(page_index));
663 // Don't update the page; it already knows the apps have been reordered.
664 base::AutoReset<bool> auto_reset(&ignore_changes_, true);
665 app_sorting->SetPageOrdinal(extension_id, page_ordinal);
668 void AppLauncherHandler::HandleSaveAppPageName(const base::ListValue* args) {
669 base::string16 name;
670 CHECK(args->GetString(0, &name));
672 double page_index;
673 CHECK(args->GetDouble(1, &page_index));
675 base::AutoReset<bool> auto_reset(&ignore_changes_, true);
676 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
677 ListPrefUpdate update(prefs, prefs::kNtpAppPageNames);
678 base::ListValue* list = update.Get();
679 list->Set(static_cast<size_t>(page_index), new base::StringValue(name));
682 void AppLauncherHandler::HandleGenerateAppForLink(const base::ListValue* args) {
683 std::string url;
684 CHECK(args->GetString(0, &url));
685 GURL launch_url(url);
687 base::string16 title;
688 CHECK(args->GetString(1, &title));
690 double page_index;
691 CHECK(args->GetDouble(2, &page_index));
692 AppSorting* app_sorting =
693 extension_service_->extension_prefs()->app_sorting();
694 const syncer::StringOrdinal& page_ordinal =
695 app_sorting->PageIntegerAsStringOrdinal(static_cast<size_t>(page_index));
697 Profile* profile = Profile::FromWebUI(web_ui());
698 FaviconService* favicon_service =
699 FaviconServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS);
700 if (!favicon_service) {
701 LOG(ERROR) << "No favicon service";
702 return;
705 scoped_ptr<AppInstallInfo> install_info(new AppInstallInfo());
706 install_info->title = title;
707 install_info->app_url = launch_url;
708 install_info->page_ordinal = page_ordinal;
710 favicon_service->GetFaviconImageForURL(
711 FaviconService::FaviconForURLParams(launch_url,
712 chrome::FAVICON,
713 gfx::kFaviconSize),
714 base::Bind(&AppLauncherHandler::OnFaviconForApp,
715 base::Unretained(this),
716 base::Passed(&install_info)),
717 &cancelable_task_tracker_);
720 void AppLauncherHandler::StopShowingAppLauncherPromo(
721 const base::ListValue* args) {
722 #if defined(ENABLE_APP_LIST)
723 g_browser_process->local_state()->SetBoolean(
724 prefs::kShowAppLauncherPromo, false);
725 RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_DISMISSED);
726 #endif
729 void AppLauncherHandler::OnLearnMore(const base::ListValue* args) {
730 RecordAppLauncherPromoHistogram(apps::APP_LAUNCHER_PROMO_LEARN_MORE);
733 void AppLauncherHandler::OnFaviconForApp(
734 scoped_ptr<AppInstallInfo> install_info,
735 const chrome::FaviconImageResult& image_result) {
736 scoped_ptr<WebApplicationInfo> web_app(new WebApplicationInfo());
737 web_app->title = install_info->title;
738 web_app->app_url = install_info->app_url;
740 if (!image_result.image.IsEmpty()) {
741 WebApplicationInfo::IconInfo icon;
742 icon.data = image_result.image.AsBitmap();
743 icon.width = icon.data.width();
744 icon.height = icon.data.height();
745 web_app->icons.push_back(icon);
748 scoped_refptr<CrxInstaller> installer(
749 CrxInstaller::CreateSilent(extension_service_));
750 installer->set_error_on_unsupported_requirements(true);
751 installer->set_page_ordinal(install_info->page_ordinal);
752 installer->InstallWebApp(*web_app);
753 attempted_bookmark_app_install_ = true;
756 void AppLauncherHandler::SetAppToBeHighlighted() {
757 if (highlight_app_id_.empty())
758 return;
760 base::StringValue app_id(highlight_app_id_);
761 web_ui()->CallJavascriptFunction("ntp.setAppToBeHighlighted", app_id);
762 highlight_app_id_.clear();
765 void AppLauncherHandler::OnExtensionPreferenceChanged() {
766 base::DictionaryValue dictionary;
767 FillAppDictionary(&dictionary);
768 web_ui()->CallJavascriptFunction("ntp.appsPrefChangeCallback", dictionary);
771 void AppLauncherHandler::OnLocalStatePreferenceChanged() {
772 #if defined(ENABLE_APP_LIST)
773 web_ui()->CallJavascriptFunction(
774 "ntp.appLauncherPromoPrefChangeCallback",
775 base::FundamentalValue(g_browser_process->local_state()->GetBoolean(
776 prefs::kShowAppLauncherPromo)));
777 #endif
780 void AppLauncherHandler::CleanupAfterUninstall() {
781 extension_id_prompting_.clear();
784 void AppLauncherHandler::PromptToEnableApp(const std::string& extension_id) {
785 if (!extension_id_prompting_.empty())
786 return; // Only one prompt at a time.
788 extension_id_prompting_ = extension_id;
789 extension_enable_flow_.reset(new ExtensionEnableFlow(
790 Profile::FromWebUI(web_ui()), extension_id, this));
791 extension_enable_flow_->StartForWebContents(web_ui()->GetWebContents());
794 void AppLauncherHandler::ExtensionUninstallAccepted() {
795 // Do the uninstall work here.
796 DCHECK(!extension_id_prompting_.empty());
798 // The extension can be uninstalled in another window while the UI was
799 // showing. Do nothing in that case.
800 const Extension* extension =
801 extension_service_->GetInstalledExtension(extension_id_prompting_);
802 if (!extension)
803 return;
805 extension_service_->UninstallExtension(extension_id_prompting_,
806 false /* external_uninstall */, NULL);
807 CleanupAfterUninstall();
810 void AppLauncherHandler::ExtensionUninstallCanceled() {
811 CleanupAfterUninstall();
814 void AppLauncherHandler::ExtensionEnableFlowFinished() {
815 DCHECK_EQ(extension_id_prompting_, extension_enable_flow_->extension_id());
817 // We bounce this off the NTP so the browser can update the apps icon.
818 // If we don't launch the app asynchronously, then the app's disabled
819 // icon disappears but isn't replaced by the enabled icon, making a poor
820 // visual experience.
821 base::StringValue app_id(extension_id_prompting_);
822 web_ui()->CallJavascriptFunction("ntp.launchAppAfterEnable", app_id);
824 extension_enable_flow_.reset();
825 extension_id_prompting_ = "";
828 void AppLauncherHandler::ExtensionEnableFlowAborted(bool user_initiated) {
829 DCHECK_EQ(extension_id_prompting_, extension_enable_flow_->extension_id());
831 // We record the histograms here because ExtensionUninstallCanceled is also
832 // called when the extension uninstall dialog is canceled.
833 const Extension* extension =
834 extension_service_->GetExtensionById(extension_id_prompting_, true);
835 std::string histogram_name = user_initiated ?
836 "Extensions.Permissions_ReEnableCancel" :
837 "Extensions.Permissions_ReEnableAbort";
838 ExtensionService::RecordPermissionMessagesHistogram(
839 extension, histogram_name.c_str());
841 extension_enable_flow_.reset();
842 CleanupAfterUninstall();
845 ExtensionUninstallDialog* AppLauncherHandler::GetExtensionUninstallDialog() {
846 if (!extension_uninstall_dialog_.get()) {
847 Browser* browser = chrome::FindBrowserWithWebContents(
848 web_ui()->GetWebContents());
849 extension_uninstall_dialog_.reset(
850 ExtensionUninstallDialog::Create(extension_service_->profile(),
851 browser, this));
853 return extension_uninstall_dialog_.get();