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 d0b075db106d60b779bef20b5b5e4abacb3bfc34..03b1ebea1a74fc2df1aa6b24fd3a47439a77bc26 100644 |
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc |
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc |
@@ -72,7 +72,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() {} |
@@ -165,15 +166,8 @@ void AppLauncherHandler::CreateAppInfo(const Extension* extension, |
} |
value->SetInteger("app_launch_index", app_launch_index); |
- int page_index = prefs->GetPageIndex(extension->id()); |
- if (page_index < 0) { |
- // Make sure every app has a page index (some predate the page index). |
- // The webstore app should be on the first page. |
- page_index = extension->id() == extension_misc::kWebStoreAppId ? |
- 0 : prefs->GetNaturalAppPageIndex(); |
- prefs->SetPageIndex(extension->id(), page_index); |
- } |
- value->SetInteger("page_index", page_index); |
+ EnsureAppHasPageIndex(service, extension->id()); |
+ value->SetInteger("page_index", prefs->GetPageIndex(extension->id())); |
} |
WebUIMessageHandler* AppLauncherHandler::Attach(WebUI* web_ui) { |
@@ -183,6 +177,9 @@ WebUIMessageHandler* AppLauncherHandler::Attach(WebUI* web_ui) { |
} |
void AppLauncherHandler::RegisterMessages() { |
+ web_ui_->RegisterMessageCallback("deleteAppsPage", |
+ base::Bind(&AppLauncherHandler::HandleDeleteAppsPage, |
+ base::Unretained(this))); |
web_ui_->RegisterMessageCallback("getApps", |
base::Bind(&AppLauncherHandler::HandleGetApps, |
base::Unretained(this))); |
@@ -210,8 +207,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, |
@@ -289,18 +286,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); |
+ uninstall_from_page_ = false; |
+ } |
+ break; |
+ } |
+ case chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED: { |
+ DictionaryValue pages; |
+ const ExtensionList* extensions = extension_service_->extensions(); |
+ ExtensionPrefs* prefs = extension_service_->extension_prefs(); |
+ for (ExtensionList::const_iterator it = extensions->begin(); |
+ it != extensions->end(); ++it) { |
+ if (!IsAppExcludedFromList(*it)) { |
+ std::string page_index = |
+ base::IntToString(prefs->GetPageIndex((*it)->id())); |
+ if (!pages.HasKey(page_index)) |
+ pages.Set(page_index, new DictionaryValue()); |
+ DictionaryValue* page; |
+ CHECK(pages.GetDictionary(page_index, &page)); |
+ page->SetString( |
+ base::IntToString(prefs->GetAppLaunchIndex((*it)->id())), |
+ (*it)->id()); |
+ } |
} |
+ web_ui_->CallJavascriptFunction("ntp4.appsReordered", pages); |
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_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: { |
DictionaryValue dictionary; |
FillAppDictionary(&dictionary); |
@@ -322,6 +342,18 @@ void AppLauncherHandler::Observe(int type, |
} |
} |
+// static |
+void AppLauncherHandler::EnsureAppHasPageIndex(ExtensionService* service, |
+ const std::string& id) { |
+ if (service->extension_prefs()->GetPageIndex(id) < 0) { |
+ // Make sure every app has a page index (some predate the page index). |
+ // The webstore app should be on the first page. |
+ int page_index = id == extension_misc::kWebStoreAppId ? |
+ 0 : service->extension_prefs()->GetNaturalAppPageIndex(); |
+ service->extension_prefs()->SetPageIndex(id, page_index); |
+ } |
+} |
+ |
void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) { |
// CreateAppInfo and ClearPageIndex can change the extension prefs. |
AutoReset<bool> auto_reset(&ignore_changes_, true); |
@@ -396,10 +428,10 @@ void AppLauncherHandler::FillAppDictionary(DictionaryValue* dictionary) { |
ListValue* list = update.Get(); |
list->Set(0, Value::CreateStringValue( |
l10n_util::GetStringUTF16(IDS_APP_DEFAULT_PAGE_NAME))); |
- dictionary->Set("appPageNames", |
+ dictionary->Set("appsPageNames", |
static_cast<ListValue*>(list->DeepCopy())); |
} else { |
- dictionary->Set("appPageNames", |
+ dictionary->Set("appsPageNames", |
static_cast<ListValue*>(app_page_names->DeepCopy())); |
} |
} |
@@ -426,6 +458,79 @@ void AppLauncherHandler::FillPromoDictionary(DictionaryValue* dictionary) { |
dictionary->SetString("promoExpire", data.expire); |
} |
+void AppLauncherHandler::HandleDeleteAppsPage(const ListValue* args) { |
+ double page_index_double; |
+ CHECK(args->GetDouble(0, &page_index_double) && page_index_double >= 0); |
+ size_t page_index = static_cast<size_t>(page_index_double); |
+ |
+ AutoReset<bool> auto_reset(&ignore_changes_, true); |
+ |
+ DeleteAppsPageRange(page_index, 1); |
+} |
+ |
+void AppLauncherHandler::CondenseAppsPages() { |
+ std::vector<bool> has_apps; |
+ const ExtensionList* extensions = extension_service_->extensions(); |
+ for (ExtensionList::const_iterator it = extensions->begin(); |
+ it != extensions->end(); ++it) { |
+ if (!IsAppExcludedFromList(*it)) { |
+ EnsureAppHasPageIndex(extension_service_, (*it)->id()); |
+ size_t page_index = |
+ extension_service_->extension_prefs()->GetPageIndex((*it)->id()); |
+ if (has_apps.size() < page_index + 1) |
+ has_apps.resize(page_index + 1); |
+ has_apps[page_index] = true; |
+ } |
+ } |
+ |
+ // Delete empty ranges of apps pages. |
+ int first_empty = -1; |
+ for (size_t i = 0; i < has_apps.size(); ++i) { |
+ if (!has_apps[i]) { |
+ if (first_empty == -1) |
+ first_empty = i; |
+ } else if (first_empty != -1) { |
+ DeleteAppsPageRange(first_empty, i - first_empty); |
+ has_apps.erase(has_apps.begin() + first_empty, has_apps.begin() + i); |
+ i -= (i - first_empty); |
+ first_empty = -1; |
+ } |
+ } |
+ // Make sure there are no pending empty apps pages at the end of our list. |
+ CHECK_EQ(first_empty, -1); |
+} |
+ |
+void AppLauncherHandler::DeleteAppsPageRange(size_t index, size_t how_many) { |
+ // Delete the apps page name from our prefs. |
+ PrefService* prefs = Profile::FromWebUI(web_ui_)->GetPrefs(); |
+ ListPrefUpdate update(prefs, prefs::kNTPAppPageNames); |
+ ListValue* list = update.Get(); |
+ |
+ // Index checking is done within list->Remove() and will return false if an |
+ // index to remove is negative or larger than the size of the list. This is |
+ // intentionally left un-CHECK()-ed in the case that there's a skew between |
+ // the list pref size and the last page index of all the apps. |
+ for (size_t i = 0; i < how_many; ++i) |
+ list->Remove(index, NULL); |
+ |
+ // Move apps that were on a page past the one we're deleting to their newly |
+ // adjusted page index. |
+ ExtensionPrefs* ext_prefs = extension_service_->extension_prefs(); |
+ const ExtensionList* extensions = extension_service_->extensions(); |
+ for (ExtensionList::const_iterator it = extensions->begin(); |
+ it != extensions->end(); ++it) { |
+ // Ignore extensions and the cloud print app (but handle CWS app). |
+ if (!IsAppExcludedFromList(*it)) { |
+ size_t apps_page_index = |
+ static_cast<size_t>(ext_prefs->GetPageIndex((*it)->id())); |
+ // Don't delete pages with apps still on them. |
+ CHECK(apps_page_index < index || apps_page_index >= (index + how_many)); |
+ if (apps_page_index >= (index + how_many)) |
+ ext_prefs->SetPageIndex((*it)->id(), apps_page_index - how_many); |
+ } |
+ } |
+} |
+ |
void AppLauncherHandler::HandleGetApps(const ListValue* args) { |
DictionaryValue dictionary; |
@@ -456,6 +561,7 @@ void AppLauncherHandler::HandleGetApps(const ListValue* args) { |
ignore_changes_ = false; |
} |
+ CondenseAppsPages(); |
SetAppToBeHighlighted(); |
FillAppDictionary(&dictionary); |
web_ui_->CallJavascriptFunction("getAppsCallback", dictionary); |
@@ -617,6 +723,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); |
} |
} |
@@ -687,7 +796,7 @@ 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)); |