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/callback.h"
11 #include "base/command_line.h"
12 #include "base/prefs/pref_service.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/extensions/extension_service.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/pref_names.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/extension_set.h"
31 #include "ui/gfx/image/image_skia.h"
33 using extensions::Extension
;
37 bool ShouldDisplayInAppLauncher(Profile
* profile
,
38 scoped_refptr
<const Extension
> app
) {
39 // If it's the web store, check the policy.
40 bool blocked_by_policy
=
41 (app
->id() == extension_misc::kWebStoreAppId
||
42 app
->id() == extension_misc::kEnterpriseWebStoreAppId
) &&
43 profile
->GetPrefs()->GetBoolean(prefs::kHideWebStoreIcon
);
44 return app
->ShouldDisplayInAppLauncher() && !blocked_by_policy
;
49 ExtensionAppModelBuilder::ExtensionAppModelBuilder(
50 AppListControllerDelegate
* controller
)
53 controller_(controller
),
55 highlighted_app_pending_(false),
59 ExtensionAppModelBuilder::~ExtensionAppModelBuilder() {
61 model_
->item_list()->RemoveObserver(this);
64 void ExtensionAppModelBuilder::InitializeWithService(
65 app_list::AppListSyncableService
* service
) {
66 DCHECK(!service_
&& !profile_
);
67 model_
= service
->model();
68 model_
->item_list()->AddObserver(this);
70 profile_
= service
->profile();
71 InitializePrefChangeRegistrar();
76 void ExtensionAppModelBuilder::InitializeWithProfile(
78 app_list::AppListModel
* model
) {
79 DCHECK(!service_
&& !profile_
);
81 model_
->item_list()->AddObserver(this);
83 InitializePrefChangeRegistrar();
88 void ExtensionAppModelBuilder::InitializePrefChangeRegistrar() {
89 if (!CommandLine::ForCurrentProcess()->HasSwitch(
90 switches::kEnableStreamlinedHostedApps
))
93 const ExtensionService
* extension_service
=
94 extensions::ExtensionSystem::Get(profile_
)->extension_service();
95 if (!extension_service
)
98 extension_pref_change_registrar_
.Init(
99 extension_service
->extension_prefs()->pref_service());
100 extension_pref_change_registrar_
.Add(
101 extensions::pref_names::kExtensions
,
102 base::Bind(&ExtensionAppModelBuilder::OnExtensionPreferenceChanged
,
103 base::Unretained(this)));
106 void ExtensionAppModelBuilder::OnExtensionPreferenceChanged() {
107 // TODO(calamity): analyze the performance impact of doing this every
108 // extension pref change.
109 app_list::AppListItemList
* item_list
= model_
->item_list();
110 for (size_t i
= 0; i
< item_list
->item_count(); ++i
) {
111 app_list::AppListItem
* item
= item_list
->item_at(i
);
112 if (item
->GetItemType() != ExtensionAppItem::kItemType
)
115 static_cast<ExtensionAppItem
*>(item
)->UpdateIconOverlay();
119 void ExtensionAppModelBuilder::OnBeginExtensionInstall(
120 const ExtensionInstallParams
& params
) {
121 if (!params
.is_app
|| params
.is_ephemeral
)
124 DVLOG(2) << service_
<< ": OnBeginExtensionInstall: "
125 << params
.extension_id
.substr(0, 8);
126 ExtensionAppItem
* existing_item
= GetExtensionAppItem(params
.extension_id
);
128 existing_item
->SetIsInstalling(true);
131 InsertApp(CreateAppItem(params
.extension_id
,
132 params
.extension_name
,
133 params
.installing_icon
,
134 params
.is_platform_app
));
135 SetHighlightedApp(params
.extension_id
);
138 void ExtensionAppModelBuilder::OnDownloadProgress(
139 const std::string
& extension_id
,
140 int percent_downloaded
) {
141 ExtensionAppItem
* item
= GetExtensionAppItem(extension_id
);
144 item
->SetPercentDownloaded(percent_downloaded
);
147 void ExtensionAppModelBuilder::OnInstallFailure(
148 const std::string
& extension_id
) {
149 model_
->DeleteItem(extension_id
);
152 void ExtensionAppModelBuilder::OnExtensionLoaded(const Extension
* extension
) {
153 if (!extension
->ShouldDisplayInAppLauncher())
156 DVLOG(2) << service_
<< ": OnExtensionLoaded: "
157 << extension
->id().substr(0, 8);
158 ExtensionAppItem
* existing_item
= GetExtensionAppItem(extension
->id());
160 existing_item
->Reload();
164 InsertApp(CreateAppItem(extension
->id(),
167 extension
->is_platform_app()));
171 void ExtensionAppModelBuilder::OnExtensionUnloaded(const Extension
* extension
) {
172 ExtensionAppItem
* item
= GetExtensionAppItem(extension
->id());
178 void ExtensionAppModelBuilder::OnExtensionUninstalled(
179 const Extension
* extension
) {
181 DVLOG(2) << service_
<< ": OnExtensionUninstalled: "
182 << extension
->id().substr(0, 8);
183 service_
->RemoveItem(extension
->id());
186 model_
->DeleteItem(extension
->id());
189 void ExtensionAppModelBuilder::OnAppsReordered() {
190 // Do nothing; App List order does not track extensions order.
193 void ExtensionAppModelBuilder::OnAppInstalledToAppList(
194 const std::string
& extension_id
) {
195 SetHighlightedApp(extension_id
);
198 void ExtensionAppModelBuilder::OnShutdown() {
200 tracker_
->RemoveObserver(this);
205 scoped_ptr
<ExtensionAppItem
> ExtensionAppModelBuilder::CreateAppItem(
206 const std::string
& extension_id
,
207 const std::string
& extension_name
,
208 const gfx::ImageSkia
& installing_icon
,
209 bool is_platform_app
) {
210 const app_list::AppListSyncableService::SyncItem
* sync_item
=
211 service_
? service_
->GetSyncItem(extension_id
) : NULL
;
212 return make_scoped_ptr(new ExtensionAppItem(profile_
,
220 void ExtensionAppModelBuilder::BuildModel() {
222 tracker_
= controller_
->GetInstallTrackerFor(profile_
);
227 // Start observing after model is built.
229 tracker_
->AddObserver(this);
232 void ExtensionAppModelBuilder::PopulateApps() {
233 extensions::ExtensionSet extensions
;
234 controller_
->GetApps(profile_
, &extensions
);
236 for (extensions::ExtensionSet::const_iterator app
= extensions
.begin();
237 app
!= extensions
.end(); ++app
) {
238 if (!ShouldDisplayInAppLauncher(profile_
, *app
))
240 InsertApp(CreateAppItem((*app
)->id(),
243 (*app
)->is_platform_app()));
247 void ExtensionAppModelBuilder::InsertApp(scoped_ptr
<ExtensionAppItem
> app
) {
249 service_
->AddItem(app
.PassAs
<app_list::AppListItem
>());
252 model_
->AddItem(app
.PassAs
<app_list::AppListItem
>());
255 void ExtensionAppModelBuilder::SetHighlightedApp(
256 const std::string
& extension_id
) {
257 if (extension_id
== highlight_app_id_
)
259 ExtensionAppItem
* old_app
= GetExtensionAppItem(highlight_app_id_
);
261 old_app
->SetHighlighted(false);
262 highlight_app_id_
= extension_id
;
263 ExtensionAppItem
* new_app
= GetExtensionAppItem(highlight_app_id_
);
264 highlighted_app_pending_
= !new_app
;
266 new_app
->SetHighlighted(true);
269 ExtensionAppItem
* ExtensionAppModelBuilder::GetExtensionAppItem(
270 const std::string
& extension_id
) {
271 app_list::AppListItem
* item
=
272 model_
->item_list()->FindItem(extension_id
);
273 LOG_IF(ERROR
, item
&&
274 item
->GetItemType() != ExtensionAppItem::kItemType
)
275 << "App Item matching id: " << extension_id
276 << " has incorrect type: '" << item
->GetItemType() << "'";
277 return static_cast<ExtensionAppItem
*>(item
);
280 void ExtensionAppModelBuilder::UpdateHighlight() {
282 if (!highlighted_app_pending_
|| highlight_app_id_
.empty())
284 ExtensionAppItem
* item
= GetExtensionAppItem(highlight_app_id_
);
287 item
->SetHighlighted(true);
288 highlighted_app_pending_
= false;
291 void ExtensionAppModelBuilder::OnListItemMoved(size_t from_index
,
293 app_list::AppListItem
* item
) {
294 // This will get called from AppListItemList::ListItemMoved after
295 // set_position is called for the item.
296 app_list::AppListItemList
* item_list
= model_
->item_list();
297 if (item
->GetItemType() != ExtensionAppItem::kItemType
)
303 ExtensionAppItem
* prev
= NULL
;
304 for (size_t idx
= to_index
; idx
> 0; --idx
) {
305 app_list::AppListItem
* item
= item_list
->item_at(idx
- 1);
306 if (item
->GetItemType() == ExtensionAppItem::kItemType
) {
307 prev
= static_cast<ExtensionAppItem
*>(item
);
311 ExtensionAppItem
* next
= NULL
;
312 for (size_t idx
= to_index
; idx
< item_list
->item_count() - 1; ++idx
) {
313 app_list::AppListItem
* item
= item_list
->item_at(idx
+ 1);
314 if (item
->GetItemType() == ExtensionAppItem::kItemType
) {
315 next
= static_cast<ExtensionAppItem
*>(item
);
319 // item->Move will call set_position, overriding the item's position.
321 static_cast<ExtensionAppItem
*>(item
)->Move(prev
, next
);