extension_app_model_builder.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright (c) 2012 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/app_list/extension_app_model_builder.h" 6 7#include <algorithm> 8 9#include "base/auto_reset.h" 10#include "base/bind.h" 11#include "base/callback.h" 12#include "base/command_line.h" 13#include "base/prefs/pref_service.h" 14#include "chrome/browser/chrome_notification_types.h" 15#include "chrome/browser/extensions/install_tracker.h" 16#include "chrome/browser/extensions/install_tracker_factory.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/browser/ui/app_list/app_list_controller_delegate.h" 19#include "chrome/browser/ui/app_list/app_list_syncable_service.h" 20#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h" 21#include "chrome/browser/ui/app_list/extension_app_item.h" 22#include "chrome/common/chrome_switches.h" 23#include "chrome/common/extensions/extension_constants.h" 24#include "chrome/common/pref_names.h" 25#include "content/public/browser/notification_service.h" 26#include "extensions/browser/extension_prefs.h" 27#include "extensions/browser/extension_system.h" 28#include "extensions/browser/extensions_browser_client.h" 29#include "extensions/browser/pref_names.h" 30#include "extensions/common/extension.h" 31#include "extensions/common/extension_set.h" 32#include "ui/gfx/image/image_skia.h" 33 34using extensions::Extension; 35 36namespace { 37 38bool ShouldDisplayInAppLauncher(Profile* profile, 39 scoped_refptr<const Extension> app) { 40 // If it's the web store, check the policy. 41 bool blocked_by_policy = 42 (app->id() == extension_misc::kWebStoreAppId || 43 app->id() == extension_misc::kEnterpriseWebStoreAppId) && 44 profile->GetPrefs()->GetBoolean(prefs::kHideWebStoreIcon); 45 return app->ShouldDisplayInAppLauncher() && !blocked_by_policy; 46} 47 48} // namespace 49 50ExtensionAppModelBuilder::ExtensionAppModelBuilder( 51 AppListControllerDelegate* controller) 52 : service_(NULL), 53 profile_(NULL), 54 controller_(controller), 55 model_(NULL), 56 highlighted_app_pending_(false), 57 tracker_(NULL) { 58} 59 60ExtensionAppModelBuilder::~ExtensionAppModelBuilder() { 61 OnShutdown(); 62 if (!service_) 63 model_->top_level_item_list()->RemoveObserver(this); 64} 65 66void ExtensionAppModelBuilder::InitializeWithService( 67 app_list::AppListSyncableService* service) { 68 DCHECK(!service_ && !profile_); 69 model_ = service->model(); 70 service_ = service; 71 profile_ = service->profile(); 72 InitializePrefChangeRegistrar(); 73 74 BuildModel(); 75} 76 77void ExtensionAppModelBuilder::InitializeWithProfile( 78 Profile* profile, 79 app_list::AppListModel* model) { 80 DCHECK(!service_ && !profile_); 81 model_ = model; 82 model_->top_level_item_list()->AddObserver(this); 83 profile_ = profile; 84 InitializePrefChangeRegistrar(); 85 86 BuildModel(); 87} 88 89void ExtensionAppModelBuilder::InitializePrefChangeRegistrar() { 90 if (!CommandLine::ForCurrentProcess()->HasSwitch( 91 switches::kEnableStreamlinedHostedApps)) 92 return; 93 94 // TODO(calamity): analyze the performance impact of doing this every 95 // extension pref change. 96 extensions::ExtensionsBrowserClient* client = 97 extensions::ExtensionsBrowserClient::Get(); 98 extension_pref_change_registrar_.Init( 99 client->GetPrefServiceForContext(profile_)); 100 extension_pref_change_registrar_.Add( 101 extensions::pref_names::kExtensions, 102 base::Bind(&ExtensionAppModelBuilder::OnExtensionPreferenceChanged, 103 base::Unretained(this))); 104} 105 106void ExtensionAppModelBuilder::OnExtensionPreferenceChanged() { 107 model_->NotifyExtensionPreferenceChanged(); 108} 109 110void ExtensionAppModelBuilder::OnBeginExtensionInstall( 111 const ExtensionInstallParams& params) { 112 if (!params.is_app || params.is_ephemeral) 113 return; 114 115 DVLOG(2) << service_ << ": OnBeginExtensionInstall: " 116 << params.extension_id.substr(0, 8); 117 ExtensionAppItem* existing_item = GetExtensionAppItem(params.extension_id); 118 if (existing_item) { 119 existing_item->SetIsInstalling(true); 120 return; 121 } 122 InsertApp(CreateAppItem(params.extension_id, 123 params.extension_name, 124 params.installing_icon, 125 params.is_platform_app)); 126 SetHighlightedApp(params.extension_id); 127} 128 129void ExtensionAppModelBuilder::OnDownloadProgress( 130 const std::string& extension_id, 131 int percent_downloaded) { 132 ExtensionAppItem* item = GetExtensionAppItem(extension_id); 133 if (!item) 134 return; 135 item->SetPercentDownloaded(percent_downloaded); 136} 137 138void ExtensionAppModelBuilder::OnInstallFailure( 139 const std::string& extension_id) { 140 model_->DeleteItem(extension_id); 141} 142 143void ExtensionAppModelBuilder::OnExtensionLoaded(const Extension* extension) { 144 if (!extension->ShouldDisplayInAppLauncher()) 145 return; 146 147 DVLOG(2) << service_ << ": OnExtensionLoaded: " 148 << extension->id().substr(0, 8); 149 ExtensionAppItem* existing_item = GetExtensionAppItem(extension->id()); 150 if (existing_item) { 151 existing_item->Reload(); 152 return; 153 } 154 155 InsertApp(CreateAppItem(extension->id(), 156 "", 157 gfx::ImageSkia(), 158 extension->is_platform_app())); 159 UpdateHighlight(); 160} 161 162void ExtensionAppModelBuilder::OnExtensionUnloaded(const Extension* extension) { 163 ExtensionAppItem* item = GetExtensionAppItem(extension->id()); 164 if (!item) 165 return; 166 item->UpdateIcon(); 167} 168 169void ExtensionAppModelBuilder::OnExtensionUninstalled( 170 const Extension* extension) { 171 if (service_) { 172 DVLOG(2) << service_ << ": OnExtensionUninstalled: " 173 << extension->id().substr(0, 8); 174 service_->RemoveItem(extension->id()); 175 return; 176 } 177 model_->DeleteItem(extension->id()); 178} 179 180void ExtensionAppModelBuilder::OnAppInstalledToAppList( 181 const std::string& extension_id) { 182 SetHighlightedApp(extension_id); 183} 184 185void ExtensionAppModelBuilder::OnShutdown() { 186 if (tracker_) { 187 tracker_->RemoveObserver(this); 188 tracker_ = NULL; 189 } 190} 191 192scoped_ptr<ExtensionAppItem> ExtensionAppModelBuilder::CreateAppItem( 193 const std::string& extension_id, 194 const std::string& extension_name, 195 const gfx::ImageSkia& installing_icon, 196 bool is_platform_app) { 197 const app_list::AppListSyncableService::SyncItem* sync_item = 198 service_ ? service_->GetSyncItem(extension_id) : NULL; 199 return make_scoped_ptr(new ExtensionAppItem(profile_, 200 sync_item, 201 extension_id, 202 extension_name, 203 installing_icon, 204 is_platform_app)); 205} 206 207void ExtensionAppModelBuilder::BuildModel() { 208 DCHECK(!tracker_); 209 tracker_ = controller_->GetInstallTrackerFor(profile_); 210 211 PopulateApps(); 212 UpdateHighlight(); 213 214 // Start observing after model is built. 215 if (tracker_) 216 tracker_->AddObserver(this); 217} 218 219void ExtensionAppModelBuilder::PopulateApps() { 220 extensions::ExtensionSet extensions; 221 controller_->GetApps(profile_, &extensions); 222 223 for (extensions::ExtensionSet::const_iterator app = extensions.begin(); 224 app != extensions.end(); ++app) { 225 if (!ShouldDisplayInAppLauncher(profile_, *app)) 226 continue; 227 InsertApp(CreateAppItem((*app)->id(), 228 "", 229 gfx::ImageSkia(), 230 (*app)->is_platform_app())); 231 } 232} 233 234void ExtensionAppModelBuilder::InsertApp(scoped_ptr<ExtensionAppItem> app) { 235 if (service_) { 236 service_->AddItem(app.PassAs<app_list::AppListItem>()); 237 return; 238 } 239 model_->AddItem(app.PassAs<app_list::AppListItem>()); 240} 241 242void ExtensionAppModelBuilder::SetHighlightedApp( 243 const std::string& extension_id) { 244 if (extension_id == highlight_app_id_) 245 return; 246 ExtensionAppItem* old_app = GetExtensionAppItem(highlight_app_id_); 247 if (old_app) 248 old_app->SetHighlighted(false); 249 highlight_app_id_ = extension_id; 250 ExtensionAppItem* new_app = GetExtensionAppItem(highlight_app_id_); 251 highlighted_app_pending_ = !new_app; 252 if (new_app) 253 new_app->SetHighlighted(true); 254} 255 256ExtensionAppItem* ExtensionAppModelBuilder::GetExtensionAppItem( 257 const std::string& extension_id) { 258 app_list::AppListItem* item = model_->FindItem(extension_id); 259 LOG_IF(ERROR, item && 260 item->GetItemType() != ExtensionAppItem::kItemType) 261 << "App Item matching id: " << extension_id 262 << " has incorrect type: '" << item->GetItemType() << "'"; 263 return static_cast<ExtensionAppItem*>(item); 264} 265 266void ExtensionAppModelBuilder::UpdateHighlight() { 267 DCHECK(model_); 268 if (!highlighted_app_pending_ || highlight_app_id_.empty()) 269 return; 270 ExtensionAppItem* item = GetExtensionAppItem(highlight_app_id_); 271 if (!item) 272 return; 273 item->SetHighlighted(true); 274 highlighted_app_pending_ = false; 275} 276 277void ExtensionAppModelBuilder::OnListItemMoved(size_t from_index, 278 size_t to_index, 279 app_list::AppListItem* item) { 280 DCHECK(!service_); 281 282 // This will get called from AppListItemList::ListItemMoved after 283 // set_position is called for the item. 284 if (item->GetItemType() != ExtensionAppItem::kItemType) 285 return; 286 287 app_list::AppListItemList* item_list = model_->top_level_item_list(); 288 ExtensionAppItem* prev = NULL; 289 for (size_t idx = to_index; idx > 0; --idx) { 290 app_list::AppListItem* item = item_list->item_at(idx - 1); 291 if (item->GetItemType() == ExtensionAppItem::kItemType) { 292 prev = static_cast<ExtensionAppItem*>(item); 293 break; 294 } 295 } 296 ExtensionAppItem* next = NULL; 297 for (size_t idx = to_index; idx < item_list->item_count() - 1; ++idx) { 298 app_list::AppListItem* item = item_list->item_at(idx + 1); 299 if (item->GetItemType() == ExtensionAppItem::kItemType) { 300 next = static_cast<ExtensionAppItem*>(item); 301 break; 302 } 303 } 304 // item->Move will call set_position, overriding the item's position. 305 if (prev || next) 306 static_cast<ExtensionAppItem*>(item)->Move(prev, next); 307} 308