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.
5 #include "chrome/browser/ui/app_list/extension_app_model_builder.h"
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "chrome/browser/extensions/extension_ui_util.h"
13 #include "chrome/browser/extensions/extension_util.h"
14 #include "chrome/browser/extensions/install_tracker.h"
15 #include "chrome/browser/extensions/install_tracker_factory.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
18 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
19 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
20 #include "chrome/browser/ui/app_list/extension_app_item.h"
21 #include "chrome/common/pref_names.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/extensions_browser_client.h"
26 #include "extensions/browser/pref_names.h"
27 #include "extensions/common/constants.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/extension_set.h"
30 #include "ui/gfx/image/image_skia.h"
31 #include "ui/gfx/image/image_skia_operations.h"
33 using extensions::Extension;
35 ExtensionAppModelBuilder::ExtensionAppModelBuilder(
36 AppListControllerDelegate* controller)
39 controller_(controller),
42 extension_registry_(NULL) {
45 ExtensionAppModelBuilder::~ExtensionAppModelBuilder() {
47 OnShutdown(extension_registry_);
49 model_->top_level_item_list()->RemoveObserver(this);
52 void ExtensionAppModelBuilder::InitializeWithService(
53 app_list::AppListSyncableService* service) {
54 DCHECK(!service_ && !profile_);
55 model_ = service->model();
57 profile_ = service->profile();
58 InitializePrefChangeRegistrars();
63 void ExtensionAppModelBuilder::InitializeWithProfile(
65 app_list::AppListModel* model) {
66 DCHECK(!service_ && !profile_);
68 model_->top_level_item_list()->AddObserver(this);
70 InitializePrefChangeRegistrars();
75 void ExtensionAppModelBuilder::InitializePrefChangeRegistrars() {
76 profile_pref_change_registrar_.Init(profile_->GetPrefs());
77 profile_pref_change_registrar_.Add(
78 prefs::kHideWebStoreIcon,
79 base::Bind(&ExtensionAppModelBuilder::OnProfilePreferenceChanged,
80 base::Unretained(this)));
82 if (!extensions::util::IsStreamlinedHostedAppsEnabled())
85 // TODO(calamity): analyze the performance impact of doing this every
86 // extension pref change.
87 extensions::ExtensionsBrowserClient* client =
88 extensions::ExtensionsBrowserClient::Get();
89 extension_pref_change_registrar_.Init(
90 client->GetPrefServiceForContext(profile_));
91 extension_pref_change_registrar_.Add(
92 extensions::pref_names::kExtensions,
93 base::Bind(&ExtensionAppModelBuilder::OnExtensionPreferenceChanged,
94 base::Unretained(this)));
97 void ExtensionAppModelBuilder::OnProfilePreferenceChanged() {
98 extensions::ExtensionSet extensions;
99 controller_->GetApps(profile_, &extensions);
101 for (extensions::ExtensionSet::const_iterator app = extensions.begin();
102 app != extensions.end(); ++app) {
103 bool should_display =
104 extensions::ui_util::ShouldDisplayInAppLauncher(app->get(), profile_);
105 bool does_display = GetExtensionAppItem((*app)->id()) != NULL;
107 if (should_display == does_display)
110 if (should_display) {
111 InsertApp(CreateAppItem((*app)->id(),
114 (*app)->is_platform_app()));
117 service_->RemoveItem((*app)->id());
119 model_->DeleteItem((*app)->id());
124 void ExtensionAppModelBuilder::OnExtensionPreferenceChanged() {
125 model_->NotifyExtensionPreferenceChanged();
128 void ExtensionAppModelBuilder::OnBeginExtensionInstall(
129 const ExtensionInstallParams& params) {
130 if (!params.is_app || params.is_ephemeral)
133 DVLOG(2) << service_ << ": OnBeginExtensionInstall: "
134 << params.extension_id.substr(0, 8);
135 ExtensionAppItem* existing_item = GetExtensionAppItem(params.extension_id);
137 existing_item->SetIsInstalling(true);
141 // Icons from the webstore can be unusual sizes. Once installed,
142 // ExtensionAppItem uses extension_misc::EXTENSION_ICON_MEDIUM (48) to load
143 // it, so be consistent with that.
144 gfx::Size icon_size(extension_misc::EXTENSION_ICON_MEDIUM,
145 extension_misc::EXTENSION_ICON_MEDIUM);
146 gfx::ImageSkia resized(gfx::ImageSkiaOperations::CreateResizedImage(
147 params.installing_icon, skia::ImageOperations::RESIZE_BEST, icon_size));
149 InsertApp(CreateAppItem(params.extension_id,
150 params.extension_name,
152 params.is_platform_app));
155 void ExtensionAppModelBuilder::OnDownloadProgress(
156 const std::string& extension_id,
157 int percent_downloaded) {
158 ExtensionAppItem* item = GetExtensionAppItem(extension_id);
161 item->SetPercentDownloaded(percent_downloaded);
164 void ExtensionAppModelBuilder::OnInstallFailure(
165 const std::string& extension_id) {
166 model_->DeleteItem(extension_id);
169 void ExtensionAppModelBuilder::OnExtensionLoaded(
170 content::BrowserContext* browser_context,
171 const extensions::Extension* extension) {
172 if (!extensions::ui_util::ShouldDisplayInAppLauncher(extension, profile_))
175 DVLOG(2) << service_ << ": OnExtensionLoaded: "
176 << extension->id().substr(0, 8);
177 ExtensionAppItem* existing_item = GetExtensionAppItem(extension->id());
179 existing_item->Reload();
181 service_->UpdateItem(existing_item);
185 InsertApp(CreateAppItem(extension->id(),
188 extension->is_platform_app()));
191 void ExtensionAppModelBuilder::OnExtensionUnloaded(
192 content::BrowserContext* browser_context,
193 const extensions::Extension* extension,
194 extensions::UnloadedExtensionInfo::Reason reason) {
195 ExtensionAppItem* item = GetExtensionAppItem(extension->id());
201 void ExtensionAppModelBuilder::OnExtensionUninstalled(
202 content::BrowserContext* browser_context,
203 const extensions::Extension* extension,
204 extensions::UninstallReason reason) {
206 DVLOG(2) << service_ << ": OnExtensionUninstalled: "
207 << extension->id().substr(0, 8);
208 service_->RemoveItem(extension->id());
211 model_->DeleteItem(extension->id());
214 void ExtensionAppModelBuilder::OnDisabledExtensionUpdated(
215 const Extension* extension) {
216 if (!extensions::ui_util::ShouldDisplayInAppLauncher(extension, profile_))
219 ExtensionAppItem* existing_item = GetExtensionAppItem(extension->id());
221 existing_item->Reload();
224 void ExtensionAppModelBuilder::OnShutdown() {
226 tracker_->RemoveObserver(this);
231 void ExtensionAppModelBuilder::OnShutdown(
232 extensions::ExtensionRegistry* registry) {
233 if (!extension_registry_)
236 DCHECK_EQ(extension_registry_, registry);
237 extension_registry_->RemoveObserver(this);
238 extension_registry_ = NULL;
241 scoped_ptr<ExtensionAppItem> ExtensionAppModelBuilder::CreateAppItem(
242 const std::string& extension_id,
243 const std::string& extension_name,
244 const gfx::ImageSkia& installing_icon,
245 bool is_platform_app) {
246 const app_list::AppListSyncableService::SyncItem* sync_item =
247 service_ ? service_->GetSyncItem(extension_id) : NULL;
248 return make_scoped_ptr(new ExtensionAppItem(profile_,
256 void ExtensionAppModelBuilder::BuildModel() {
258 tracker_ = controller_->GetInstallTrackerFor(profile_);
259 extension_registry_ = extensions::ExtensionRegistry::Get(profile_);
263 // Start observing after model is built.
265 tracker_->AddObserver(this);
267 if (extension_registry_)
268 extension_registry_->AddObserver(this);
271 void ExtensionAppModelBuilder::PopulateApps() {
272 extensions::ExtensionSet extensions;
273 controller_->GetApps(profile_, &extensions);
275 for (extensions::ExtensionSet::const_iterator app = extensions.begin();
276 app != extensions.end(); ++app) {
277 if (!extensions::ui_util::ShouldDisplayInAppLauncher(app->get(), profile_))
279 InsertApp(CreateAppItem((*app)->id(),
282 (*app)->is_platform_app()));
286 void ExtensionAppModelBuilder::InsertApp(scoped_ptr<ExtensionAppItem> app) {
288 service_->AddItem(app.Pass());
291 model_->AddItem(app.Pass());
294 ExtensionAppItem* ExtensionAppModelBuilder::GetExtensionAppItem(
295 const std::string& extension_id) {
296 app_list::AppListItem* item = model_->FindItem(extension_id);
297 LOG_IF(ERROR, item &&
298 item->GetItemType() != ExtensionAppItem::kItemType)
299 << "App Item matching id: " << extension_id
300 << " has incorrect type: '" << item->GetItemType() << "'";
301 return static_cast<ExtensionAppItem*>(item);
304 void ExtensionAppModelBuilder::OnListItemMoved(size_t from_index,
306 app_list::AppListItem* item) {
309 // This will get called from AppListItemList::ListItemMoved after
310 // set_position is called for the item.
311 if (item->GetItemType() != ExtensionAppItem::kItemType)
314 app_list::AppListItemList* item_list = model_->top_level_item_list();
315 ExtensionAppItem* prev = NULL;
316 for (size_t idx = to_index; idx > 0; --idx) {
317 app_list::AppListItem* item = item_list->item_at(idx - 1);
318 if (item->GetItemType() == ExtensionAppItem::kItemType) {
319 prev = static_cast<ExtensionAppItem*>(item);
323 ExtensionAppItem* next = NULL;
324 for (size_t idx = to_index; idx < item_list->item_count() - 1; ++idx) {
325 app_list::AppListItem* item = item_list->item_at(idx + 1);
326 if (item->GetItemType() == ExtensionAppItem::kItemType) {
327 next = static_cast<ExtensionAppItem*>(item);
331 // item->Move will call set_position, overriding the item's position.
333 static_cast<ExtensionAppItem*>(item)->Move(prev, next);