| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/app_launcher_handler.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/metrics/histogram.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/extensions/apps_promo.h" |
| #include "chrome/browser/extensions/extension_prefs.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/webui/extension_icon_source.h" |
| #include "chrome/browser/ui/webui/shown_sections_handler.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/extensions/extension_icon_set.h" |
| #include "chrome/common/extensions/extension_resource.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/browser/disposition_utils.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/common/notification_service.h" |
| #include "content/common/notification_type.h" |
| #include "googleurl/src/gurl.h" |
| #include "grit/browser_resources.h" |
| #include "grit/generated_resources.h" |
| #include "net/base/escape.h" |
| #include "ui/base/animation/animation.h" |
| #include "webkit/glue/window_open_disposition.h" |
| |
| namespace { |
| |
| // The URL prefixes used by the NTP to signal when the web store or an app |
| // has launched so we can record the proper histogram. |
| const char* kPingLaunchAppByID = "record-app-launch-by-id"; |
| const char* kPingLaunchWebStore = "record-webstore-launch"; |
| const char* kPingLaunchAppByURL = "record-app-launch-by-url"; |
| |
| const UnescapeRule::Type kUnescapeRules = |
| UnescapeRule::NORMAL | UnescapeRule::URL_SPECIAL_CHARS; |
| |
| extension_misc::AppLaunchBucket ParseLaunchSource( |
| const std::string& launch_source) { |
| int bucket_num = extension_misc::APP_LAUNCH_BUCKET_INVALID; |
| base::StringToInt(launch_source, &bucket_num); |
| extension_misc::AppLaunchBucket bucket = |
| static_cast<extension_misc::AppLaunchBucket>(bucket_num); |
| CHECK(bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); |
| return bucket; |
| } |
| |
| } // namespace |
| |
| AppLauncherHandler::AppLauncherHandler(ExtensionService* extension_service) |
| : extensions_service_(extension_service), |
| promo_active_(false), |
| ignore_changes_(false) { |
| } |
| |
| AppLauncherHandler::~AppLauncherHandler() {} |
| |
| // static |
| void AppLauncherHandler::CreateAppInfo(const Extension* extension, |
| ExtensionPrefs* prefs, |
| DictionaryValue* value) { |
| bool enabled = |
| prefs->GetExtensionState(extension->id()) != Extension::DISABLED; |
| GURL icon_big = |
| ExtensionIconSource::GetIconURL(extension, |
| Extension::EXTENSION_ICON_LARGE, |
| ExtensionIconSet::MATCH_EXACTLY, |
| !enabled); |
| GURL icon_small = |
| ExtensionIconSource::GetIconURL(extension, |
| Extension::EXTENSION_ICON_BITTY, |
| ExtensionIconSet::MATCH_BIGGER, |
| !enabled); |
| |
| value->Clear(); |
| value->SetString("id", extension->id()); |
| value->SetString("name", extension->name()); |
| value->SetString("description", extension->description()); |
| value->SetString("launch_url", extension->GetFullLaunchURL().spec()); |
| value->SetString("options_url", extension->options_url().spec()); |
| value->SetBoolean("can_uninstall", |
| Extension::UserMayDisable(extension->location())); |
| value->SetString("icon_big", icon_big.spec()); |
| value->SetString("icon_small", icon_small.spec()); |
| value->SetInteger("launch_container", extension->launch_container()); |
| value->SetInteger("launch_type", |
| prefs->GetLaunchType(extension->id(), |
| ExtensionPrefs::LAUNCH_DEFAULT)); |
| |
| int app_launch_index = prefs->GetAppLaunchIndex(extension->id()); |
| if (app_launch_index == -1) { |
| // Make sure every app has a launch index (some predate the launch index). |
| app_launch_index = prefs->GetNextAppLaunchIndex(); |
| prefs->SetAppLaunchIndex(extension->id(), app_launch_index); |
| } |
| value->SetInteger("app_launch_index", app_launch_index); |
| |
| int page_index = prefs->GetPageIndex(extension->id()); |
| if (page_index >= 0) { |
| // Only provide a value if one is stored |
| value->SetInteger("page_index", page_index); |
| } |
| } |
| |
| // static |
| bool AppLauncherHandler::HandlePing(Profile* profile, const std::string& path) { |
| std::vector<std::string> params; |
| base::SplitString(path, '+', ¶ms); |
| |
| // Check if the user launched an app from the most visited or recently |
| // closed sections. |
| if (kPingLaunchAppByURL == params.at(0)) { |
| CHECK(params.size() == 3); |
| RecordAppLaunchByURL( |
| profile, params.at(1), ParseLaunchSource(params.at(2))); |
| return true; |
| } |
| |
| bool is_web_store_ping = kPingLaunchWebStore == params.at(0); |
| bool is_app_launch_ping = kPingLaunchAppByID == params.at(0); |
| |
| if (!is_web_store_ping && !is_app_launch_ping) |
| return false; |
| |
| CHECK(params.size() >= 2); |
| |
| bool is_promo_active = params.at(1) == "true"; |
| |
| // At this point, the user must have used the app launcher, so we hide the |
| // promo if its still displayed. |
| if (is_promo_active) { |
| DCHECK(profile->GetExtensionService()); |
| profile->GetExtensionService()->apps_promo()->ExpireDefaultApps(); |
| } |
| |
| if (is_web_store_ping) { |
| RecordWebStoreLaunch(is_promo_active); |
| } else { |
| CHECK(params.size() == 3); |
| RecordAppLaunchByID(is_promo_active, ParseLaunchSource(params.at(2))); |
| } |
| |
| return true; |
| } |
| |
| WebUIMessageHandler* AppLauncherHandler::Attach(WebUI* web_ui) { |
| // TODO(arv): Add initialization code to the Apps store etc. |
| return WebUIMessageHandler::Attach(web_ui); |
| } |
| |
| void AppLauncherHandler::RegisterMessages() { |
| web_ui_->RegisterMessageCallback("getApps", |
| NewCallback(this, &AppLauncherHandler::HandleGetApps)); |
| web_ui_->RegisterMessageCallback("launchApp", |
| NewCallback(this, &AppLauncherHandler::HandleLaunchApp)); |
| web_ui_->RegisterMessageCallback("setLaunchType", |
| NewCallback(this, &AppLauncherHandler::HandleSetLaunchType)); |
| web_ui_->RegisterMessageCallback("uninstallApp", |
| NewCallback(this, &AppLauncherHandler::HandleUninstallApp)); |
| web_ui_->RegisterMessageCallback("hideAppsPromo", |
| NewCallback(this, &AppLauncherHandler::HandleHideAppsPromo)); |
| web_ui_->RegisterMessageCallback("createAppShortcut", |
| NewCallback(this, &AppLauncherHandler::HandleCreateAppShortcut)); |
| web_ui_->RegisterMessageCallback("reorderApps", |
| NewCallback(this, &AppLauncherHandler::HandleReorderApps)); |
| web_ui_->RegisterMessageCallback("setPageIndex", |
| NewCallback(this, &AppLauncherHandler::HandleSetPageIndex)); |
| web_ui_->RegisterMessageCallback("promoSeen", |
| NewCallback(this, &AppLauncherHandler::HandlePromoSeen)); |
| } |
| |
| void AppLauncherHandler::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| if (ignore_changes_) |
| return; |
| |
| switch (type.value) { |
| case NotificationType::EXTENSION_LOADED: |
| case NotificationType::EXTENSION_UNLOADED: |
| case NotificationType::EXTENSION_LAUNCHER_REORDERED: |
| // The promo may not load until a couple seconds after the first NTP view, |
| // so we listen for the load notification and notify the NTP when ready. |
| case NotificationType::WEB_STORE_PROMO_LOADED: |
| if (web_ui_->tab_contents()) |
| HandleGetApps(NULL); |
| break; |
| case NotificationType::PREF_CHANGED: { |
| if (!web_ui_->tab_contents()) |
| break; |
| |
| DictionaryValue dictionary; |
| FillAppDictionary(&dictionary); |
| web_ui_->CallJavascriptFunction("appsPrefChangeCallback", dictionary); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) { |
| ListValue* list = new ListValue(); |
| const ExtensionList* extensions = extensions_service_->extensions(); |
| ExtensionList::const_iterator it; |
| for (it = extensions->begin(); it != extensions->end(); ++it) { |
| // Don't include the WebStore and other component apps. |
| // The WebStore launcher gets special treatment in ntp/apps.js. |
| if ((*it)->is_app() && (*it)->location() != Extension::COMPONENT) { |
| DictionaryValue* app_info = new DictionaryValue(); |
| CreateAppInfo(*it, extensions_service_->extension_prefs(), app_info); |
| list->Append(app_info); |
| } |
| } |
| |
| extensions = extensions_service_->disabled_extensions(); |
| for (it = extensions->begin(); it != extensions->end(); ++it) { |
| if ((*it)->is_app() && (*it)->location() != Extension::COMPONENT) { |
| DictionaryValue* app_info = new DictionaryValue(); |
| CreateAppInfo(*it, extensions_service_->extension_prefs(), app_info); |
| list->Append(app_info); |
| } |
| } |
| |
| dictionary->Set("apps", list); |
| |
| #if defined(OS_MACOSX) |
| // App windows are not yet implemented on mac. |
| dictionary->SetBoolean("disableAppWindowLaunch", true); |
| dictionary->SetBoolean("disableCreateAppShortcut", true); |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| // Making shortcut does not make sense on ChromeOS because it does not have |
| // a desktop. |
| dictionary->SetBoolean("disableCreateAppShortcut", true); |
| #endif |
| |
| dictionary->SetBoolean( |
| "showLauncher", |
| extensions_service_->apps_promo()->ShouldShowAppLauncher( |
| extensions_service_->GetAppIds())); |
| } |
| |
| void AppLauncherHandler::FillPromoDictionary(DictionaryValue* dictionary) { |
| dictionary->SetString("promoHeader", AppsPromo::GetPromoHeaderText()); |
| dictionary->SetString("promoButton", AppsPromo::GetPromoButtonText()); |
| dictionary->SetString("promoLink", AppsPromo::GetPromoLink().spec()); |
| dictionary->SetString("promoExpire", AppsPromo::GetPromoExpireText()); |
| } |
| |
| void AppLauncherHandler::HandleGetApps(const ListValue* args) { |
| DictionaryValue dictionary; |
| |
| // Tell the client whether to show the promo for this view. We don't do this |
| // in the case of PREF_CHANGED because: |
| // |
| // a) At that point in time, depending on the pref that changed, it can look |
| // like the set of apps installed has changed, and we will mark the promo |
| // expired. |
| // b) Conceptually, it doesn't really make sense to count a |
| // prefchange-triggered refresh as a promo 'view'. |
| AppsPromo* apps_promo = extensions_service_->apps_promo(); |
| PrefService* prefs = web_ui_->GetProfile()->GetPrefs(); |
| bool apps_promo_just_expired = false; |
| if (apps_promo->ShouldShowPromo(extensions_service_->GetAppIds(), |
| &apps_promo_just_expired)) { |
| // Maximize the apps section on the first promo view. |
| apps_promo->MaximizeAppsIfFirstView(); |
| dictionary.SetBoolean("showPromo", true); |
| FillPromoDictionary(&dictionary); |
| promo_active_ = true; |
| } else { |
| dictionary.SetBoolean("showPromo", false); |
| promo_active_ = false; |
| } |
| |
| // If the default apps have just expired (user viewed them too many times with |
| // no interaction), then we uninstall them and focus the recent sites section. |
| if (apps_promo_just_expired) { |
| ignore_changes_ = true; |
| UninstallDefaultApps(); |
| ignore_changes_ = false; |
| ShownSectionsHandler::SetShownSection(prefs, THUMB); |
| } |
| |
| FillAppDictionary(&dictionary); |
| web_ui_->CallJavascriptFunction("getAppsCallback", dictionary); |
| |
| // First time we get here we set up the observer so that we can tell update |
| // the apps as they change. |
| if (registrar_.IsEmpty()) { |
| registrar_.Add(this, NotificationType::EXTENSION_LOADED, |
| NotificationService::AllSources()); |
| registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, |
| NotificationService::AllSources()); |
| registrar_.Add(this, NotificationType::EXTENSION_LAUNCHER_REORDERED, |
| NotificationService::AllSources()); |
| registrar_.Add(this, NotificationType::WEB_STORE_PROMO_LOADED, |
| NotificationService::AllSources()); |
| } |
| if (pref_change_registrar_.IsEmpty()) { |
| pref_change_registrar_.Init( |
| extensions_service_->extension_prefs()->pref_service()); |
| pref_change_registrar_.Add(ExtensionPrefs::kExtensionsPref, this); |
| } |
| } |
| |
| void AppLauncherHandler::HandleLaunchApp(const ListValue* args) { |
| std::string extension_id; |
| double source = -1.0; |
| bool alt_key = false; |
| bool ctrl_key = false; |
| bool meta_key = false; |
| bool shift_key = false; |
| double button = 0.0; |
| |
| CHECK(args->GetString(0, &extension_id)); |
| CHECK(args->GetDouble(1, &source)); |
| if (args->GetSize() > 2) { |
| CHECK(args->GetBoolean(2, &alt_key)); |
| CHECK(args->GetBoolean(3, &ctrl_key)); |
| CHECK(args->GetBoolean(4, &meta_key)); |
| CHECK(args->GetBoolean(5, &shift_key)); |
| CHECK(args->GetDouble(6, &button)); |
| } |
| |
| extension_misc::AppLaunchBucket launch_bucket = |
| static_cast<extension_misc::AppLaunchBucket>( |
| static_cast<int>(source)); |
| CHECK(launch_bucket >= 0 && |
| launch_bucket < extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); |
| |
| const Extension* extension = |
| extensions_service_->GetExtensionById(extension_id, false); |
| |
| // Prompt the user to re-enable the application if disabled. |
| if (!extension) { |
| PromptToEnableApp(extension_id); |
| return; |
| } |
| |
| Profile* profile = extensions_service_->profile(); |
| |
| // If the user pressed special keys when clicking, override the saved |
| // preference for launch container. |
| bool middle_button = (button == 1.0); |
| WindowOpenDisposition disposition = |
| disposition_utils::DispositionFromClick(middle_button, alt_key, |
| ctrl_key, meta_key, shift_key); |
| |
| if (extension_id != extension_misc::kWebStoreAppId) { |
| RecordAppLaunchByID(promo_active_, launch_bucket); |
| extensions_service_->apps_promo()->ExpireDefaultApps(); |
| } |
| |
| if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) { |
| // TODO(jamescook): Proper support for background tabs. |
| Browser::OpenApplication( |
| profile, extension, extension_misc::LAUNCH_TAB, NULL); |
| } else if (disposition == NEW_WINDOW) { |
| // Force a new window open. |
| Browser::OpenApplication( |
| profile, extension, extension_misc::LAUNCH_WINDOW, NULL); |
| } else { |
| // Look at preference to find the right launch container. If no preference |
| // is set, launch as a regular tab. |
| extension_misc::LaunchContainer launch_container = |
| extensions_service_->extension_prefs()->GetLaunchContainer( |
| extension, ExtensionPrefs::LAUNCH_REGULAR); |
| |
| // To give a more "launchy" experience when using the NTP launcher, we close |
| // it automatically. |
| Browser* browser = BrowserList::GetLastActive(); |
| TabContents* old_contents = NULL; |
| if (browser) |
| old_contents = browser->GetSelectedTabContents(); |
| |
| TabContents* new_contents = Browser::OpenApplication( |
| profile, extension, launch_container, old_contents); |
| |
| // This will also destroy the handler, so do not perform any actions after. |
| if (new_contents != old_contents && browser->tab_count() > 1) |
| browser->CloseTabContents(old_contents); |
| } |
| |
| } |
| |
| void AppLauncherHandler::HandleSetLaunchType(const ListValue* args) { |
| std::string extension_id; |
| double launch_type; |
| CHECK(args->GetString(0, &extension_id)); |
| CHECK(args->GetDouble(1, &launch_type)); |
| |
| const Extension* extension = |
| extensions_service_->GetExtensionById(extension_id, true); |
| CHECK(extension); |
| |
| extensions_service_->extension_prefs()->SetLaunchType( |
| extension_id, |
| static_cast<ExtensionPrefs::LaunchType>( |
| static_cast<int>(launch_type))); |
| } |
| |
| void AppLauncherHandler::HandleUninstallApp(const ListValue* args) { |
| std::string extension_id = UTF16ToUTF8(ExtractStringValue(args)); |
| const Extension* extension = extensions_service_->GetExtensionById( |
| extension_id, false); |
| if (!extension) |
| return; |
| |
| if (!Extension::UserMayDisable(extension->location())) { |
| LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable " |
| << "was made. Extension id : " << extension->id(); |
| return; |
| } |
| if (!extension_id_prompting_.empty()) |
| return; // Only one prompt at a time. |
| |
| extension_id_prompting_ = extension_id; |
| GetExtensionUninstallDialog()->ConfirmUninstall(this, extension); |
| } |
| |
| void AppLauncherHandler::HandleHideAppsPromo(const ListValue* args) { |
| // If the user has intentionally hidden the promotion, we'll uninstall all the |
| // default apps (we know the user hasn't installed any apps on their own at |
| // this point, or the promotion wouldn't have been shown). |
| ignore_changes_ = true; |
| UninstallDefaultApps(); |
| extensions_service_->apps_promo()->HidePromo(); |
| ignore_changes_ = false; |
| HandleGetApps(NULL); |
| } |
| |
| void AppLauncherHandler::HandleCreateAppShortcut(const ListValue* args) { |
| std::string extension_id; |
| if (!args->GetString(0, &extension_id)) { |
| NOTREACHED(); |
| return; |
| } |
| |
| const Extension* extension = |
| extensions_service_->GetExtensionById(extension_id, true); |
| CHECK(extension); |
| |
| Browser* browser = BrowserList::GetLastActive(); |
| if (!browser) |
| return; |
| browser->window()->ShowCreateChromeAppShortcutsDialog( |
| browser->profile(), extension); |
| } |
| |
| void AppLauncherHandler::HandleReorderApps(const ListValue* args) { |
| CHECK(args->GetSize() == 2); |
| |
| std::string dragged_app_id; |
| ListValue* app_order; |
| CHECK(args->GetString(0, &dragged_app_id)); |
| CHECK(args->GetList(1, &app_order)); |
| |
| std::vector<std::string> extension_ids; |
| for (size_t i = 0; i < app_order->GetSize(); ++i) { |
| std::string value; |
| if (app_order->GetString(i, &value)) |
| extension_ids.push_back(value); |
| } |
| |
| extensions_service_->extension_prefs()->SetAppDraggedByUser(dragged_app_id); |
| extensions_service_->extension_prefs()->SetAppLauncherOrder(extension_ids); |
| } |
| |
| void AppLauncherHandler::HandleSetPageIndex(const ListValue* args) { |
| std::string extension_id; |
| double page_index; |
| CHECK(args->GetString(0, &extension_id)); |
| CHECK(args->GetDouble(1, &page_index)); |
| |
| extensions_service_->extension_prefs()->SetPageIndex(extension_id, |
| static_cast<int>(page_index)); |
| } |
| |
| void AppLauncherHandler::HandlePromoSeen(const ListValue* args) { |
| UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, |
| extension_misc::PROMO_SEEN, |
| extension_misc::PROMO_BUCKET_BOUNDARY); |
| } |
| |
| // static |
| void AppLauncherHandler::RecordWebStoreLaunch(bool promo_active) { |
| UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, |
| extension_misc::APP_LAUNCH_NTP_WEBSTORE, |
| extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); |
| |
| if (!promo_active) return; |
| |
| UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, |
| extension_misc::PROMO_LAUNCH_WEB_STORE, |
| extension_misc::PROMO_BUCKET_BOUNDARY); |
| } |
| |
| // static |
| void AppLauncherHandler::RecordAppLaunchByID( |
| bool promo_active, extension_misc::AppLaunchBucket bucket) { |
| CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID); |
| |
| UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, bucket, |
| extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); |
| |
| if (!promo_active) return; |
| |
| UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppsPromoHistogram, |
| extension_misc::PROMO_LAUNCH_APP, |
| extension_misc::PROMO_BUCKET_BOUNDARY); |
| } |
| |
| // static |
| void AppLauncherHandler::RecordAppLaunchByURL( |
| Profile* profile, |
| std::string escaped_url, |
| extension_misc::AppLaunchBucket bucket) { |
| CHECK(bucket != extension_misc::APP_LAUNCH_BUCKET_INVALID); |
| |
| GURL url(UnescapeURLComponent(escaped_url, kUnescapeRules)); |
| DCHECK(profile->GetExtensionService()); |
| if (!profile->GetExtensionService()->IsInstalledApp(url)) |
| return; |
| |
| UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, bucket, |
| extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); |
| } |
| |
| void AppLauncherHandler::PromptToEnableApp(const std::string& extension_id) { |
| const Extension* extension = |
| extensions_service_->GetExtensionById(extension_id, true); |
| CHECK(extension); |
| |
| ExtensionPrefs* extension_prefs = extensions_service_->extension_prefs(); |
| if (!extension_prefs->DidExtensionEscalatePermissions(extension_id)) { |
| // Enable the extension immediately if its privileges weren't escalated. |
| extensions_service_->EnableExtension(extension_id); |
| |
| // Launch app asynchronously so the image will update. |
| StringValue* app_id = Value::CreateStringValue(extension->id()); |
| web_ui_->CallJavascriptFunction("launchAppAfterEnable", *app_id); |
| return; |
| } |
| |
| if (!extension_id_prompting_.empty()) |
| return; // Only one prompt at a time. |
| |
| extension_id_prompting_ = extension_id; |
| GetExtensionInstallUI()->ConfirmReEnable(this, extension); |
| } |
| |
| void AppLauncherHandler::ExtensionDialogAccepted() { |
| // Do the uninstall work here. |
| DCHECK(!extension_id_prompting_.empty()); |
| |
| // The extension can be uninstalled in another window while the UI was |
| // showing. Do nothing in that case. |
| const Extension* extension = |
| extensions_service_->GetExtensionById(extension_id_prompting_, true); |
| if (!extension) |
| return; |
| |
| extensions_service_->UninstallExtension(extension_id_prompting_, |
| false /* external_uninstall */, NULL); |
| |
| extension_id_prompting_ = ""; |
| } |
| |
| void AppLauncherHandler::ExtensionDialogCanceled() { |
| const Extension* extension = |
| extensions_service_->GetExtensionById(extension_id_prompting_, true); |
| ExtensionService::RecordPermissionMessagesHistogram( |
| extension, "Extensions.Permissions_ReEnableCancel"); |
| |
| extension_id_prompting_ = ""; |
| } |
| |
| void AppLauncherHandler::InstallUIProceed() { |
| // Do the re-enable work here. |
| DCHECK(!extension_id_prompting_.empty()); |
| |
| // The extension can be uninstalled in another window while the UI was |
| // showing. Do nothing in that case. |
| const Extension* extension = |
| extensions_service_->GetExtensionById(extension_id_prompting_, true); |
| if (!extension) |
| return; |
| |
| extensions_service_->GrantPermissionsAndEnableExtension(extension); |
| |
| // We bounce this off the NTP so the browser can update the apps icon. |
| // If we don't launch the app asynchronously, then the app's disabled |
| // icon disappears but isn't replaced by the enabled icon, making a poor |
| // visual experience. |
| StringValue* app_id = Value::CreateStringValue(extension->id()); |
| web_ui_->CallJavascriptFunction("launchAppAfterEnable", *app_id); |
| |
| extension_id_prompting_ = ""; |
| } |
| |
| void AppLauncherHandler::InstallUIAbort() { |
| ExtensionDialogCanceled(); |
| } |
| |
| ExtensionUninstallDialog* AppLauncherHandler::GetExtensionUninstallDialog() { |
| if (!extension_uninstall_dialog_.get()) { |
| extension_uninstall_dialog_.reset( |
| new ExtensionUninstallDialog(web_ui_->GetProfile())); |
| } |
| return extension_uninstall_dialog_.get(); |
| } |
| |
| ExtensionInstallUI* AppLauncherHandler::GetExtensionInstallUI() { |
| if (!extension_install_ui_.get()) { |
| extension_install_ui_.reset( |
| new ExtensionInstallUI(web_ui_->GetProfile())); |
| } |
| return extension_install_ui_.get(); |
| } |
| |
| void AppLauncherHandler::UninstallDefaultApps() { |
| AppsPromo* apps_promo = extensions_service_->apps_promo(); |
| const ExtensionIdSet& app_ids = apps_promo->old_default_apps(); |
| for (ExtensionIdSet::const_iterator iter = app_ids.begin(); |
| iter != app_ids.end(); ++iter) { |
| if (extensions_service_->GetExtensionById(*iter, true)) |
| extensions_service_->UninstallExtension(*iter, false, NULL); |
| } |
| } |