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