extension_app_model_builder.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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/prefs/pref_service.h"
11#include "chrome/browser/chrome_notification_types.h"
12#include "chrome/browser/extensions/extension_prefs.h"
13#include "chrome/browser/extensions/extension_service.h"
14#include "chrome/browser/extensions/extension_sorting.h"
15#include "chrome/browser/extensions/extension_system.h"
16#include "chrome/browser/extensions/install_tracker.h"
17#include "chrome/browser/extensions/install_tracker_factory.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/browser/ui/app_list/extension_app_item.h"
20#include "chrome/common/extensions/extension.h"
21#include "chrome/common/extensions/extension_constants.h"
22#include "chrome/common/pref_names.h"
23#include "content/public/browser/notification_service.h"
24#include "ui/gfx/image/image_skia.h"
25
26using extensions::Extension;
27
28namespace {
29
30bool ShouldDisplayInAppLauncher(Profile* profile,
31                                scoped_refptr<const Extension> app) {
32  // If it's the web store, check the policy.
33  bool blocked_by_policy =
34      (app->id() == extension_misc::kWebStoreAppId ||
35       app->id() == extension_misc::kEnterpriseWebStoreAppId) &&
36      profile->GetPrefs()->GetBoolean(prefs::kHideWebStoreIcon);
37  return app->ShouldDisplayInAppLauncher() && !blocked_by_policy;
38}
39
40}  // namespace
41
42ExtensionAppModelBuilder::ExtensionAppModelBuilder(
43    Profile* profile,
44    app_list::AppListModel* model,
45    AppListControllerDelegate* controller)
46    : profile_(NULL),
47      controller_(controller),
48      model_(model),
49      highlighted_app_pending_(false),
50      tracker_(NULL) {
51  model_->apps()->AddObserver(this);
52  SwitchProfile(profile);  // Builds the model.
53}
54
55ExtensionAppModelBuilder::~ExtensionAppModelBuilder() {
56  OnShutdown();
57  model_->apps()->RemoveObserver(this);
58}
59
60void ExtensionAppModelBuilder::OnBeginExtensionInstall(
61    const std::string& extension_id,
62    const std::string& extension_name,
63    const gfx::ImageSkia& installing_icon,
64    bool is_app,
65    bool is_platform_app) {
66  if (!is_app)
67    return;
68
69  ExtensionAppItem* existing_item = GetExtensionAppItem(extension_id);
70  if (existing_item) {
71    existing_item->SetIsInstalling(true);
72    return;
73  }
74
75  InsertApp(new ExtensionAppItem(profile_,
76                                 extension_id,
77                                 controller_,
78                                 extension_name,
79                                 installing_icon,
80                                 is_platform_app));
81  SetHighlightedApp(extension_id);
82}
83
84void ExtensionAppModelBuilder::OnDownloadProgress(
85    const std::string& extension_id,
86    int percent_downloaded) {
87  ExtensionAppItem* item = GetExtensionAppItem(extension_id);
88  if (!item)
89    return;
90  item->SetPercentDownloaded(percent_downloaded);
91}
92
93void ExtensionAppModelBuilder::OnInstallFailure(
94    const std::string& extension_id) {
95  // Do nothing, item will be disabled
96}
97
98void ExtensionAppModelBuilder::OnExtensionLoaded(const Extension* extension) {
99  if (!extension->ShouldDisplayInAppLauncher())
100    return;
101
102  ExtensionAppItem* existing_item = GetExtensionAppItem(extension->id());
103  if (existing_item) {
104    existing_item->Reload();
105    return;
106  }
107
108  InsertApp(new ExtensionAppItem(profile_,
109                                 extension->id(),
110                                 controller_,
111                                 "",
112                                 gfx::ImageSkia(),
113                                 extension->is_platform_app()));
114  UpdateHighlight();
115}
116
117void ExtensionAppModelBuilder::OnExtensionUnloaded(const Extension* extension) {
118  ExtensionAppItem* item = GetExtensionAppItem(extension->id());
119  if (!item)
120    return;
121  item->UpdateIcon();
122}
123
124void ExtensionAppModelBuilder::OnExtensionUninstalled(
125    const Extension* extension) {
126  model_->DeleteItem(extension->id());
127}
128
129void ExtensionAppModelBuilder::OnAppsReordered() {
130  // Do nothing; App List order does not track extensions order.
131}
132
133void ExtensionAppModelBuilder::OnAppInstalledToAppList(
134    const std::string& extension_id) {
135  SetHighlightedApp(extension_id);
136}
137
138void ExtensionAppModelBuilder::OnShutdown() {
139  if (tracker_) {
140    tracker_->RemoveObserver(this);
141    tracker_ = NULL;
142  }
143}
144
145void ExtensionAppModelBuilder::AddApps(const ExtensionSet* extensions,
146                                       ExtensionAppList* apps) {
147  for (ExtensionSet::const_iterator app = extensions->begin();
148       app != extensions->end(); ++app) {
149    if (ShouldDisplayInAppLauncher(profile_, *app))
150      apps->push_back(new ExtensionAppItem(profile_,
151                                           (*app)->id(),
152                                           controller_,
153                                           "",
154                                           gfx::ImageSkia(),
155                                           (*app)->is_platform_app()));
156  }
157}
158
159void ExtensionAppModelBuilder::SwitchProfile(Profile* profile) {
160  if (profile_ == profile)
161    return;
162  profile_ = profile;
163
164  // Delete any extension apps.
165  app_list::AppListModel::Apps* app_list = model_->apps();
166  for (int i = static_cast<int>(app_list->item_count()) - 1; i >= 0; --i) {
167    app_list::AppListItemModel* item = app_list->GetItemAt(i);
168    if (item->GetAppType() == ExtensionAppItem::kAppType)
169      app_list->DeleteAt(i);
170  }
171
172  if (tracker_)
173    tracker_->RemoveObserver(this);
174
175  if (extensions::ExtensionSystem::Get(profile_)->extension_service())
176    tracker_ = extensions::InstallTrackerFactory::GetForProfile(profile_);
177  else
178    tracker_ = NULL;
179
180  PopulateApps();
181  UpdateHighlight();
182
183  // Start observing after model is built.
184  if (tracker_)
185    tracker_->AddObserver(this);
186}
187
188void ExtensionAppModelBuilder::PopulateApps() {
189  ExtensionService* service =
190      extensions::ExtensionSystem::Get(profile_)->extension_service();
191  if (!service)
192    return;
193
194  ExtensionAppList apps;
195  AddApps(service->extensions(), &apps);
196  AddApps(service->disabled_extensions(), &apps);
197  AddApps(service->terminated_extensions(), &apps);
198
199  if (apps.empty())
200    return;
201
202  for (size_t i = 0; i < apps.size(); ++i)
203    InsertApp(apps[i]);
204}
205
206void ExtensionAppModelBuilder::InsertApp(ExtensionAppItem* app) {
207  model_->AddItem(app);
208}
209
210void ExtensionAppModelBuilder::SetHighlightedApp(
211    const std::string& extension_id) {
212  if (extension_id == highlight_app_id_)
213    return;
214  ExtensionAppItem* old_app = GetExtensionAppItem(highlight_app_id_);
215  if (old_app)
216    old_app->SetHighlighted(false);
217  highlight_app_id_ = extension_id;
218  ExtensionAppItem* new_app = GetExtensionAppItem(highlight_app_id_);
219  highlighted_app_pending_ = !new_app;
220  if (new_app)
221    new_app->SetHighlighted(true);
222}
223
224ExtensionAppItem* ExtensionAppModelBuilder::GetExtensionAppItem(
225    const std::string& extension_id) {
226  app_list::AppListItemModel* item = model_->FindItem(extension_id);
227  LOG_IF(ERROR, item &&
228         item->GetAppType() != ExtensionAppItem::kAppType)
229      << "App Item matching id: " << extension_id
230      << " has incorrect type: '" << item->GetAppType() << "'";
231  return static_cast<ExtensionAppItem*>(item);
232}
233
234void ExtensionAppModelBuilder::UpdateHighlight() {
235  DCHECK(model_);
236  if (!highlighted_app_pending_ || highlight_app_id_.empty())
237    return;
238  ExtensionAppItem* item = GetExtensionAppItem(highlight_app_id_);
239  if (!item)
240    return;
241  item->SetHighlighted(true);
242  highlighted_app_pending_ = false;
243}
244
245void ExtensionAppModelBuilder::ListItemsAdded(size_t start, size_t count) {
246}
247
248void ExtensionAppModelBuilder::ListItemsRemoved(size_t start, size_t count) {
249}
250
251void ExtensionAppModelBuilder::ListItemMoved(size_t index,
252                                             size_t target_index) {
253  app_list::AppListModel::Apps* app_list = model_->apps();
254  app_list::AppListItemModel* item = app_list->GetItemAt(target_index);
255  if (item->GetAppType() != ExtensionAppItem::kAppType)
256    return;
257
258  ExtensionAppItem* prev = NULL;
259  for (size_t idx = target_index; idx > 1; --idx) {
260    app_list::AppListItemModel* item = app_list->GetItemAt(idx - 1);
261    if (item->GetAppType() == ExtensionAppItem::kAppType) {
262      prev = static_cast<ExtensionAppItem*>(item);
263      break;
264    }
265  }
266  ExtensionAppItem* next = NULL;
267  for (size_t idx = target_index; idx < app_list->item_count() - 1; ++idx) {
268    app_list::AppListItemModel* item = app_list->GetItemAt(idx + 1);
269    if (item->GetAppType() == ExtensionAppItem::kAppType) {
270      next = static_cast<ExtensionAppItem*>(item);
271      break;
272    }
273  }
274  if (prev || next)
275    static_cast<ExtensionAppItem*>(item)->Move(prev, next);
276}
277
278void ExtensionAppModelBuilder::ListItemsChanged(size_t start, size_t count) {
279  NOTREACHED();
280}
281