Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / extension_app_model_builder.cc
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 "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"
32
33 using extensions::Extension;
34
35 ExtensionAppModelBuilder::ExtensionAppModelBuilder(
36     AppListControllerDelegate* controller)
37     : service_(NULL),
38       profile_(NULL),
39       controller_(controller),
40       model_(NULL),
41       tracker_(NULL),
42       extension_registry_(NULL) {
43 }
44
45 ExtensionAppModelBuilder::~ExtensionAppModelBuilder() {
46   OnShutdown();
47   OnShutdown(extension_registry_);
48   if (!service_)
49     model_->top_level_item_list()->RemoveObserver(this);
50 }
51
52 void ExtensionAppModelBuilder::InitializeWithService(
53     app_list::AppListSyncableService* service) {
54   DCHECK(!service_ && !profile_);
55   model_ = service->model();
56   service_ = service;
57   profile_ = service->profile();
58   InitializePrefChangeRegistrars();
59
60   BuildModel();
61 }
62
63 void ExtensionAppModelBuilder::InitializeWithProfile(
64     Profile* profile,
65     app_list::AppListModel* model) {
66   DCHECK(!service_ && !profile_);
67   model_ = model;
68   model_->top_level_item_list()->AddObserver(this);
69   profile_ = profile;
70   InitializePrefChangeRegistrars();
71
72   BuildModel();
73 }
74
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)));
81
82   if (!extensions::util::IsStreamlinedHostedAppsEnabled())
83     return;
84
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)));
95 }
96
97 void ExtensionAppModelBuilder::OnProfilePreferenceChanged() {
98   extensions::ExtensionSet extensions;
99   controller_->GetApps(profile_, &extensions);
100
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;
106
107     if (should_display == does_display)
108       continue;
109
110     if (should_display) {
111       InsertApp(CreateAppItem((*app)->id(),
112                               "",
113                               gfx::ImageSkia(),
114                               (*app)->is_platform_app()));
115     } else {
116       if (service_)
117         service_->RemoveItem((*app)->id());
118       else
119         model_->DeleteItem((*app)->id());
120     }
121   }
122 }
123
124 void ExtensionAppModelBuilder::OnExtensionPreferenceChanged() {
125   model_->NotifyExtensionPreferenceChanged();
126 }
127
128 void ExtensionAppModelBuilder::OnBeginExtensionInstall(
129     const ExtensionInstallParams& params) {
130   if (!params.is_app || params.is_ephemeral)
131     return;
132
133   DVLOG(2) << service_ << ": OnBeginExtensionInstall: "
134            << params.extension_id.substr(0, 8);
135   ExtensionAppItem* existing_item = GetExtensionAppItem(params.extension_id);
136   if (existing_item) {
137     existing_item->SetIsInstalling(true);
138     return;
139   }
140
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));
148
149   InsertApp(CreateAppItem(params.extension_id,
150                           params.extension_name,
151                           resized,
152                           params.is_platform_app));
153 }
154
155 void ExtensionAppModelBuilder::OnDownloadProgress(
156     const std::string& extension_id,
157     int percent_downloaded) {
158   ExtensionAppItem* item = GetExtensionAppItem(extension_id);
159   if (!item)
160     return;
161   item->SetPercentDownloaded(percent_downloaded);
162 }
163
164 void ExtensionAppModelBuilder::OnInstallFailure(
165     const std::string& extension_id) {
166   model_->DeleteItem(extension_id);
167 }
168
169 void ExtensionAppModelBuilder::OnExtensionLoaded(
170     content::BrowserContext* browser_context,
171     const extensions::Extension* extension) {
172   if (!extensions::ui_util::ShouldDisplayInAppLauncher(extension, profile_))
173     return;
174
175   DVLOG(2) << service_ << ": OnExtensionLoaded: "
176            << extension->id().substr(0, 8);
177   ExtensionAppItem* existing_item = GetExtensionAppItem(extension->id());
178   if (existing_item) {
179     existing_item->Reload();
180     if (service_)
181       service_->UpdateItem(existing_item);
182     return;
183   }
184
185   InsertApp(CreateAppItem(extension->id(),
186                           "",
187                           gfx::ImageSkia(),
188                           extension->is_platform_app()));
189 }
190
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());
196   if (!item)
197     return;
198   item->UpdateIcon();
199 }
200
201 void ExtensionAppModelBuilder::OnExtensionUninstalled(
202     content::BrowserContext* browser_context,
203     const extensions::Extension* extension,
204     extensions::UninstallReason reason) {
205   if (service_) {
206     DVLOG(2) << service_ << ": OnExtensionUninstalled: "
207              << extension->id().substr(0, 8);
208     service_->RemoveItem(extension->id());
209     return;
210   }
211   model_->DeleteItem(extension->id());
212 }
213
214 void ExtensionAppModelBuilder::OnDisabledExtensionUpdated(
215     const Extension* extension) {
216   if (!extensions::ui_util::ShouldDisplayInAppLauncher(extension, profile_))
217     return;
218
219   ExtensionAppItem* existing_item = GetExtensionAppItem(extension->id());
220   if (existing_item)
221     existing_item->Reload();
222 }
223
224 void ExtensionAppModelBuilder::OnShutdown() {
225   if (tracker_) {
226     tracker_->RemoveObserver(this);
227     tracker_ = NULL;
228   }
229 }
230
231 void ExtensionAppModelBuilder::OnShutdown(
232     extensions::ExtensionRegistry* registry) {
233   if (!extension_registry_)
234     return;
235
236   DCHECK_EQ(extension_registry_, registry);
237   extension_registry_->RemoveObserver(this);
238   extension_registry_ = NULL;
239 }
240
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_,
249                                               sync_item,
250                                               extension_id,
251                                               extension_name,
252                                               installing_icon,
253                                               is_platform_app));
254 }
255
256 void ExtensionAppModelBuilder::BuildModel() {
257   DCHECK(!tracker_);
258   tracker_ = controller_->GetInstallTrackerFor(profile_);
259   extension_registry_ = extensions::ExtensionRegistry::Get(profile_);
260
261   PopulateApps();
262
263   // Start observing after model is built.
264   if (tracker_)
265     tracker_->AddObserver(this);
266
267   if (extension_registry_)
268     extension_registry_->AddObserver(this);
269 }
270
271 void ExtensionAppModelBuilder::PopulateApps() {
272   extensions::ExtensionSet extensions;
273   controller_->GetApps(profile_, &extensions);
274
275   for (extensions::ExtensionSet::const_iterator app = extensions.begin();
276        app != extensions.end(); ++app) {
277     if (!extensions::ui_util::ShouldDisplayInAppLauncher(app->get(), profile_))
278       continue;
279     InsertApp(CreateAppItem((*app)->id(),
280                             "",
281                             gfx::ImageSkia(),
282                             (*app)->is_platform_app()));
283   }
284 }
285
286 void ExtensionAppModelBuilder::InsertApp(scoped_ptr<ExtensionAppItem> app) {
287   if (service_) {
288     service_->AddItem(app.Pass());
289     return;
290   }
291   model_->AddItem(app.Pass());
292 }
293
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);
302 }
303
304 void ExtensionAppModelBuilder::OnListItemMoved(size_t from_index,
305                                                size_t to_index,
306                                                app_list::AppListItem* item) {
307   DCHECK(!service_);
308
309   // This will get called from AppListItemList::ListItemMoved after
310   // set_position is called for the item.
311   if (item->GetItemType() != ExtensionAppItem::kItemType)
312     return;
313
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);
320       break;
321     }
322   }
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);
328       break;
329     }
330   }
331   // item->Move will call set_position, overriding the item's position.
332   if (prev || next)
333     static_cast<ExtensionAppItem*>(item)->Move(prev, next);
334 }