extension_app_model_builder.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
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 if (service_) 153 service_->UpdateItem(existing_item); 154 return; 155 } 156 157 InsertApp(CreateAppItem(extension->id(), 158 "", 159 gfx::ImageSkia(), 160 extension->is_platform_app())); 161 UpdateHighlight(); 162} 163 164void ExtensionAppModelBuilder::OnExtensionUnloaded(const Extension* extension) { 165 ExtensionAppItem* item = GetExtensionAppItem(extension->id()); 166 if (!item) 167 return; 168 item->UpdateIcon(); 169} 170 171void ExtensionAppModelBuilder::OnExtensionUninstalled( 172 const Extension* extension) { 173 if (service_) { 174 DVLOG(2) << service_ << ": OnExtensionUninstalled: " 175 << extension->id().substr(0, 8); 176 service_->RemoveItem(extension->id()); 177 return; 178 } 179 model_->DeleteItem(extension->id()); 180} 181 182void ExtensionAppModelBuilder::OnDisabledExtensionUpdated( 183 const Extension* extension) { 184 if (!extension->ShouldDisplayInAppLauncher()) 185 return; 186 187 ExtensionAppItem* existing_item = GetExtensionAppItem(extension->id()); 188 if (existing_item) 189 existing_item->Reload(); 190} 191 192void ExtensionAppModelBuilder::OnAppInstalledToAppList( 193 const std::string& extension_id) { 194 SetHighlightedApp(extension_id); 195} 196 197void ExtensionAppModelBuilder::OnShutdown() { 198 if (tracker_) { 199 tracker_->RemoveObserver(this); 200 tracker_ = NULL; 201 } 202} 203 204scoped_ptr<ExtensionAppItem> ExtensionAppModelBuilder::CreateAppItem( 205 const std::string& extension_id, 206 const std::string& extension_name, 207 const gfx::ImageSkia& installing_icon, 208 bool is_platform_app) { 209 const app_list::AppListSyncableService::SyncItem* sync_item = 210 service_ ? service_->GetSyncItem(extension_id) : NULL; 211 return make_scoped_ptr(new ExtensionAppItem(profile_, 212 sync_item, 213 extension_id, 214 extension_name, 215 installing_icon, 216 is_platform_app)); 217} 218 219void ExtensionAppModelBuilder::BuildModel() { 220 DCHECK(!tracker_); 221 tracker_ = controller_->GetInstallTrackerFor(profile_); 222 223 PopulateApps(); 224 UpdateHighlight(); 225 226 // Start observing after model is built. 227 if (tracker_) 228 tracker_->AddObserver(this); 229} 230 231void ExtensionAppModelBuilder::PopulateApps() { 232 extensions::ExtensionSet extensions; 233 controller_->GetApps(profile_, &extensions); 234 235 for (extensions::ExtensionSet::const_iterator app = extensions.begin(); 236 app != extensions.end(); ++app) { 237 if (!ShouldDisplayInAppLauncher(profile_, *app)) 238 continue; 239 InsertApp(CreateAppItem((*app)->id(), 240 "", 241 gfx::ImageSkia(), 242 (*app)->is_platform_app())); 243 } 244} 245 246void ExtensionAppModelBuilder::InsertApp(scoped_ptr<ExtensionAppItem> app) { 247 if (service_) { 248 service_->AddItem(app.PassAs<app_list::AppListItem>()); 249 return; 250 } 251 model_->AddItem(app.PassAs<app_list::AppListItem>()); 252} 253 254void ExtensionAppModelBuilder::SetHighlightedApp( 255 const std::string& extension_id) { 256 if (extension_id == highlight_app_id_) 257 return; 258 ExtensionAppItem* old_app = GetExtensionAppItem(highlight_app_id_); 259 if (old_app) 260 old_app->SetHighlighted(false); 261 highlight_app_id_ = extension_id; 262 ExtensionAppItem* new_app = GetExtensionAppItem(highlight_app_id_); 263 highlighted_app_pending_ = !new_app; 264 if (new_app) 265 new_app->SetHighlighted(true); 266} 267 268ExtensionAppItem* ExtensionAppModelBuilder::GetExtensionAppItem( 269 const std::string& extension_id) { 270 app_list::AppListItem* item = model_->FindItem(extension_id); 271 LOG_IF(ERROR, item && 272 item->GetItemType() != ExtensionAppItem::kItemType) 273 << "App Item matching id: " << extension_id 274 << " has incorrect type: '" << item->GetItemType() << "'"; 275 return static_cast<ExtensionAppItem*>(item); 276} 277 278void ExtensionAppModelBuilder::UpdateHighlight() { 279 DCHECK(model_); 280 if (!highlighted_app_pending_ || highlight_app_id_.empty()) 281 return; 282 ExtensionAppItem* item = GetExtensionAppItem(highlight_app_id_); 283 if (!item) 284 return; 285 item->SetHighlighted(true); 286 highlighted_app_pending_ = false; 287} 288 289void ExtensionAppModelBuilder::OnListItemMoved(size_t from_index, 290 size_t to_index, 291 app_list::AppListItem* item) { 292 DCHECK(!service_); 293 294 // This will get called from AppListItemList::ListItemMoved after 295 // set_position is called for the item. 296 if (item->GetItemType() != ExtensionAppItem::kItemType) 297 return; 298 299 app_list::AppListItemList* item_list = model_->top_level_item_list(); 300 ExtensionAppItem* prev = NULL; 301 for (size_t idx = to_index; idx > 0; --idx) { 302 app_list::AppListItem* item = item_list->item_at(idx - 1); 303 if (item->GetItemType() == ExtensionAppItem::kItemType) { 304 prev = static_cast<ExtensionAppItem*>(item); 305 break; 306 } 307 } 308 ExtensionAppItem* next = NULL; 309 for (size_t idx = to_index; idx < item_list->item_count() - 1; ++idx) { 310 app_list::AppListItem* item = item_list->item_at(idx + 1); 311 if (item->GetItemType() == ExtensionAppItem::kItemType) { 312 next = static_cast<ExtensionAppItem*>(item); 313 break; 314 } 315 } 316 // item->Move will call set_position, overriding the item's position. 317 if (prev || next) 318 static_cast<ExtensionAppItem*>(item)->Move(prev, next); 319} 320