extension_app_model_builder.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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_->item_list()->AddObserver(this);
52  SwitchProfile(profile);  // Builds the model.
53}
54
55ExtensionAppModelBuilder::~ExtensionAppModelBuilder() {
56  OnShutdown();
57  model_->item_list()->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_->item_list()->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  model_->item_list()->DeleteItemsByType(ExtensionAppItem::kAppType);
166
167  if (tracker_)
168    tracker_->RemoveObserver(this);
169
170  if (extensions::ExtensionSystem::Get(profile_)->extension_service())
171    tracker_ = extensions::InstallTrackerFactory::GetForProfile(profile_);
172  else
173    tracker_ = NULL;
174
175  PopulateApps();
176  UpdateHighlight();
177
178  // Start observing after model is built.
179  if (tracker_)
180    tracker_->AddObserver(this);
181}
182
183void ExtensionAppModelBuilder::PopulateApps() {
184  ExtensionService* service =
185      extensions::ExtensionSystem::Get(profile_)->extension_service();
186  if (!service)
187    return;
188
189  ExtensionAppList apps;
190  AddApps(service->extensions(), &apps);
191  AddApps(service->disabled_extensions(), &apps);
192  AddApps(service->terminated_extensions(), &apps);
193
194  if (apps.empty())
195    return;
196
197  for (size_t i = 0; i < apps.size(); ++i)
198    InsertApp(apps[i]);
199}
200
201void ExtensionAppModelBuilder::InsertApp(ExtensionAppItem* app) {
202  model_->item_list()->AddItem(app);
203}
204
205void ExtensionAppModelBuilder::SetHighlightedApp(
206    const std::string& extension_id) {
207  if (extension_id == highlight_app_id_)
208    return;
209  ExtensionAppItem* old_app = GetExtensionAppItem(highlight_app_id_);
210  if (old_app)
211    old_app->SetHighlighted(false);
212  highlight_app_id_ = extension_id;
213  ExtensionAppItem* new_app = GetExtensionAppItem(highlight_app_id_);
214  highlighted_app_pending_ = !new_app;
215  if (new_app)
216    new_app->SetHighlighted(true);
217}
218
219ExtensionAppItem* ExtensionAppModelBuilder::GetExtensionAppItem(
220    const std::string& extension_id) {
221  app_list::AppListItemModel* item =
222      model_->item_list()->FindItem(extension_id);
223  LOG_IF(ERROR, item &&
224         item->GetAppType() != ExtensionAppItem::kAppType)
225      << "App Item matching id: " << extension_id
226      << " has incorrect type: '" << item->GetAppType() << "'";
227  return static_cast<ExtensionAppItem*>(item);
228}
229
230void ExtensionAppModelBuilder::UpdateHighlight() {
231  DCHECK(model_);
232  if (!highlighted_app_pending_ || highlight_app_id_.empty())
233    return;
234  ExtensionAppItem* item = GetExtensionAppItem(highlight_app_id_);
235  if (!item)
236    return;
237  item->SetHighlighted(true);
238  highlighted_app_pending_ = false;
239}
240
241void ExtensionAppModelBuilder::OnListItemMoved(
242    size_t from_index,
243    size_t to_index,
244    app_list::AppListItemModel* item) {
245  // This will get called from AppListItemList::ListItemMoved after
246  // set_position is called for the item.
247  app_list::AppListItemList* item_list = model_->item_list();
248  if (item->GetAppType() != ExtensionAppItem::kAppType)
249    return;
250
251  ExtensionAppItem* prev = NULL;
252  for (size_t idx = to_index; idx > 0; --idx) {
253    app_list::AppListItemModel* item = item_list->item_at(idx - 1);
254    if (item->GetAppType() == ExtensionAppItem::kAppType) {
255      prev = static_cast<ExtensionAppItem*>(item);
256      break;
257    }
258  }
259  ExtensionAppItem* next = NULL;
260  for (size_t idx = to_index; idx < item_list->item_count() - 1; ++idx) {
261    app_list::AppListItemModel* item = item_list->item_at(idx + 1);
262    if (item->GetAppType() == ExtensionAppItem::kAppType) {
263      next = static_cast<ExtensionAppItem*>(item);
264      break;
265    }
266  }
267  // item->Move will call set_position, overriding the item's position.
268  if (prev || next)
269    static_cast<ExtensionAppItem*>(item)->Move(prev, next);
270}
271