background_application_list_model.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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/background_application_list_model.h"
6
7#include <algorithm>
8
9#include "app/l10n_util_collator.h"
10#include "base/stl_util-inl.h"
11#include "base/utf_string_conversions.h"
12#include "chrome/browser/background_mode_manager.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/extensions/extension_prefs.h"
15#include "chrome/browser/extensions/extensions_service.h"
16#include "chrome/browser/extensions/image_loading_tracker.h"
17#include "chrome/browser/prefs/pref_service.h"
18#include "chrome/browser/profile.h"
19#include "chrome/common/extensions/extension.h"
20#include "chrome/common/extensions/extension_resource.h"
21#include "chrome/common/notification_service.h"
22#include "chrome/common/pref_names.h"
23
24class ExtensionNameComparator {
25 public:
26  explicit ExtensionNameComparator(icu::Collator* collator);
27  bool operator()(const Extension* x, const Extension* y);
28
29 private:
30  icu::Collator* collator_;
31};
32
33ExtensionNameComparator::ExtensionNameComparator(icu::Collator* collator)
34  : collator_(collator) {
35}
36
37bool ExtensionNameComparator::operator()(const Extension* x,
38                                         const Extension* y) {
39  return l10n_util::StringComparator<string16>(collator_)(
40    UTF8ToUTF16(x->name()),
41    UTF8ToUTF16(y->name()));
42}
43
44// Background application representation, private to the
45// BackgroundApplicationListModel class.
46class BackgroundApplicationListModel::Application
47  : public ImageLoadingTracker::Observer {
48 public:
49  Application(BackgroundApplicationListModel* model,
50              const Extension* an_extension);
51
52  virtual ~Application();
53
54  // Invoked when a request icon is available.
55  virtual void OnImageLoaded(SkBitmap* image,
56                             ExtensionResource resource,
57                             int index);
58
59  // Uses the FILE thread to request this extension's icon, sized
60  // appropriately.
61  void RequestIcon(Extension::Icons size);
62
63  const Extension* extension_;
64  scoped_ptr<SkBitmap> icon_;
65  BackgroundApplicationListModel* model_;
66  ImageLoadingTracker tracker_;
67};
68
69namespace {
70void GetServiceApplications(ExtensionsService* service,
71                            ExtensionList* applications_result) {
72  const ExtensionList* extensions = service->extensions();
73
74  for (ExtensionList::const_iterator cursor = extensions->begin();
75       cursor != extensions->end();
76       ++cursor) {
77    const Extension* extension = *cursor;
78    if (BackgroundApplicationListModel::IsBackgroundApp(*extension))
79      applications_result->push_back(extension);
80  }
81  std::string locale = g_browser_process->GetApplicationLocale();
82  icu::Locale loc(locale.c_str());
83  UErrorCode error = U_ZERO_ERROR;
84  scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error));
85  sort(applications_result->begin(), applications_result->end(),
86       ExtensionNameComparator(collator.get()));
87}
88
89bool HasBackgroundAppPermission(
90    const std::set<std::string>& api_permissions) {
91  return Extension::HasApiPermission(
92      api_permissions, Extension::kBackgroundPermission);
93}
94}  // namespace
95
96void
97BackgroundApplicationListModel::Observer::OnApplicationDataChanged(
98    const Extension* extension) {
99}
100
101void
102BackgroundApplicationListModel::Observer::OnApplicationListChanged() {
103}
104
105BackgroundApplicationListModel::Observer::~Observer() {
106}
107
108BackgroundApplicationListModel::Application::~Application() {
109}
110
111BackgroundApplicationListModel::Application::Application(
112    BackgroundApplicationListModel* model,
113    const Extension* extension)
114    : extension_(extension),
115      icon_(NULL),
116      model_(model),
117      ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) {
118}
119
120void BackgroundApplicationListModel::Application::OnImageLoaded(
121    SkBitmap* image,
122    ExtensionResource resource,
123    int index) {
124  if (!image)
125    return;
126  icon_.reset(new SkBitmap(*image));
127  model_->OnApplicationDataChanged(extension_);
128}
129
130void BackgroundApplicationListModel::Application::RequestIcon(
131    Extension::Icons size) {
132  ExtensionResource resource = extension_->GetIconResource(
133      size, ExtensionIconSet::MATCH_BIGGER);
134  tracker_.LoadImage(extension_, resource, gfx::Size(size, size),
135                     ImageLoadingTracker::CACHE);
136}
137
138BackgroundApplicationListModel::~BackgroundApplicationListModel() {
139  STLDeleteContainerPairSecondPointers(applications_.begin(),
140                                       applications_.end());
141}
142
143BackgroundApplicationListModel::BackgroundApplicationListModel(Profile* profile)
144    : profile_(profile) {
145  DCHECK(profile_);
146  registrar_.Add(this,
147                 NotificationType::EXTENSION_LOADED,
148                 Source<Profile>(profile));
149  registrar_.Add(this,
150                 NotificationType::EXTENSION_UNLOADED,
151                 Source<Profile>(profile));
152  registrar_.Add(this,
153                 NotificationType::EXTENSION_UNLOADED_DISABLED,
154                 Source<Profile>(profile));
155  registrar_.Add(this,
156                 NotificationType::EXTENSIONS_READY,
157                 Source<Profile>(profile));
158}
159
160void BackgroundApplicationListModel::AddObserver(Observer* observer) {
161  observers_.AddObserver(observer);
162}
163
164void BackgroundApplicationListModel::AssociateApplicationData(
165    const Extension* extension) {
166  DCHECK(IsBackgroundApp(*extension));
167  Application* application = FindApplication(extension);
168  if (!application) {
169    application = new Application(this, extension);
170    applications_[extension->id()] = application;
171    application->RequestIcon(Extension::EXTENSION_ICON_BITTY);
172  }
173}
174
175void BackgroundApplicationListModel::DissociateApplicationData(
176    const Extension* extension) {
177  ApplicationMap::iterator found = applications_.find(extension->id());
178  if (found != applications_.end()) {
179    delete found->second;
180    applications_.erase(found);
181  }
182}
183
184const Extension* BackgroundApplicationListModel::GetExtension(
185    int position) const {
186  DCHECK(position >= 0 && static_cast<size_t>(position) < extensions_.size());
187  return extensions_[position];
188}
189
190const BackgroundApplicationListModel::Application*
191BackgroundApplicationListModel::FindApplication(
192    const Extension* extension) const {
193  const std::string& id = extension->id();
194  ApplicationMap::const_iterator found = applications_.find(id);
195  return (found == applications_.end()) ? NULL : found->second;
196}
197
198BackgroundApplicationListModel::Application*
199BackgroundApplicationListModel::FindApplication(const Extension* extension) {
200  const std::string& id = extension->id();
201  ApplicationMap::iterator found = applications_.find(id);
202  return (found == applications_.end()) ? NULL : found->second;
203}
204
205const SkBitmap* BackgroundApplicationListModel::GetIcon(
206    const Extension* extension) {
207  const Application* application = FindApplication(extension);
208  if (application)
209    return application->icon_.get();
210  AssociateApplicationData(extension);
211  return NULL;
212}
213
214int BackgroundApplicationListModel::GetPosition(
215    const Extension* extension) const {
216  int position = 0;
217  const std::string& id = extension->id();
218  for (ExtensionList::const_iterator cursor = extensions_.begin();
219       cursor != extensions_.end();
220       ++cursor, ++position) {
221    if (id == cursor->get()->id())
222      return position;
223  }
224  NOTREACHED();
225  return -1;
226}
227
228// static
229bool BackgroundApplicationListModel::IsBackgroundApp(
230    const Extension& extension) {
231  return HasBackgroundAppPermission(extension.api_permissions());
232}
233
234void BackgroundApplicationListModel::Observe(
235    NotificationType type,
236    const NotificationSource& source,
237    const NotificationDetails& details) {
238  if (type == NotificationType::EXTENSIONS_READY) {
239    Update();
240    return;
241  }
242  ExtensionsService* service = profile_->GetExtensionsService();
243  if (!service || !service->is_ready())
244    return;
245  switch (type.value) {
246    case NotificationType::EXTENSION_LOADED:
247      OnExtensionLoaded(Details<Extension>(details).ptr());
248      break;
249    case NotificationType::EXTENSION_UNLOADED:
250      // Handle extension unload uniformly, falling through to next case.
251    case NotificationType::EXTENSION_UNLOADED_DISABLED:
252      OnExtensionUnloaded(Details<Extension>(details).ptr());
253      break;
254    default:
255      NOTREACHED() << "Received unexpected notification";
256  }
257}
258
259void BackgroundApplicationListModel::OnApplicationDataChanged(
260    const Extension* extension) {
261  FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension));
262}
263
264void BackgroundApplicationListModel::OnExtensionLoaded(Extension* extension) {
265  // We only care about extensions that are background applications
266  if (!IsBackgroundApp(*extension))
267    return;
268  AssociateApplicationData(extension);
269  Update();
270}
271
272void BackgroundApplicationListModel::OnExtensionUnloaded(Extension* extension) {
273  if (!IsBackgroundApp(*extension))
274    return;
275  Update();
276  DissociateApplicationData(extension);
277}
278
279void BackgroundApplicationListModel::RemoveObserver(Observer* observer) {
280  observers_.RemoveObserver(observer);
281}
282
283// Update queries the extensions service of the profile with which the model was
284// initialized to determine the current set of background applications.  If that
285// differs from the old list, it generates OnApplicationListChanged events for
286// each observer.
287void BackgroundApplicationListModel::Update() {
288  ExtensionsService* service = profile_->GetExtensionsService();
289  DCHECK(service->is_ready());
290
291  // Discover current background applications, compare with previous list, which
292  // is consistently sorted, and notify observers if they differ.
293  ExtensionList extensions;
294  GetServiceApplications(service, &extensions);
295  ExtensionList::const_iterator old_cursor = extensions_.begin();
296  ExtensionList::const_iterator new_cursor = extensions.begin();
297  while (old_cursor != extensions_.end() &&
298         new_cursor != extensions.end() &&
299         (*old_cursor)->name() == (*new_cursor)->name() &&
300         (*old_cursor)->id() == (*new_cursor)->id()) {
301    ++old_cursor;
302    ++new_cursor;
303  }
304  if (old_cursor != extensions_.end() || new_cursor != extensions.end()) {
305    extensions_ = extensions;
306    FOR_EACH_OBSERVER(Observer, observers_, OnApplicationListChanged());
307  }
308}
309