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