• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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.
4 
5 #include "chrome/browser/ui/webui/app_launcher_handler.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/metrics/histogram.h"
11 #include "base/string_number_conversions.h"
12 #include "base/string_split.h"
13 #include "base/string_util.h"
14 #include "base/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "chrome/browser/extensions/apps_promo.h"
17 #include "chrome/browser/extensions/extension_prefs.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/platform_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_list.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/browser/ui/webui/extension_icon_source.h"
25 #include "chrome/browser/ui/webui/shown_sections_handler.h"
26 #include "chrome/common/extensions/extension.h"
27 #include "chrome/common/extensions/extension_constants.h"
28 #include "chrome/common/extensions/extension_icon_set.h"
29 #include "chrome/common/extensions/extension_resource.h"
30 #include "chrome/common/url_constants.h"
31 #include "content/browser/disposition_utils.h"
32 #include "content/browser/tab_contents/tab_contents.h"
33 #include "content/common/notification_service.h"
34 #include "content/common/notification_type.h"
35 #include "googleurl/src/gurl.h"
36 #include "grit/browser_resources.h"
37 #include "grit/generated_resources.h"
38 #include "net/base/escape.h"
39 #include "ui/base/animation/animation.h"
40 #include "webkit/glue/window_open_disposition.h"
41 
42 namespace {
43 
44 // The URL prefixes used by the NTP to signal when the web store or an app
45 // has launched so we can record the proper histogram.
46 const char* kPingLaunchAppByID = "record-app-launch-by-id";
47 const char* kPingLaunchWebStore = "record-webstore-launch";
48 const char* kPingLaunchAppByURL = "record-app-launch-by-url";
49 
50 const UnescapeRule::Type kUnescapeRules =
51     UnescapeRule::NORMAL | UnescapeRule::URL_SPECIAL_CHARS;
52 
ParseLaunchSource(const std::string & launch_source)53 extension_misc::AppLaunchBucket ParseLaunchSource(
54     const std::string& launch_source) {
55   int bucket_num = extension_misc::APP_LAUNCH_BUCKET_INVALID;
56   base::StringToInt(launch_source, &bucket_num);
57   extension_misc::AppLaunchBucket bucket =
58       static_cast<extension_misc::AppLaunchBucket>(bucket_num);
59   CHECK(bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
60   return bucket;
61 }
62 
63 }  // namespace
64 
AppLauncherHandler(ExtensionService * extension_service)65 AppLauncherHandler::AppLauncherHandler(ExtensionService* extension_service)
66     : extensions_service_(extension_service),
67       promo_active_(false),
68       ignore_changes_(false) {
69 }
70 
~AppLauncherHandler()71 AppLauncherHandler::~AppLauncherHandler() {}
72 
73 // static
CreateAppInfo(const Extension * extension,ExtensionPrefs * prefs,DictionaryValue * value)74 void AppLauncherHandler::CreateAppInfo(const Extension* extension,
75                                        ExtensionPrefs* prefs,
76                                        DictionaryValue* value) {
77   bool enabled =
78       prefs->GetExtensionState(extension->id()) != Extension::DISABLED;
79   GURL icon_big =
80       ExtensionIconSource::GetIconURL(extension,
81                                       Extension::EXTENSION_ICON_LARGE,
82                                       ExtensionIconSet::MATCH_EXACTLY,
83                                       !enabled);
84   GURL icon_small =
85       ExtensionIconSource::GetIconURL(extension,
86                                       Extension::EXTENSION_ICON_BITTY,
87                                       ExtensionIconSet::MATCH_BIGGER,
88                                       !enabled);
89 
90   value->Clear();
91   value->SetString("id", extension->id());
92   value->SetString("name", extension->name());
93   value->SetString("description", extension->description());
94   value->SetString("launch_url", extension->GetFullLaunchURL().spec());
95   value->SetString("options_url", extension->options_url().spec());
96   value->SetBoolean("can_uninstall",
97                     Extension::UserMayDisable(extension->location()));
98   value->SetString("icon_big", icon_big.spec());
99   value->SetString("icon_small", icon_small.spec());
100   value->SetInteger("launch_container", extension->launch_container());
101   value->SetInteger("launch_type",
102       prefs->GetLaunchType(extension->id(),
103                                      ExtensionPrefs::LAUNCH_DEFAULT));
104 
105   int app_launch_index = prefs->GetAppLaunchIndex(extension->id());
106   if (app_launch_index == -1) {
107     // Make sure every app has a launch index (some predate the launch index).
108     app_launch_index = prefs->GetNextAppLaunchIndex();
109     prefs->SetAppLaunchIndex(extension->id(), app_launch_index);
110   }
111   value->SetInteger("app_launch_index", app_launch_index);
112 
113   int page_index = prefs->GetPageIndex(extension->id());
114   if (page_index >= 0) {
115     // Only provide a value if one is stored
116     value->SetInteger("page_index", page_index);
117   }
118 }
119 
120 // static
HandlePing(Profile * profile,const std::string & path)121 bool AppLauncherHandler::HandlePing(Profile* profile, const std::string& path) {
122   std::vector<std::string> params;
123   base::SplitString(path, '+', &params);
124 
125   // Check if the user launched an app from the most visited or recently
126   // closed sections.
127   if (kPingLaunchAppByURL == params.at(0)) {
128     CHECK(params.size() == 3);
129     RecordAppLaunchByURL(
130         profile, params.at(1), ParseLaunchSource(params.at(2)));
131     return true;
132   }
133 
134   bool is_web_store_ping = kPingLaunchWebStore == params.at(0);
135   bool is_app_launch_ping = kPingLaunchAppByID == params.at(0);
136 
137   if (!is_web_store_ping && !is_app_launch_ping)
138     return false;
139 
140   CHECK(params.size() >= 2);
141 
142   bool is_promo_active = params.at(1) == "true";
143 
144   // At this point, the user must have used the app launcher, so we hide the
145   // promo if its still displayed.
146   if (is_promo_active) {
147     DCHECK(profile->GetExtensionService());
148     profile->GetExtensionService()->apps_promo()->ExpireDefaultApps();
149   }
150 
151   if (is_web_store_ping) {
152     RecordWebStoreLaunch(is_promo_active);
153   }  else {
154     CHECK(params.size() == 3);
155     RecordAppLaunchByID(is_promo_active, ParseLaunchSource(params.at(2)));
156   }
157 
158   return true;
159 }
160 
Attach(WebUI * web_ui)161 WebUIMessageHandler* AppLauncherHandler::Attach(WebUI* web_ui) {
162   // TODO(arv): Add initialization code to the Apps store etc.
163   return WebUIMessageHandler::Attach(web_ui);
164 }
165 
RegisterMessages()166 void AppLauncherHandler::RegisterMessages() {
167   web_ui_->RegisterMessageCallback("getApps",
168       NewCallback(this, &AppLauncherHandler::HandleGetApps));
169   web_ui_->RegisterMessageCallback("launchApp",
170       NewCallback(this, &AppLauncherHandler::HandleLaunchApp));
171   web_ui_->RegisterMessageCallback("setLaunchType",
172       NewCallback(this, &AppLauncherHandler::HandleSetLaunchType));
173   web_ui_->RegisterMessageCallback("uninstallApp",
174       NewCallback(this, &AppLauncherHandler::HandleUninstallApp));
175   web_ui_->RegisterMessageCallback("hideAppsPromo",
176       NewCallback(this, &AppLauncherHandler::HandleHideAppsPromo));
177   web_ui_->RegisterMessageCallback("createAppShortcut",
178       NewCallback(this, &AppLauncherHandler::HandleCreateAppShortcut));
179   web_ui_->RegisterMessageCallback("reorderApps",
180       NewCallback(this, &AppLauncherHandler::HandleReorderApps));
181   web_ui_->RegisterMessageCallback("setPageIndex",
182       NewCallback(this, &AppLauncherHandler::HandleSetPageIndex));
183   web_ui_->RegisterMessageCallback("promoSeen",
184       NewCallback(this, &AppLauncherHandler::HandlePromoSeen));
185 }
186 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)187 void AppLauncherHandler::Observe(NotificationType type,
188                                  const NotificationSource& source,
189                                  const NotificationDetails& details) {
190   if (ignore_changes_)
191     return;
192 
193   switch (type.value) {
194     case NotificationType::EXTENSION_LOADED:
195     case NotificationType::EXTENSION_UNLOADED:
196     case NotificationType::EXTENSION_LAUNCHER_REORDERED:
197     // The promo may not load until a couple seconds after the first NTP view,
198     // so we listen for the load notification and notify the NTP when ready.
199     case NotificationType::WEB_STORE_PROMO_LOADED:
200       if (web_ui_->tab_contents())
201         HandleGetApps(NULL);
202       break;
203     case NotificationType::PREF_CHANGED: {
204       if (!web_ui_->tab_contents())
205         break;
206 
207       DictionaryValue dictionary;
208       FillAppDictionary(&dictionary);
209       web_ui_->CallJavascriptFunction("appsPrefChangeCallback", dictionary);
210       break;
211     }
212     default:
213       NOTREACHED();
214   }
215 }
216 
FillAppDictionary(DictionaryValue * dictionary)217 void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) {
218   ListValue* list = new ListValue();
219   const ExtensionList* extensions = extensions_service_->extensions();
220   ExtensionList::const_iterator it;
221   for (it = extensions->begin(); it != extensions->end(); ++it) {
222     // Don't include the WebStore and other component apps.
223     // The WebStore launcher gets special treatment in ntp/apps.js.
224     if ((*it)->is_app() && (*it)->location() != Extension::COMPONENT) {
225       DictionaryValue* app_info = new DictionaryValue();
226       CreateAppInfo(*it, extensions_service_->extension_prefs(), app_info);
227       list->Append(app_info);
228     }
229   }
230 
231   extensions = extensions_service_->disabled_extensions();
232   for (it = extensions->begin(); it != extensions->end(); ++it) {
233     if ((*it)->is_app() && (*it)->location() != Extension::COMPONENT) {
234       DictionaryValue* app_info = new DictionaryValue();
235       CreateAppInfo(*it, extensions_service_->extension_prefs(), app_info);
236       list->Append(app_info);
237     }
238   }
239 
240   dictionary->Set("apps", list);
241 
242 #if defined(OS_MACOSX)
243   // App windows are not yet implemented on mac.
244   dictionary->SetBoolean("disableAppWindowLaunch", true);
245   dictionary->SetBoolean("disableCreateAppShortcut", true);
246 #endif
247 
248 #if defined(OS_CHROMEOS)
249   // Making shortcut does not make sense on ChromeOS because it does not have
250   // a desktop.
251   dictionary->SetBoolean("disableCreateAppShortcut", true);
252 #endif
253 
254   dictionary->SetBoolean(
255       "showLauncher",
256       extensions_service_->apps_promo()->ShouldShowAppLauncher(
257           extensions_service_->GetAppIds()));
258 }
259 
FillPromoDictionary(DictionaryValue * dictionary)260 void AppLauncherHandler::FillPromoDictionary(DictionaryValue* dictionary) {
261   dictionary->SetString("promoHeader", AppsPromo::GetPromoHeaderText());
262   dictionary->SetString("promoButton", AppsPromo::GetPromoButtonText());
263   dictionary->SetString("promoLink", AppsPromo::GetPromoLink().spec());
264   dictionary->SetString("promoExpire", AppsPromo::GetPromoExpireText());
265 }
266 
HandleGetApps(const ListValue * args)267 void AppLauncherHandler::HandleGetApps(const ListValue* args) {
268   DictionaryValue dictionary;
269 
270   // Tell the client whether to show the promo for this view. We don't do this
271   // in the case of PREF_CHANGED because:
272   //
273   // a) At that point in time, depending on the pref that changed, it can look
274   //    like the set of apps installed has changed, and we will mark the promo
275   //    expired.
276   // b) Conceptually, it doesn't really make sense to count a
277   //    prefchange-triggered refresh as a promo 'view'.
278   AppsPromo* apps_promo = extensions_service_->apps_promo();
279   PrefService* prefs = web_ui_->GetProfile()->GetPrefs();
280   bool apps_promo_just_expired = false;
281   if (apps_promo->ShouldShowPromo(extensions_service_->GetAppIds(),
282                                   &apps_promo_just_expired)) {
283     // Maximize the apps section on the first promo view.
284     apps_promo->MaximizeAppsIfFirstView();
285     dictionary.SetBoolean("showPromo", true);
286     FillPromoDictionary(&dictionary);
287     promo_active_ = true;
288   } else {
289     dictionary.SetBoolean("showPromo", false);
290     promo_active_ = false;
291   }
292 
293   // If the default apps have just expired (user viewed them too many times with
294   // no interaction), then we uninstall them and focus the recent sites section.
295   if (apps_promo_just_expired) {
296     ignore_changes_ = true;
297     UninstallDefaultApps();
298     ignore_changes_ = false;
299     ShownSectionsHandler::SetShownSection(prefs, THUMB);
300   }
301 
302   FillAppDictionary(&dictionary);
303   web_ui_->CallJavascriptFunction("getAppsCallback", dictionary);
304 
305   // First time we get here we set up the observer so that we can tell update
306   // the apps as they change.
307   if (registrar_.IsEmpty()) {
308     registrar_.Add(this, NotificationType::EXTENSION_LOADED,
309         NotificationService::AllSources());
310     registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
311         NotificationService::AllSources());
312     registrar_.Add(this, NotificationType::EXTENSION_LAUNCHER_REORDERED,
313         NotificationService::AllSources());
314     registrar_.Add(this, NotificationType::WEB_STORE_PROMO_LOADED,
315         NotificationService::AllSources());
316   }
317   if (pref_change_registrar_.IsEmpty()) {
318     pref_change_registrar_.Init(
319         extensions_service_->extension_prefs()->pref_service());
320     pref_change_registrar_.Add(ExtensionPrefs::kExtensionsPref, this);
321   }
322 }
323 
HandleLaunchApp(const ListValue * args)324 void AppLauncherHandler::HandleLaunchApp(const ListValue* args) {
325   std::string extension_id;
326   double source = -1.0;
327   bool alt_key = false;
328   bool ctrl_key = false;
329   bool meta_key = false;
330   bool shift_key = false;
331   double button = 0.0;
332 
333   CHECK(args->GetString(0, &extension_id));
334   CHECK(args->GetDouble(1, &source));
335   if (args->GetSize() > 2) {
336       CHECK(args->GetBoolean(2, &alt_key));
337       CHECK(args->GetBoolean(3, &ctrl_key));
338       CHECK(args->GetBoolean(4, &meta_key));
339       CHECK(args->GetBoolean(5, &shift_key));
340       CHECK(args->GetDouble(6, &button));
341   }
342 
343   extension_misc::AppLaunchBucket launch_bucket =
344       static_cast<extension_misc::AppLaunchBucket>(
345           static_cast<int>(source));
346   CHECK(launch_bucket >= 0 &&
347         launch_bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
348 
349   const Extension* extension =
350       extensions_service_->GetExtensionById(extension_id, false);
351 
352   // Prompt the user to re-enable the application if disabled.
353   if (!extension) {
354     PromptToEnableApp(extension_id);
355     return;
356   }
357 
358   Profile* profile = extensions_service_->profile();
359 
360   // If the user pressed special keys when clicking, override the saved
361   // preference for launch container.
362   bool middle_button = (button == 1.0);
363   WindowOpenDisposition disposition =
364         disposition_utils::DispositionFromClick(middle_button, alt_key,
365                                                 ctrl_key, meta_key, shift_key);
366 
367   if (extension_id != extension_misc::kWebStoreAppId) {
368     RecordAppLaunchByID(promo_active_, launch_bucket);
369     extensions_service_->apps_promo()->ExpireDefaultApps();
370   }
371 
372   if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) {
373     // TODO(jamescook): Proper support for background tabs.
374     Browser::OpenApplication(
375         profile, extension, extension_misc::LAUNCH_TAB, NULL);
376   } else if (disposition == NEW_WINDOW) {
377     // Force a new window open.
378     Browser::OpenApplication(
379             profile, extension, extension_misc::LAUNCH_WINDOW, NULL);
380   } else {
381     // Look at preference to find the right launch container.  If no preference
382     // is set, launch as a regular tab.
383     extension_misc::LaunchContainer launch_container =
384         extensions_service_->extension_prefs()->GetLaunchContainer(
385             extension, ExtensionPrefs::LAUNCH_REGULAR);
386 
387     // To give a more "launchy" experience when using the NTP launcher, we close
388     // it automatically.
389     Browser* browser = BrowserList::GetLastActive();
390     TabContents* old_contents = NULL;
391     if (browser)
392       old_contents = browser->GetSelectedTabContents();
393 
394     TabContents* new_contents = Browser::OpenApplication(
395         profile, extension, launch_container, old_contents);
396 
397     // This will also destroy the handler, so do not perform any actions after.
398     if (new_contents != old_contents && browser->tab_count() > 1)
399       browser->CloseTabContents(old_contents);
400   }
401 
402 }
403 
HandleSetLaunchType(const ListValue * args)404 void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) {
405   std::string extension_id;
406   double launch_type;
407   CHECK(args->GetString(0, &extension_id));
408   CHECK(args->GetDouble(1, &launch_type));
409 
410   const Extension* extension =
411       extensions_service_->GetExtensionById(extension_id, true);
412   CHECK(extension);
413 
414   extensions_service_->extension_prefs()->SetLaunchType(
415       extension_id,
416       static_cast<ExtensionPrefs::LaunchType>(
417           static_cast<int>(launch_type)));
418 }
419 
HandleUninstallApp(const ListValue * args)420 void AppLauncherHandler::HandleUninstallApp(const ListValue* args) {
421   std::string extension_id = UTF16ToUTF8(ExtractStringValue(args));
422   const Extension* extension = extensions_service_->GetExtensionById(
423       extension_id, false);
424   if (!extension)
425     return;
426 
427   if (!Extension::UserMayDisable(extension->location())) {
428     LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable "
429                << "was made. Extension id : " << extension->id();
430     return;
431   }
432   if (!extension_id_prompting_.empty())
433     return;  // Only one prompt at a time.
434 
435   extension_id_prompting_ = extension_id;
436   GetExtensionUninstallDialog()->ConfirmUninstall(this, extension);
437 }
438 
HandleHideAppsPromo(const ListValue * args)439 void AppLauncherHandler::HandleHideAppsPromo(const ListValue* args) {
440   // If the user has intentionally hidden the promotion, we'll uninstall all the
441   // default apps (we know the user hasn't installed any apps on their own at
442   // this point, or the promotion wouldn't have been shown).
443   ignore_changes_ = true;
444   UninstallDefaultApps();
445   extensions_service_->apps_promo()->HidePromo();
446   ignore_changes_ = false;
447   HandleGetApps(NULL);
448 }
449 
HandleCreateAppShortcut(const ListValue * args)450 void AppLauncherHandler::HandleCreateAppShortcut(const ListValue* args) {
451   std::string extension_id;
452   if (!args->GetString(0, &extension_id)) {
453     NOTREACHED();
454     return;
455   }
456 
457   const Extension* extension =
458       extensions_service_->GetExtensionById(extension_id, true);
459   CHECK(extension);
460 
461   Browser* browser = BrowserList::GetLastActive();
462   if (!browser)
463     return;
464   browser->window()->ShowCreateChromeAppShortcutsDialog(
465       browser->profile(), extension);
466 }
467 
HandleReorderApps(const ListValue * args)468 void AppLauncherHandler::HandleReorderApps(const ListValue* args) {
469   CHECK(args->GetSize() == 2);
470 
471   std::string dragged_app_id;
472   ListValue* app_order;
473   CHECK(args->GetString(0, &dragged_app_id));
474   CHECK(args->GetList(1, &app_order));
475 
476   std::vector<std::string> extension_ids;
477   for (size_t i = 0; i < app_order->GetSize(); ++i) {
478     std::string value;
479     if (app_order->GetString(i, &value))
480       extension_ids.push_back(value);
481   }
482 
483   extensions_service_->extension_prefs()->SetAppDraggedByUser(dragged_app_id);
484   extensions_service_->extension_prefs()->SetAppLauncherOrder(extension_ids);
485 }
486 
HandleSetPageIndex(const ListValue * args)487 void AppLauncherHandler::HandleSetPageIndex(const ListValue* args) {
488   std::string extension_id;
489   double page_index;
490   CHECK(args->GetString(0, &extension_id));
491   CHECK(args->GetDouble(1, &page_index));
492 
493   extensions_service_->extension_prefs()->SetPageIndex(extension_id,
494       static_cast<int>(page_index));
495 }
496 
HandlePromoSeen(const ListValue * args)497 void AppLauncherHandler::HandlePromoSeen(const ListValue* args) {
498   UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram,
499                             extension_misc::PROMO_SEEN,
500                             extension_misc::PROMO_BUCKET_BOUNDARY);
501 }
502 
503 // static
RecordWebStoreLaunch(bool promo_active)504 void AppLauncherHandler::RecordWebStoreLaunch(bool promo_active) {
505   UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram,
506                             extension_misc::APP_LAUNCH_NTP_WEBSTORE,
507                             extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
508 
509   if (!promo_active) return;
510 
511   UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram,
512                             extension_misc::PROMO_LAUNCH_WEB_STORE,
513                             extension_misc::PROMO_BUCKET_BOUNDARY);
514 }
515 
516 // static
RecordAppLaunchByID(bool promo_active,extension_misc::AppLaunchBucket bucket)517 void AppLauncherHandler::RecordAppLaunchByID(
518     bool promo_active, extension_misc::AppLaunchBucket bucket) {
519   CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID);
520 
521   UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, bucket,
522                             extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
523 
524   if (!promo_active) return;
525 
526   UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram,
527                             extension_misc::PROMO_LAUNCH_APP,
528                             extension_misc::PROMO_BUCKET_BOUNDARY);
529 }
530 
531 // static
RecordAppLaunchByURL(Profile * profile,std::string escaped_url,extension_misc::AppLaunchBucket bucket)532 void AppLauncherHandler::RecordAppLaunchByURL(
533     Profile* profile,
534     std::string escaped_url,
535     extension_misc::AppLaunchBucket bucket) {
536   CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID);
537 
538   GURL url(UnescapeURLComponent(escaped_url, kUnescapeRules));
539   DCHECK(profile->GetExtensionService());
540   if (!profile->GetExtensionService()->IsInstalledApp(url))
541     return;
542 
543   UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, bucket,
544                             extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
545 }
546 
PromptToEnableApp(const std::string & extension_id)547 void AppLauncherHandler::PromptToEnableApp(const std::string& extension_id) {
548   const Extension* extension =
549       extensions_service_->GetExtensionById(extension_id, true);
550   CHECK(extension);
551 
552   ExtensionPrefs* extension_prefs = extensions_service_->extension_prefs();
553   if (!extension_prefs->DidExtensionEscalatePermissions(extension_id)) {
554     // Enable the extension immediately if its privileges weren't escalated.
555     extensions_service_->EnableExtension(extension_id);
556 
557     // Launch app asynchronously so the image will update.
558     StringValue* app_id = Value::CreateStringValue(extension->id());
559     web_ui_->CallJavascriptFunction("launchAppAfterEnable", *app_id);
560     return;
561   }
562 
563   if (!extension_id_prompting_.empty())
564     return;  // Only one prompt at a time.
565 
566   extension_id_prompting_ = extension_id;
567   GetExtensionInstallUI()->ConfirmReEnable(this, extension);
568 }
569 
ExtensionDialogAccepted()570 void AppLauncherHandler::ExtensionDialogAccepted() {
571   // Do the uninstall work here.
572   DCHECK(!extension_id_prompting_.empty());
573 
574   // The extension can be uninstalled in another window while the UI was
575   // showing. Do nothing in that case.
576   const Extension* extension =
577       extensions_service_->GetExtensionById(extension_id_prompting_, true);
578   if (!extension)
579     return;
580 
581   extensions_service_->UninstallExtension(extension_id_prompting_,
582                                           false /* external_uninstall */, NULL);
583 
584   extension_id_prompting_ = "";
585 }
586 
ExtensionDialogCanceled()587 void AppLauncherHandler::ExtensionDialogCanceled() {
588   const Extension* extension =
589       extensions_service_->GetExtensionById(extension_id_prompting_, true);
590   ExtensionService::RecordPermissionMessagesHistogram(
591       extension, "Extensions.Permissions_ReEnableCancel");
592 
593   extension_id_prompting_ = "";
594 }
595 
InstallUIProceed()596 void AppLauncherHandler::InstallUIProceed() {
597   // Do the re-enable work here.
598   DCHECK(!extension_id_prompting_.empty());
599 
600   // The extension can be uninstalled in another window while the UI was
601   // showing. Do nothing in that case.
602   const Extension* extension =
603       extensions_service_->GetExtensionById(extension_id_prompting_, true);
604   if (!extension)
605     return;
606 
607   extensions_service_->GrantPermissionsAndEnableExtension(extension);
608 
609   // We bounce this off the NTP so the browser can update the apps icon.
610   // If we don't launch the app asynchronously, then the app's disabled
611   // icon disappears but isn't replaced by the enabled icon, making a poor
612   // visual experience.
613   StringValue* app_id = Value::CreateStringValue(extension->id());
614   web_ui_->CallJavascriptFunction("launchAppAfterEnable", *app_id);
615 
616   extension_id_prompting_ = "";
617 }
618 
InstallUIAbort()619 void AppLauncherHandler::InstallUIAbort() {
620   ExtensionDialogCanceled();
621 }
622 
GetExtensionUninstallDialog()623 ExtensionUninstallDialog* AppLauncherHandler::GetExtensionUninstallDialog() {
624   if (!extension_uninstall_dialog_.get()) {
625     extension_uninstall_dialog_.reset(
626         new ExtensionUninstallDialog(web_ui_->GetProfile()));
627   }
628   return extension_uninstall_dialog_.get();
629 }
630 
GetExtensionInstallUI()631 ExtensionInstallUI* AppLauncherHandler::GetExtensionInstallUI() {
632   if (!extension_install_ui_.get()) {
633     extension_install_ui_.reset(
634         new ExtensionInstallUI(web_ui_->GetProfile()));
635   }
636   return extension_install_ui_.get();
637 }
638 
UninstallDefaultApps()639 void AppLauncherHandler::UninstallDefaultApps() {
640   AppsPromo* apps_promo = extensions_service_->apps_promo();
641   const ExtensionIdSet& app_ids = apps_promo->old_default_apps();
642   for (ExtensionIdSet::const_iterator iter = app_ids.begin();
643        iter != app_ids.end(); ++iter) {
644     if (extensions_service_->GetExtensionById(*iter, true))
645       extensions_service_->UninstallExtension(*iter, false, NULL);
646   }
647 }
648