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 app_list::AppListModel
* model
) {
55 DCHECK(!service_
&& !profile_
);
58 profile_
= service
->profile();
59 InitializePrefChangeRegistrars();
64 void ExtensionAppModelBuilder::InitializeWithProfile(
66 app_list::AppListModel
* model
) {
67 DCHECK(!service_
&& !profile_
);
69 model_
->top_level_item_list()->AddObserver(this);
71 InitializePrefChangeRegistrars();
76 void ExtensionAppModelBuilder::InitializePrefChangeRegistrars() {
77 profile_pref_change_registrar_
.Init(profile_
->GetPrefs());
78 profile_pref_change_registrar_
.Add(
79 prefs::kHideWebStoreIcon
,
80 base::Bind(&ExtensionAppModelBuilder::OnProfilePreferenceChanged
,
81 base::Unretained(this)));
83 if (!extensions::util::IsNewBookmarkAppsEnabled())
86 // TODO(calamity): analyze the performance impact of doing this every
87 // extension pref change.
88 extensions::ExtensionsBrowserClient
* client
=
89 extensions::ExtensionsBrowserClient::Get();
90 extension_pref_change_registrar_
.Init(
91 client
->GetPrefServiceForContext(profile_
));
92 extension_pref_change_registrar_
.Add(
93 extensions::pref_names::kExtensions
,
94 base::Bind(&ExtensionAppModelBuilder::OnExtensionPreferenceChanged
,
95 base::Unretained(this)));
98 void ExtensionAppModelBuilder::OnProfilePreferenceChanged() {
99 extensions::ExtensionSet extensions
;
100 controller_
->GetApps(profile_
, &extensions
);
102 for (extensions::ExtensionSet::const_iterator app
= extensions
.begin();
103 app
!= extensions
.end(); ++app
) {
104 bool should_display
=
105 extensions::ui_util::ShouldDisplayInAppLauncher(app
->get(), profile_
);
106 bool does_display
= GetExtensionAppItem((*app
)->id()) != NULL
;
108 if (should_display
== does_display
)
111 if (should_display
) {
112 InsertApp(CreateAppItem((*app
)->id(),
115 (*app
)->is_platform_app()));
118 service_
->RemoveItem((*app
)->id());
120 model_
->DeleteItem((*app
)->id());
125 void ExtensionAppModelBuilder::OnExtensionPreferenceChanged() {
126 model_
->NotifyExtensionPreferenceChanged();
129 void ExtensionAppModelBuilder::OnBeginExtensionInstall(
130 const ExtensionInstallParams
& params
) {
131 if (!params
.is_app
|| params
.is_ephemeral
)
134 DVLOG(2) << service_
<< ": OnBeginExtensionInstall: "
135 << params
.extension_id
.substr(0, 8);
136 ExtensionAppItem
* existing_item
= GetExtensionAppItem(params
.extension_id
);
138 existing_item
->SetIsInstalling(true);
142 // Icons from the webstore can be unusual sizes. Once installed,
143 // ExtensionAppItem uses extension_misc::EXTENSION_ICON_MEDIUM (48) to load
144 // it, so be consistent with that.
145 gfx::Size
icon_size(extension_misc::EXTENSION_ICON_MEDIUM
,
146 extension_misc::EXTENSION_ICON_MEDIUM
);
147 gfx::ImageSkia
resized(gfx::ImageSkiaOperations::CreateResizedImage(
148 params
.installing_icon
, skia::ImageOperations::RESIZE_BEST
, icon_size
));
150 InsertApp(CreateAppItem(params
.extension_id
,
151 params
.extension_name
,
153 params
.is_platform_app
));
156 void ExtensionAppModelBuilder::OnDownloadProgress(
157 const std::string
& extension_id
,
158 int percent_downloaded
) {
159 ExtensionAppItem
* item
= GetExtensionAppItem(extension_id
);
162 item
->SetPercentDownloaded(percent_downloaded
);
165 void ExtensionAppModelBuilder::OnInstallFailure(
166 const std::string
& extension_id
) {
167 model_
->DeleteItem(extension_id
);
170 void ExtensionAppModelBuilder::OnExtensionLoaded(
171 content::BrowserContext
* browser_context
,
172 const extensions::Extension
* extension
) {
173 if (!extensions::ui_util::ShouldDisplayInAppLauncher(extension
, profile_
))
176 DVLOG(2) << service_
<< ": OnExtensionLoaded: "
177 << extension
->id().substr(0, 8);
178 ExtensionAppItem
* existing_item
= GetExtensionAppItem(extension
->id());
180 existing_item
->Reload();
182 service_
->UpdateItem(existing_item
);
186 InsertApp(CreateAppItem(extension
->id(),
189 extension
->is_platform_app()));
192 void ExtensionAppModelBuilder::OnExtensionUnloaded(
193 content::BrowserContext
* browser_context
,
194 const extensions::Extension
* extension
,
195 extensions::UnloadedExtensionInfo::Reason reason
) {
196 ExtensionAppItem
* item
= GetExtensionAppItem(extension
->id());
202 void ExtensionAppModelBuilder::OnExtensionUninstalled(
203 content::BrowserContext
* browser_context
,
204 const extensions::Extension
* extension
,
205 extensions::UninstallReason reason
) {
207 DVLOG(2) << service_
<< ": OnExtensionUninstalled: "
208 << extension
->id().substr(0, 8);
209 service_
->RemoveUninstalledItem(extension
->id());
212 model_
->DeleteUninstalledItem(extension
->id());
215 void ExtensionAppModelBuilder::OnDisabledExtensionUpdated(
216 const Extension
* extension
) {
217 if (!extensions::ui_util::ShouldDisplayInAppLauncher(extension
, profile_
))
220 ExtensionAppItem
* existing_item
= GetExtensionAppItem(extension
->id());
222 existing_item
->Reload();
225 void ExtensionAppModelBuilder::OnShutdown() {
227 tracker_
->RemoveObserver(this);
232 void ExtensionAppModelBuilder::OnShutdown(
233 extensions::ExtensionRegistry
* registry
) {
234 if (!extension_registry_
)
237 DCHECK_EQ(extension_registry_
, registry
);
238 extension_registry_
->RemoveObserver(this);
239 extension_registry_
= NULL
;
242 scoped_ptr
<ExtensionAppItem
> ExtensionAppModelBuilder::CreateAppItem(
243 const std::string
& extension_id
,
244 const std::string
& extension_name
,
245 const gfx::ImageSkia
& installing_icon
,
246 bool is_platform_app
) {
247 const app_list::AppListSyncableService::SyncItem
* sync_item
=
248 service_
? service_
->GetSyncItem(extension_id
) : NULL
;
249 return make_scoped_ptr(new ExtensionAppItem(profile_
,
257 void ExtensionAppModelBuilder::BuildModel() {
259 tracker_
= controller_
->GetInstallTrackerFor(profile_
);
260 extension_registry_
= extensions::ExtensionRegistry::Get(profile_
);
264 // Start observing after model is built.
266 tracker_
->AddObserver(this);
268 if (extension_registry_
)
269 extension_registry_
->AddObserver(this);
272 void ExtensionAppModelBuilder::PopulateApps() {
273 extensions::ExtensionSet extensions
;
274 controller_
->GetApps(profile_
, &extensions
);
276 for (extensions::ExtensionSet::const_iterator app
= extensions
.begin();
277 app
!= extensions
.end(); ++app
) {
278 if (!extensions::ui_util::ShouldDisplayInAppLauncher(app
->get(), profile_
))
280 InsertApp(CreateAppItem((*app
)->id(),
283 (*app
)->is_platform_app()));
287 void ExtensionAppModelBuilder::InsertApp(scoped_ptr
<ExtensionAppItem
> app
) {
289 service_
->AddItem(app
.Pass());
292 model_
->AddItem(app
.Pass());
295 ExtensionAppItem
* ExtensionAppModelBuilder::GetExtensionAppItem(
296 const std::string
& extension_id
) {
297 app_list::AppListItem
* item
= model_
->FindItem(extension_id
);
298 LOG_IF(ERROR
, item
&&
299 item
->GetItemType() != ExtensionAppItem::kItemType
)
300 << "App Item matching id: " << extension_id
301 << " has incorrect type: '" << item
->GetItemType() << "'";
302 return static_cast<ExtensionAppItem
*>(item
);
305 void ExtensionAppModelBuilder::OnListItemMoved(size_t from_index
,
307 app_list::AppListItem
* item
) {
310 // This will get called from AppListItemList::ListItemMoved after
311 // set_position is called for the item.
312 if (item
->GetItemType() != ExtensionAppItem::kItemType
)
315 app_list::AppListItemList
* item_list
= model_
->top_level_item_list();
316 ExtensionAppItem
* prev
= NULL
;
317 for (size_t idx
= to_index
; idx
> 0; --idx
) {
318 app_list::AppListItem
* item
= item_list
->item_at(idx
- 1);
319 if (item
->GetItemType() == ExtensionAppItem::kItemType
) {
320 prev
= static_cast<ExtensionAppItem
*>(item
);
324 ExtensionAppItem
* next
= NULL
;
325 for (size_t idx
= to_index
; idx
< item_list
->item_count() - 1; ++idx
) {
326 app_list::AppListItem
* item
= item_list
->item_at(idx
+ 1);
327 if (item
->GetItemType() == ExtensionAppItem::kItemType
) {
328 next
= static_cast<ExtensionAppItem
*>(item
);
332 // item->Move will call set_position, overriding the item's position.
334 static_cast<ExtensionAppItem
*>(item
)->Move(prev
, next
);