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() { |