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