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"
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"
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
;
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
) {}
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
) {
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(
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(
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(
145 extension_misc::EXTENSION_ICON_LARGE
,
146 ExtensionIconSet::MATCH_BIGGER
,
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(
154 extension_misc::EXTENSION_ICON_BITTY
,
155 ExtensionIconSet::MATCH_BIGGER
,
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
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)));
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();
256 if (ignore_changes_
|| !has_loaded_apps_
)
260 case chrome::NOTIFICATION_EXTENSION_LOADED
: {
261 const Extension
* extension
=
262 content::Details
<const Extension
>(details
).ptr();
263 if (!extension
->is_app())
266 PrefService
* prefs
= Profile::FromWebUI(web_ui())->GetPrefs();
267 if (!ShouldDisplayInNewTabPage(extension
, prefs
))
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
);
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();
293 } else { // NOTIFICATION_EXTENSION_UNLOADED
294 if (content::Details
<UnloadedExtensionInfo
>(details
)->reason
==
295 UnloadedExtensionInfo::REASON_UNINSTALL
) {
296 // Uninstalls are tracked by NOTIFICATION_EXTENSION_UNINSTALLED.
299 extension
= content::Details
<extensions::UnloadedExtensionInfo
>(
303 if (!extension
->is_app())
306 PrefService
* prefs
= Profile::FromWebUI(web_ui())->GetPrefs();
307 if (!ShouldDisplayInNewTabPage(extension
, prefs
))
310 scoped_ptr
<base::DictionaryValue
> app_info(GetAppInfo(extension
));
311 if (app_info
.get()) {
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
);
324 case chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED
: {
325 const std::string
* id
=
326 content::Details
<const std::string
>(details
).ptr();
328 const Extension
* extension
=
329 extension_service_
->GetInstalledExtension(*id
);
331 // Extension could still be downloading or installing.
335 base::DictionaryValue app_info
;
336 CreateAppInfo(extension
,
339 web_ui()->CallJavascriptFunction("ntp.appMoved", app_info
);
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())) {
353 case chrome::NOTIFICATION_EXTENSION_LOAD_ERROR
: {
354 attempted_bookmark_app_install_
= false;
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);
388 #if defined(OS_CHROMEOS)
389 // Making shortcut does not make sense on ChromeOS because it does not have
391 dictionary
->SetBoolean("disableCreateAppShortcut", true);
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()));
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
,
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
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
));
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.
510 PromptToEnableApp(extension_id
);
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());
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
,
534 params
.override_url
= GURL(url
);
535 OpenApplication(params
);
537 // To give a more "launchy" experience when using the NTP launcher, we close
539 Browser
* browser
= chrome::FindBrowserWithWebContents(
540 web_ui()->GetWebContents());
541 WebContents
* old_contents
= NULL
;
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
;
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);
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(
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(
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();
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();
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);
616 Browser
* browser
= chrome::FindBrowserWithWebContents(
617 web_ui()->GetWebContents());
618 chrome::ShowCreateChromeAppShortcutsDialog(
619 browser
->window()->GetNativeWindow(), browser
->profile(), extension
,
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
) {
635 if (app_order
->GetString(i
, &value
) && value
== dragged_app_id
) {
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
));
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
;
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
) {
670 CHECK(args
->GetString(0, &name
));
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
) {
684 CHECK(args
->GetString(0, &url
));
685 GURL
launch_url(url
);
687 base::string16 title
;
688 CHECK(args
->GetString(1, &title
));
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";
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
,
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
);
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())
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
)));
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_
);
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(),
853 return extension_uninstall_dialog_
.get();