| Index: chrome/browser/ui/webui/ntp/app_launcher_handler.cc
|
| diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
|
| index ae106c188ec8b38f3b7c26ec2a8c1ea3f0d6c914..1494922f5aa6ed4025c609c3560adecdd5bd62cd 100644
|
| --- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
|
| +++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
|
| @@ -4,9 +4,14 @@
|
|
|
| #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h"
|
|
|
| +#include <algorithm>
|
| #include <string>
|
| #include <vector>
|
|
|
| +
|
| +#include "base/json/json_writer.h"
|
| +
|
| +
|
| #include "base/auto_reset.h"
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| @@ -32,12 +37,13 @@
|
| #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
|
| #include "chrome/browser/ui/webui/web_ui_util.h"
|
| #include "chrome/common/chrome_notification_types.h"
|
| -#include "chrome/common/extensions/extension.h"
|
| #include "chrome/common/extensions/extension_constants.h"
|
| +#include "chrome/common/extensions/extension.h"
|
| #include "chrome/common/extensions/extension_icon_set.h"
|
| #include "chrome/common/extensions/extension_resource.h"
|
| #include "chrome/common/favicon_url.h"
|
| #include "chrome/common/pref_names.h"
|
| +#include "chrome/common/string_ordinal.h"
|
| #include "chrome/common/url_constants.h"
|
| #include "chrome/common/web_apps.h"
|
| #include "content/browser/webui/web_ui.h"
|
| @@ -67,6 +73,29 @@ extension_misc::AppLaunchBucket ParseLaunchSource(
|
| return bucket;
|
| }
|
|
|
| +enum PageListPrefMigrationUMABuckets {
|
| + NONE_REQUIRED = 0,
|
| + LIST_TO_DICTIONARY = 1,
|
| + BOUNDARY_BUCKET,
|
| +};
|
| +
|
| +// Returns whether the original ordinal was valid.
|
| +static void EnsurePageOrdinalIsValid(const Extension* extension,
|
| + ExtensionPrefs* ext_prefs) {
|
| + DCHECK(extension && ext_prefs);
|
| + StringOrdinal page_ordinal = ext_prefs->GetPageOrdinal(extension->id());
|
| +
|
| + // Make sure every app has a page ordinal (some predate the page ordinal).
|
| + if (!page_ordinal.IsValid()) {
|
| + // If the ordinal lacking app is the webstore, put it on the first page.
|
| + page_ordinal = extension->id() == extension_misc::kWebStoreAppId ?
|
| + ext_prefs->CreateFirstAppPageOrdinal() :
|
| + ext_prefs->GetNaturalAppPageOrdinal();
|
| + LOG(ERROR) << "extension(" << extension->id() << ") didn't have a valid page ordinal! Creating one for it as " << page_ordinal.ToString();
|
| + ext_prefs->SetPageOrdinal(extension->id(), page_ordinal);
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| AppLauncherHandler::AppInstallInfo::AppInstallInfo() {}
|
| @@ -77,7 +106,8 @@ AppLauncherHandler::AppLauncherHandler(ExtensionService* extension_service)
|
| : extension_service_(extension_service),
|
| ignore_changes_(false),
|
| attempted_bookmark_app_install_(false),
|
| - has_loaded_apps_(false) {
|
| + has_loaded_apps_(false),
|
| + uninstall_from_page_(false) {
|
| }
|
|
|
| AppLauncherHandler::~AppLauncherHandler() {}
|
| @@ -110,6 +140,9 @@ void AppLauncherHandler::CreateAppInfo(const Extension* extension,
|
| const AppNotification* notification,
|
| ExtensionService* service,
|
| DictionaryValue* value) {
|
| + // Change this check if you'd like to pass a non-empty dictionary here.
|
| + DCHECK(value && value->empty());
|
| +
|
| bool enabled = service->IsExtensionEnabled(extension->id()) &&
|
| !service->GetTerminatedExtension(extension->id());
|
| bool icon_big_exists = true;
|
| @@ -156,7 +189,6 @@ void AppLauncherHandler::CreateAppInfo(const Extension* extension,
|
| extension->id() == extension_misc::kWebStoreAppId);
|
|
|
| if (extension->HasAPIPermission(ExtensionAPIPermission::kAppNotifications)) {
|
| - ExtensionPrefs* prefs = service->extension_prefs();
|
| value->SetBoolean("notifications_disabled",
|
| prefs->IsAppNotificationDisabled(extension->id()));
|
| }
|
| @@ -164,19 +196,9 @@ void AppLauncherHandler::CreateAppInfo(const Extension* extension,
|
| if (notification)
|
| value->Set("notification", SerializeNotification(*notification));
|
|
|
| + EnsurePageOrdinalIsValid(extension, prefs);
|
| StringOrdinal page_ordinal = prefs->GetPageOrdinal(extension->id());
|
| - if (!page_ordinal.IsValid()) {
|
| - // Make sure every app has a page ordinal (some predate the page ordinal).
|
| - // The webstore app should be on the first page.
|
| - page_ordinal = extension->id() == extension_misc::kWebStoreAppId ?
|
| - prefs->CreateFirstAppPageOrdinal() : prefs->GetNaturalAppPageOrdinal();
|
| - prefs->SetPageOrdinal(extension->id(), page_ordinal);
|
| - }
|
| - // We convert the page_ordinal to an integer because the pages are referenced
|
| - // from within an array in the javascript code, which can't be easily
|
| - // changed to handle the StringOrdinal values, so we do the conversion here.
|
| - int page_index = prefs->PageStringOrdinalAsInteger(page_ordinal);
|
| - value->SetInteger("page_index", page_index >= 0 ? page_index : 0);
|
| + value->SetString("page_ordinal", page_ordinal.ToString());
|
|
|
| StringOrdinal app_launch_ordinal =
|
| prefs->GetAppLaunchOrdinal(extension->id());
|
| @@ -196,6 +218,9 @@ void AppLauncherHandler::RegisterMessages() {
|
| registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_NTP,
|
| content::Source<WebContents>(web_ui()->web_contents()));
|
|
|
| + web_ui()->RegisterMessageCallback("deleteEmptyAppsPage",
|
| + base::Bind(&AppLauncherHandler::HandleDeleteEmptyAppsPage,
|
| + base::Unretained(this)));
|
| web_ui()->RegisterMessageCallback("getApps",
|
| base::Bind(&AppLauncherHandler::HandleGetApps,
|
| base::Unretained(this)));
|
| @@ -223,8 +248,8 @@ void AppLauncherHandler::RegisterMessages() {
|
| web_ui()->RegisterMessageCallback("promoSeen",
|
| base::Bind(&AppLauncherHandler::HandlePromoSeen,
|
| base::Unretained(this)));
|
| - web_ui()->RegisterMessageCallback("saveAppPageName",
|
| - base::Bind(&AppLauncherHandler::HandleSaveAppPageName,
|
| + web_ui()->RegisterMessageCallback("saveAppsPageName",
|
| + base::Bind(&AppLauncherHandler::HandleSaveAppsPageName,
|
| base::Unretained(this)));
|
| web_ui()->RegisterMessageCallback("generateAppForLink",
|
| base::Bind(&AppLauncherHandler::HandleGenerateAppForLink,
|
| @@ -302,19 +327,41 @@ void AppLauncherHandler::Observe(int type,
|
| content::Details<UnloadedExtensionInfo>(details)->reason ==
|
| extension_misc::UNLOAD_REASON_UNINSTALL));
|
| if (app_info.get()) {
|
| + scoped_ptr<base::FundamentalValue> from_page(
|
| + Value::CreateBooleanValue(uninstall_from_page_));
|
| web_ui()->CallJavascriptFunction(
|
| - "ntp4.appRemoved", *app_info, *uninstall_value);
|
| + "ntp4.appRemoved", *app_info, *uninstall_value, *from_page);
|
| }
|
| break;
|
| }
|
| - case chrome::NOTIFICATION_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 chrome::NOTIFICATION_WEB_STORE_PROMO_LOADED:
|
| - // TODO(estade): try to get rid of this inefficient operation.
|
| + case chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED: {
|
| + DictionaryValue pages;
|
| + const ExtensionSet* extensions = extension_service_->extensions();
|
| + ExtensionPrefs* prefs = extension_service_->extension_prefs();
|
| + for (ExtensionSet::const_iterator it = extensions->begin();
|
| + it != extensions->end(); ++it) {
|
| + if (!IsAppExcludedFromList(*it)) {
|
| + std::string page_ordinal_string =
|
| + prefs->GetPageOrdinal((*it)->id()).ToString();
|
| + if (!pages.HasKey(page_ordinal_string))
|
| + pages.Set(page_ordinal_string, new DictionaryValue());
|
| + DictionaryValue* page;
|
| + CHECK(pages.GetDictionary(page_ordinal_string, &page));
|
| + page->SetString(prefs->GetAppLaunchOrdinal((*it)->id()).ToString(),
|
| + (*it)->id());
|
| + }
|
| + }
|
| + web_ui()->CallJavascriptFunction("ntp4.appsReordered", pages);
|
| + break;
|
| + }
|
| + case chrome::NOTIFICATION_WEB_STORE_PROMO_LOADED: {
|
| + // 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.
|
| HandleGetApps(NULL);
|
| break;
|
| + }
|
| case chrome::NOTIFICATION_PREF_CHANGED: {
|
| + LOG(ERROR) << "Pref changed!";
|
| DictionaryValue dictionary;
|
| FillAppDictionary(&dictionary);
|
| web_ui()->CallJavascriptFunction("appsPrefChangeCallback", dictionary);
|
| @@ -337,87 +384,179 @@ void AppLauncherHandler::Observe(int type,
|
| }
|
| }
|
|
|
| -void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) {
|
| - // CreateAppInfo and ClearPageOrdinal can change the extension prefs.
|
| - AutoReset<bool> auto_reset(&ignore_changes_, true);
|
| -
|
| - ListValue* list = new ListValue();
|
| - const ExtensionSet* extensions = extension_service_->extensions();
|
| +void AppLauncherHandler::RemoveNonAppExtensionOrdinals(
|
| + const ExtensionSet* extensions) {
|
| ExtensionSet::const_iterator it;
|
| for (it = extensions->begin(); it != extensions->end(); ++it) {
|
| const Extension* extension = *it;
|
| - if (!IsAppExcludedFromList(extension)) {
|
| - DictionaryValue* app_info = GetAppInfo(extension);
|
| - list->Append(app_info);
|
| - } else {
|
| - // This is necessary because in some previous versions of chrome, we set a
|
| - // page index for non-app extensions. Old profiles can persist this error,
|
| - // and this fixes it. This caused GetNaturalAppPageIndex() to break
|
| - // (see http://crbug.com/98325) before it was an ordinal value.
|
| - ExtensionPrefs* prefs = extension_service_->extension_prefs();
|
| - if (prefs->GetPageOrdinal(extension->id()).IsValid())
|
| - prefs->ClearPageOrdinal(extension->id());
|
| + // Ensure any previous non-app extensions aren't showing / don't have a page
|
| + // ordinal or migrated page index. This is necessary because in some
|
| + // previous versions of chrome, we set a page index for non-app extensions.
|
| + // Old profiles can persist this error, and this fixes it. This caused
|
| + // GetNaturalAppPageIndex() to break (see http://crbug.com/98325) before it
|
| + // was an ordinal value.
|
| + if (IsAppExcludedFromList(extension)) {
|
| + ExtensionPrefs* ext_prefs = extension_service_->extension_prefs();
|
| + if (ext_prefs->GetPageOrdinal(extension->id()).IsValid())
|
| + ext_prefs->ClearPageOrdinal(extension->id());
|
| }
|
| }
|
| +}
|
|
|
| - extensions = extension_service_->disabled_extensions();
|
| - for (it = extensions->begin(); it != extensions->end(); ++it) {
|
| - if (!IsAppExcludedFromList(*it)) {
|
| - DictionaryValue* app_info = new DictionaryValue();
|
| - CreateAppInfo(*it,
|
| - NULL,
|
| - extension_service_,
|
| - app_info);
|
| - list->Append(app_info);
|
| +void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) {
|
| + // CreateAppInfo and ClearPageOrdinal can change the extension prefs.
|
| + AutoReset<bool> auto_reset(&ignore_changes_, true);
|
| +
|
| + RemoveNonAppExtensionOrdinals(extension_service_->extensions());
|
| +
|
| + // Start NULL, CHECK() that it's not NULL after being populated with names.
|
| + DictionaryValue* apps_page_names = NULL;
|
| +
|
| + // Get all the existing apps' page_ordinals, de-duped and sorted, and make
|
| + // sure we've got page names for all the existing pages (or populate with
|
| + // default). If there's a page without any apps on it, delete that pref.
|
| + scoped_ptr<std::vector<std::string> > page_ordinals(PageOrdinalsFromApps());
|
| +
|
| + // Most of the time we probably won't need to update, so let's only do this
|
| + // when something changes (during migration or an edge case like mentioned
|
| + // above when the page names pref doesn't match the apps' info).
|
| + bool update_required = false;
|
| +
|
| + // If the legacy preference exists, migrate it from a list to a dictionary.
|
| + // TODO(dbeam): Remove migration when UMA hits sufficiently small number.
|
| + PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
|
| + if (prefs->HasPrefPath(prefs::kNTPAppsPageNamesOld)) {
|
| + LOG(ERROR) << "Migrating from list to dict for page names required!";
|
| + apps_page_names = new DictionaryValue();
|
| + scoped_ptr<ListValue> apps_page_names_list(
|
| + prefs->GetList(prefs::kNTPAppsPageNamesOld)->DeepCopy());
|
| + string16 default_page_name(
|
| + l10n_util::GetStringUTF16(IDS_APP_DEFAULT_PAGE_NAME));
|
| + for (size_t i = 0; i < page_ordinals->size(); ++i) {
|
| + if (i >= apps_page_names_list->GetSize()) {
|
| + apps_page_names->Set(page_ordinals->at(i),
|
| + Value::CreateStringValue(default_page_name));
|
| + } else {
|
| + Value* name;
|
| + CHECK(apps_page_names_list->Get(i, &name));
|
| + apps_page_names->Set(page_ordinals->at(i), name->DeepCopy());
|
| + }
|
| }
|
| + // If there were extra entries just ignore, we're deleting whole preference.
|
| + prefs->ClearPref(prefs::kNTPAppsPageNamesOld);
|
| + update_required = true;
|
| + UMA_HISTOGRAM_ENUMERATION("NewTabPage.PageNamesPrefMigration",
|
| + LIST_TO_DICTIONARY, BOUNDARY_BUCKET);
|
| + } else {
|
| + LOG(ERROR) << "No migration required!";
|
| + apps_page_names =
|
| + prefs->GetDictionary(prefs::kNTPAppsPageNames)->DeepCopy();
|
| + update_required = SyncPageNamesToApps(apps_page_names);
|
| + // Log this to more easily track how many folks are done migrating prefs.
|
| + UMA_HISTOGRAM_ENUMERATION("NewTabPage.PageNamesPrefMigration",
|
| + NONE_REQUIRED, BOUNDARY_BUCKET);
|
| + }
|
| + // There should always be something in this pref (as there should always be at
|
| + // least one app which requires a page to live on).
|
| + CHECK(apps_page_names && !apps_page_names->empty());
|
| +
|
| + if (update_required) {
|
| + LOG(ERROR) << "Something about page names changed, updating!";
|
| + scoped_ptr<DictionaryValue> page_names_copy(apps_page_names->DeepCopy());
|
| + DictionaryPrefUpdate update(prefs, prefs::kNTPAppsPageNames);
|
| + update.Get()->Swap(page_names_copy.get());
|
| }
|
|
|
| - extensions = extension_service_->terminated_extensions();
|
| - for (it = extensions->begin(); it != extensions->end(); ++it) {
|
| + std::string json;
|
| + base::JSONWriter::Write(apps_page_names, false, &json);
|
| + LOG(ERROR) << apps_page_names->size() << ", " << json;
|
| +
|
| + // Create a dictionary structured by pages and app ordinals, like this:
|
| + // {
|
| + // "<page_ordinal>": {
|
| + // "name": <page_name>,
|
| + // "apps": {
|
| + // <app_launch_ordinal>": { <app_info> },
|
| + // ...
|
| + // }
|
| + // ...
|
| + // },
|
| + // ...
|
| + // }
|
| + DictionaryValue* apps_pages = new DictionaryValue();
|
| +
|
| + scoped_ptr<const ExtensionSet> installed_extensions(
|
| + extension_service_->GetAllInstalledExtensions());
|
| +
|
| + for (ExtensionSet::const_iterator it = installed_extensions->begin();
|
| + it != installed_extensions->end(); ++it) {
|
| if (!IsAppExcludedFromList(*it)) {
|
| DictionaryValue* app_info = new DictionaryValue();
|
| - CreateAppInfo(*it,
|
| - NULL,
|
| - extension_service_,
|
| - app_info);
|
| - list->Append(app_info);
|
| - }
|
| - }
|
| + CreateAppInfo(*it, NULL, extension_service_, app_info);
|
|
|
| - dictionary->Set("apps", list);
|
| + std::string page_ordinal;
|
| + DCHECK(app_info->GetString("page_ordinal", &page_ordinal));
|
| + LOG(ERROR) << "App had page_ordinal: " << page_ordinal;
|
|
|
| - // TODO(estade): remove these settings when the old NTP is removed. The new
|
| - // NTP does it in js.
|
| -#if defined(OS_MACOSX)
|
| - // App windows are not yet implemented on mac.
|
| - dictionary->SetBoolean("disableAppWindowLaunch", true);
|
| - dictionary->SetBoolean("disableCreateAppShortcut", true);
|
| -#endif
|
| + if (!apps_pages->HasKey(page_ordinal))
|
| + apps_pages->Set(page_ordinal, new DictionaryValue());
|
|
|
| -#if defined(OS_CHROMEOS)
|
| - // Making shortcut does not make sense on ChromeOS because it does not have
|
| - // a desktop.
|
| - dictionary->SetBoolean("disableCreateAppShortcut", true);
|
| -#endif
|
| + DictionaryValue* page;
|
| + DCHECK(apps_pages->GetDictionary(page_ordinal, &page));
|
|
|
| - dictionary->SetBoolean(
|
| - "showLauncher",
|
| - extension_service_->apps_promo()->ShouldShowAppLauncher(
|
| - extension_service_->GetAppIds()));
|
| + if (!page->HasKey("apps"))
|
| + page->Set("apps", new DictionaryValue());
|
|
|
| - PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
|
| - const ListValue* app_page_names = prefs->GetList(prefs::kNTPAppPageNames);
|
| - if (!app_page_names || !app_page_names->GetSize()) {
|
| - ListPrefUpdate update(prefs, prefs::kNTPAppPageNames);
|
| - ListValue* list = update.Get();
|
| - list->Set(0, Value::CreateStringValue(
|
| - l10n_util::GetStringUTF16(IDS_APP_DEFAULT_PAGE_NAME)));
|
| - dictionary->Set("appPageNames",
|
| - static_cast<ListValue*>(list->DeepCopy()));
|
| - } else {
|
| - dictionary->Set("appPageNames",
|
| - static_cast<ListValue*>(app_page_names->DeepCopy()));
|
| + if (!page->HasKey("name")) {
|
| + std::string page_name;
|
| + DCHECK(apps_page_names->GetString(page_ordinal, &page_name));
|
| + page->Set("name", Value::CreateStringValue(page_name));
|
| + }
|
| +
|
| + DictionaryValue* apps;
|
| + DCHECK(page->GetDictionary("apps", &apps));
|
| +
|
| + std::string app_launch_ordinal;
|
| + DCHECK(app_info->GetString("app_launch_ordinal", &app_launch_ordinal));
|
| +
|
| + DCHECK(!apps->HasKey(app_launch_ordinal));
|
| + apps->Set(app_launch_ordinal, app_info);
|
| + }
|
| }
|
| + CHECK(apps_pages->size() >= 1);
|
| + dictionary->Set("appsPages", apps_pages);
|
| +}
|
| +
|
| +bool AppLauncherHandler::SyncPageNamesToApps(DictionaryValue* page_names) {
|
| + string16 default_page_name =
|
| + l10n_util::GetStringUTF16(IDS_APP_DEFAULT_PAGE_NAME);
|
| + bool value_changed = false;
|
| + // Get all the unique page ordinals that the current set of apps has and if no
|
| + // page name exists for that page yet, create one with the default value.
|
| + scoped_ptr<std::vector<std::string> > ordinals(PageOrdinalsFromApps());
|
| + for (std::vector<std::string>::const_iterator it = ordinals->begin();
|
| + it != ordinals->end(); ++it) {
|
| + if (!page_names->HasKey(*it)) {
|
| + LOG(ERROR) << "Found new page ordinal, " << *it << ", setting it to default page name! (" << default_page_name << ")";
|
| + page_names->SetString(*it, default_page_name);
|
| + value_changed = true;
|
| + }
|
| + }
|
| + // Make a copy to avoid invalidating our iterator if a key is deleted.
|
| + scoped_ptr<DictionaryValue> page_names_copy(page_names->DeepCopy());
|
| + // Look for extraneous pages that no longer have apps on them.
|
| + for (DictionaryValue::key_iterator it = page_names->begin_keys();
|
| + it != page_names->end_keys(); ++it) {
|
| + if (std::find(ordinals->begin(), ordinals->end(), *it) == ordinals->end()) {
|
| + LOG(ERROR) << "Found left over page ordinal, " << *it << ", deleting!";
|
| + DCHECK(page_names_copy->Remove(*it, NULL));
|
| + value_changed = true;
|
| + }
|
| + }
|
| + // Swap with the possibly affected dictionary.
|
| + page_names->Swap(page_names_copy.get());
|
| + // Tell the caller whether anything changed in this method.
|
| + return value_changed;
|
| }
|
|
|
| DictionaryValue* AppLauncherHandler::GetAppInfo(const Extension* extension) {
|
| @@ -433,6 +572,23 @@ DictionaryValue* AppLauncherHandler::GetAppInfo(const Extension* extension) {
|
| return app_info;
|
| }
|
|
|
| +std::vector<std::string>* AppLauncherHandler::PageOrdinalsFromApps() {
|
| + std::vector<std::string>* pages = new std::vector<std::string>();
|
| + const ExtensionSet* extensions = extension_service_->extensions();
|
| + ExtensionPrefs* ext_prefs = extension_service_->extension_prefs();
|
| + for (ExtensionSet::const_iterator it = extensions->begin();
|
| + it != extensions->end(); ++it) {
|
| + if (!IsAppExcludedFromList(*it)) {
|
| + EnsurePageOrdinalIsValid(*it, ext_prefs);
|
| + std::string ordinal(ext_prefs->GetPageOrdinal((*it)->id()).ToString());
|
| + if (std::find(pages->begin(), pages->end(), ordinal) == pages->end())
|
| + pages->push_back(ordinal);
|
| + }
|
| + }
|
| + std::sort(pages->begin(), pages->end());
|
| + return pages;
|
| +}
|
| +
|
| void AppLauncherHandler::FillPromoDictionary(DictionaryValue* dictionary) {
|
| AppsPromo::PromoData data = AppsPromo::GetPromo();
|
| dictionary->SetString("promoHeader", data.header);
|
| @@ -442,6 +598,38 @@ void AppLauncherHandler::FillPromoDictionary(DictionaryValue* dictionary) {
|
| dictionary->SetString("promoExpire", data.expire);
|
| }
|
|
|
| +void AppLauncherHandler::HandleDeleteEmptyAppsPage(const ListValue* args) {
|
| + std::string page_ordinal;
|
| + CHECK(args->GetString(0, &page_ordinal));
|
| +
|
| + AutoReset<bool> auto_reset(&ignore_changes_, true);
|
| +
|
| + PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
|
| + const DictionaryValue* dict = prefs->GetDictionary(prefs::kNTPAppsPageNames);
|
| + std::string value;
|
| + DCHECK(dict->GetString(page_ordinal, &value));
|
| +
|
| + LOG(ERROR) << "Its value is: " << value;
|
| +
|
| + // Ensure the page we're deleting is empty.
|
| + scoped_ptr<std::vector<std::string> > pages(PageOrdinalsFromApps());
|
| + DCHECK(std::find(pages->begin(), pages->end(), page_ordinal) == pages->end());
|
| +
|
| + DictionaryPrefUpdate update(prefs, prefs::kNTPAppsPageNames);
|
| + DCHECK(update.Get()->Remove(page_ordinal, NULL));
|
| +
|
| + dict = prefs->GetDictionary(prefs::kNTPAppsPageNames);
|
| +
|
| + std::string json;
|
| + base::JSONWriter::Write(dict, false, &json);
|
| + LOG(ERROR) << "now is: " << dict->size() << ", " << json;
|
| +}
|
| +
|
| +void AppLauncherHandler::CleanupAfterUninstall() {
|
| + uninstall_from_page_ = false;
|
| + extension_id_prompting_ = "";
|
| +}
|
| +
|
| void AppLauncherHandler::HandleGetApps(const ListValue* args) {
|
| DictionaryValue dictionary;
|
|
|
| @@ -482,7 +670,7 @@ void AppLauncherHandler::HandleGetApps(const ListValue* args) {
|
| pref_change_registrar_.Init(
|
| extension_service_->extension_prefs()->pref_service());
|
| pref_change_registrar_.Add(ExtensionPrefs::kExtensionsPref, this);
|
| - pref_change_registrar_.Add(prefs::kNTPAppPageNames, this);
|
| + pref_change_registrar_.Add(prefs::kNTPAppsPageNames, this);
|
|
|
| registrar_.Add(this, chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED,
|
| content::Source<Profile>(profile));
|
| @@ -615,6 +803,9 @@ void AppLauncherHandler::HandleUninstallApp(const ListValue* args) {
|
| AutoReset<bool> auto_reset(&ignore_changes_, true);
|
| ExtensionUninstallAccepted();
|
| } else {
|
| + // We don't use an AutoReset<bool> here as the uninstall dialog runs in a
|
| + // different thread so it's not sync.
|
| + uninstall_from_page_ = true;
|
| GetExtensionUninstallDialog()->ConfirmUninstall(extension);
|
| }
|
| }
|
| @@ -696,18 +887,31 @@ void AppLauncherHandler::HandlePromoSeen(const ListValue* args) {
|
| extension_misc::PROMO_BUCKET_BOUNDARY);
|
| }
|
|
|
| -void AppLauncherHandler::HandleSaveAppPageName(const ListValue* args) {
|
| +void AppLauncherHandler::HandleSaveAppsPageName(const ListValue* args) {
|
| string16 name;
|
| CHECK(args->GetString(0, &name));
|
|
|
| - double page_index;
|
| - CHECK(args->GetDouble(1, &page_index));
|
| + double page_index_double;
|
| + CHECK(args->GetDouble(1, &page_index_double));
|
| + size_t page_index = static_cast<size_t>(page_index_double);
|
| +
|
| + bool should_notify;
|
| + CHECK(args->GetBoolean(2, &should_notify));
|
| +
|
| + // There is logic within ExtensionPrefs::PageIntegerAsStringOrdinal() to give
|
| + // us the next page ordinal if we're requesting an index that's greater than
|
| + // what already exists (but only if it's +1 larger, which is fine for us).
|
| + ExtensionPrefs* ext_prefs = extension_service_->extension_prefs();
|
| + StringOrdinal page_ordinal(ext_prefs->PageIntegerAsStringOrdinal(page_index));
|
| + CHECK(page_ordinal.IsValid());
|
| +
|
| + // Don't ignore changes in same cases when we need to propagate the page
|
| + // ordinal to the JS.
|
| + AutoReset<bool> auto_reset(&ignore_changes_, !should_notify);
|
|
|
| - AutoReset<bool> auto_reset(&ignore_changes_, true);
|
| PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
|
| - ListPrefUpdate update(prefs, prefs::kNTPAppPageNames);
|
| - ListValue* list = update.Get();
|
| - list->Set(static_cast<size_t>(page_index), Value::CreateStringValue(name));
|
| + DictionaryPrefUpdate update(prefs, prefs::kNTPAppsPageNames);
|
| + update.Get()->Set(page_ordinal.ToString(), Value::CreateStringValue(name));
|
| }
|
|
|
| void AppLauncherHandler::HandleGenerateAppForLink(const ListValue* args) {
|
| @@ -829,7 +1033,9 @@ void AppLauncherHandler::SetAppToBeHighlighted() {
|
| // static
|
| void AppLauncherHandler::RegisterUserPrefs(PrefService* pref_service) {
|
| // TODO(csharp): We will want this to be a syncable preference instead.
|
| - pref_service->RegisterListPref(prefs::kNTPAppPageNames,
|
| + pref_service->RegisterDictionaryPref(prefs::kNTPAppsPageNames,
|
| + PrefService::UNSYNCABLE_PREF);
|
| + pref_service->RegisterListPref(prefs::kNTPAppsPageNamesOld,
|
| PrefService::UNSYNCABLE_PREF);
|
| }
|
|
|
| @@ -918,12 +1124,11 @@ void AppLauncherHandler::ExtensionUninstallAccepted() {
|
|
|
| extension_service_->UninstallExtension(extension_id_prompting_,
|
| false /* external_uninstall */, NULL);
|
| -
|
| - extension_id_prompting_ = "";
|
| + CleanupAfterUninstall();
|
| }
|
|
|
| void AppLauncherHandler::ExtensionUninstallCanceled() {
|
| - extension_id_prompting_ = "";
|
| + CleanupAfterUninstall();
|
| }
|
|
|
| void AppLauncherHandler::InstallUIProceed() {
|
|
|