Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / background / background_application_list_model.cc
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/background/background_application_list_model.h"
6
7 #include <algorithm>
8 #include <set>
9
10 #include "base/sha1.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/app/chrome_command_ids.h"
15 #include "chrome/browser/background/background_contents_service.h"
16 #include "chrome/browser/background/background_contents_service_factory.h"
17 #include "chrome/browser/background/background_mode_manager.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/image_loader.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/common/extensions/extension_constants.h"
24 #include "chrome/common/extensions/extension_icon_set.h"
25 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_source.h"
28 #include "extensions/browser/extension_prefs.h"
29 #include "extensions/browser/extension_registry.h"
30 #include "extensions/browser/extension_system.h"
31 #include "extensions/common/extension.h"
32 #include "extensions/common/extension_resource.h"
33 #include "extensions/common/extension_set.h"
34 #include "extensions/common/manifest_handlers/background_info.h"
35 #include "extensions/common/permissions/permission_set.h"
36 #include "ui/base/l10n/l10n_util_collator.h"
37 #include "ui/gfx/image/image.h"
38 #include "ui/gfx/image/image_skia.h"
39
40 using extensions::APIPermission;
41 using extensions::Extension;
42 using extensions::ExtensionList;
43 using extensions::ExtensionRegistry;
44 using extensions::ExtensionSet;
45 using extensions::PermissionSet;
46 using extensions::UnloadedExtensionInfo;
47 using extensions::UpdatedExtensionPermissionsInfo;
48
49 class ExtensionNameComparator {
50  public:
51   explicit ExtensionNameComparator(icu::Collator* collator);
52   bool operator()(const scoped_refptr<const Extension>& x,
53                   const scoped_refptr<const Extension>& y);
54
55  private:
56   icu::Collator* collator_;
57 };
58
59 ExtensionNameComparator::ExtensionNameComparator(icu::Collator* collator)
60   : collator_(collator) {
61 }
62
63 bool ExtensionNameComparator::operator()(
64     const scoped_refptr<const Extension>& x,
65     const scoped_refptr<const Extension>& y) {
66   return l10n_util::StringComparator<base::string16>(collator_)(
67       base::UTF8ToUTF16(x->name()), base::UTF8ToUTF16(y->name()));
68 }
69
70 // Background application representation, private to the
71 // BackgroundApplicationListModel class.
72 class BackgroundApplicationListModel::Application
73     : public base::SupportsWeakPtr<Application> {
74  public:
75   Application(BackgroundApplicationListModel* model,
76               const Extension* an_extension);
77
78   virtual ~Application();
79
80   // Invoked when a request icon is available.
81   void OnImageLoaded(const gfx::Image& image);
82
83   // Uses the FILE thread to request this extension's icon, sized
84   // appropriately.
85   void RequestIcon(extension_misc::ExtensionIcons size);
86
87   const Extension* extension_;
88   scoped_ptr<gfx::ImageSkia> icon_;
89   BackgroundApplicationListModel* model_;
90 };
91
92 namespace {
93 void GetServiceApplications(ExtensionService* service,
94                             ExtensionList* applications_result) {
95   ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
96   const ExtensionSet& enabled_extensions = registry->enabled_extensions();
97
98   for (ExtensionSet::const_iterator cursor = enabled_extensions.begin();
99        cursor != enabled_extensions.end();
100        ++cursor) {
101     const Extension* extension = cursor->get();
102     if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
103                                                         service->profile())) {
104       applications_result->push_back(extension);
105     }
106   }
107
108   // Walk the list of terminated extensions also (just because an extension
109   // crashed doesn't mean we should ignore it).
110   const ExtensionSet& terminated_extensions = registry->terminated_extensions();
111   for (ExtensionSet::const_iterator cursor = terminated_extensions.begin();
112        cursor != terminated_extensions.end();
113        ++cursor) {
114     const Extension* extension = cursor->get();
115     if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
116                                                         service->profile())) {
117       applications_result->push_back(extension);
118     }
119   }
120
121   std::string locale = g_browser_process->GetApplicationLocale();
122   icu::Locale loc(locale.c_str());
123   UErrorCode error = U_ZERO_ERROR;
124   scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error));
125   std::sort(applications_result->begin(), applications_result->end(),
126        ExtensionNameComparator(collator.get()));
127 }
128
129 }  // namespace
130
131 void
132 BackgroundApplicationListModel::Observer::OnApplicationDataChanged(
133     const Extension* extension, Profile* profile) {
134 }
135
136 void
137 BackgroundApplicationListModel::Observer::OnApplicationListChanged(
138     Profile* profile) {
139 }
140
141 BackgroundApplicationListModel::Observer::~Observer() {
142 }
143
144 BackgroundApplicationListModel::Application::~Application() {
145 }
146
147 BackgroundApplicationListModel::Application::Application(
148     BackgroundApplicationListModel* model,
149     const Extension* extension)
150     : extension_(extension), model_(model) {}
151
152 void BackgroundApplicationListModel::Application::OnImageLoaded(
153     const gfx::Image& image) {
154   if (image.IsEmpty())
155     return;
156   icon_.reset(image.CopyImageSkia());
157   model_->SendApplicationDataChangedNotifications(extension_);
158 }
159
160 void BackgroundApplicationListModel::Application::RequestIcon(
161     extension_misc::ExtensionIcons size) {
162   extensions::ExtensionResource resource =
163       extensions::IconsInfo::GetIconResource(
164           extension_, size, ExtensionIconSet::MATCH_BIGGER);
165   extensions::ImageLoader::Get(model_->profile_)->LoadImageAsync(
166       extension_, resource, gfx::Size(size, size),
167       base::Bind(&Application::OnImageLoaded, AsWeakPtr()));
168 }
169
170 BackgroundApplicationListModel::~BackgroundApplicationListModel() {
171   STLDeleteContainerPairSecondPointers(applications_.begin(),
172                                        applications_.end());
173 }
174
175 BackgroundApplicationListModel::BackgroundApplicationListModel(Profile* profile)
176     : profile_(profile) {
177   DCHECK(profile_);
178   registrar_.Add(this,
179                  chrome::NOTIFICATION_EXTENSION_LOADED,
180                  content::Source<Profile>(profile));
181   registrar_.Add(this,
182                  chrome::NOTIFICATION_EXTENSION_UNLOADED,
183                  content::Source<Profile>(profile));
184   registrar_.Add(this,
185                  chrome::NOTIFICATION_EXTENSIONS_READY,
186                  content::Source<Profile>(profile));
187   registrar_.Add(this,
188                  chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
189                  content::Source<Profile>(profile));
190   registrar_.Add(this,
191                  chrome::NOTIFICATION_BACKGROUND_CONTENTS_SERVICE_CHANGED,
192                  content::Source<Profile>(profile));
193   ExtensionService* service = extensions::ExtensionSystem::Get(profile)->
194       extension_service();
195   if (service && service->is_ready())
196     Update();
197 }
198
199 void BackgroundApplicationListModel::AddObserver(Observer* observer) {
200   observers_.AddObserver(observer);
201 }
202
203 void BackgroundApplicationListModel::AssociateApplicationData(
204     const Extension* extension) {
205   DCHECK(IsBackgroundApp(*extension, profile_));
206   Application* application = FindApplication(extension);
207   if (!application) {
208     // App position is used as a dynamic command and so must be less than any
209     // predefined command id.
210     if (applications_.size() >= IDC_MinimumLabelValue) {
211       LOG(ERROR) << "Background application limit of " << IDC_MinimumLabelValue
212                  << " exceeded.  Ignoring.";
213       return;
214     }
215     application = new Application(this, extension);
216     applications_[extension->id()] = application;
217     Update();
218     application->RequestIcon(extension_misc::EXTENSION_ICON_BITTY);
219   }
220 }
221
222 void BackgroundApplicationListModel::DissociateApplicationData(
223     const Extension* extension) {
224   ApplicationMap::iterator found = applications_.find(extension->id());
225   if (found != applications_.end()) {
226     delete found->second;
227     applications_.erase(found);
228   }
229 }
230
231 const Extension* BackgroundApplicationListModel::GetExtension(
232     int position) const {
233   DCHECK(position >= 0 && static_cast<size_t>(position) < extensions_.size());
234   return extensions_[position].get();
235 }
236
237 const BackgroundApplicationListModel::Application*
238 BackgroundApplicationListModel::FindApplication(
239     const Extension* extension) const {
240   const std::string& id = extension->id();
241   ApplicationMap::const_iterator found = applications_.find(id);
242   return (found == applications_.end()) ? NULL : found->second;
243 }
244
245 BackgroundApplicationListModel::Application*
246 BackgroundApplicationListModel::FindApplication(
247     const Extension* extension) {
248   const std::string& id = extension->id();
249   ApplicationMap::iterator found = applications_.find(id);
250   return (found == applications_.end()) ? NULL : found->second;
251 }
252
253 const gfx::ImageSkia* BackgroundApplicationListModel::GetIcon(
254     const Extension* extension) {
255   const Application* application = FindApplication(extension);
256   if (application)
257     return application->icon_.get();
258   AssociateApplicationData(extension);
259   return NULL;
260 }
261
262 int BackgroundApplicationListModel::GetPosition(
263     const Extension* extension) const {
264   int position = 0;
265   const std::string& id = extension->id();
266   for (ExtensionList::const_iterator cursor = extensions_.begin();
267        cursor != extensions_.end();
268        ++cursor, ++position) {
269     if (id == cursor->get()->id())
270       return position;
271   }
272   NOTREACHED();
273   return -1;
274 }
275
276 // static
277 bool BackgroundApplicationListModel::RequiresBackgroundModeForPushMessaging(
278     const Extension& extension) {
279   // No PushMessaging permission - does not require the background mode.
280   if (!extension.HasAPIPermission(APIPermission::kPushMessaging))
281     return false;
282
283   // If in the whitelist, then does not require background mode even if
284   // uses push messaging.
285   // TODO(dimich): remove this whitelist once we have a better way to keep
286   // listening for GCM. http://crbug.com/311268
287   std::string id_hash = base::SHA1HashString(extension.id());
288   std::string hexencoded_id_hash = base::HexEncode(id_hash.c_str(),
289                                                    id_hash.length());
290   // The id starting from "9A04..." is a one from unit test.
291   if (hexencoded_id_hash == "C41AD9DCD670210295614257EF8C9945AD68D86E" ||
292       hexencoded_id_hash == "9A0417016F345C934A1A88F55CA17C05014EEEBA")
293      return false;
294
295    return true;
296  }
297
298 // static
299 bool BackgroundApplicationListModel::IsBackgroundApp(
300     const Extension& extension, Profile* profile) {
301   // An extension is a "background app" if it has the "background API"
302   // permission, and meets one of the following criteria:
303   // 1) It is an extension (not a hosted app).
304   // 2) It is a hosted app, and has a background contents registered or in the
305   //    manifest.
306
307   // Not a background app if we don't have the background permission or
308   // the push messaging permission
309   if (!extension.HasAPIPermission(APIPermission::kBackground) &&
310       !RequiresBackgroundModeForPushMessaging(extension))
311     return false;
312
313   // Extensions and packaged apps with background permission are always treated
314   // as background apps.
315   if (!extension.is_hosted_app())
316     return true;
317
318   // Hosted apps with manifest-provided background pages are background apps.
319   if (extensions::BackgroundInfo::HasBackgroundPage(&extension))
320     return true;
321
322   BackgroundContentsService* service =
323       BackgroundContentsServiceFactory::GetForProfile(profile);
324   base::string16 app_id = base::ASCIIToUTF16(extension.id());
325   // If we have an active or registered background contents for this app, then
326   // it's a background app. This covers the cases where the app has created its
327   // background contents, but it hasn't navigated yet, or the background
328   // contents crashed and hasn't yet been restarted - in both cases we still
329   // want to treat the app as a background app.
330   if (service->GetAppBackgroundContents(app_id) ||
331       service->HasRegisteredBackgroundContents(app_id)) {
332     return true;
333   }
334
335   // Doesn't meet our criteria, so it's not a background app.
336   return false;
337 }
338
339 void BackgroundApplicationListModel::Observe(
340     int type,
341     const content::NotificationSource& source,
342     const content::NotificationDetails& details) {
343   if (type == chrome::NOTIFICATION_EXTENSIONS_READY) {
344     Update();
345     return;
346   }
347   ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->
348       extension_service();
349   if (!service || !service->is_ready())
350     return;
351
352   switch (type) {
353     case chrome::NOTIFICATION_EXTENSION_LOADED:
354       OnExtensionLoaded(content::Details<Extension>(details).ptr());
355       break;
356     case chrome::NOTIFICATION_EXTENSION_UNLOADED:
357       OnExtensionUnloaded(
358           content::Details<UnloadedExtensionInfo>(details)->extension);
359       break;
360     case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED:
361       OnExtensionPermissionsUpdated(
362           content::Details<UpdatedExtensionPermissionsInfo>(details)->extension,
363           content::Details<UpdatedExtensionPermissionsInfo>(details)->reason,
364           content::Details<UpdatedExtensionPermissionsInfo>(details)->
365               permissions);
366       break;
367     case chrome::NOTIFICATION_BACKGROUND_CONTENTS_SERVICE_CHANGED:
368       Update();
369       break;
370     default:
371       NOTREACHED() << "Received unexpected notification";
372   }
373 }
374
375 void BackgroundApplicationListModel::SendApplicationDataChangedNotifications(
376     const Extension* extension) {
377   FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension,
378                                                                    profile_));
379 }
380
381 void BackgroundApplicationListModel::OnExtensionLoaded(
382     const Extension* extension) {
383   // We only care about extensions that are background applications
384   if (!IsBackgroundApp(*extension, profile_))
385     return;
386   AssociateApplicationData(extension);
387 }
388
389 void BackgroundApplicationListModel::OnExtensionUnloaded(
390     const Extension* extension) {
391   if (!IsBackgroundApp(*extension, profile_))
392     return;
393   Update();
394   DissociateApplicationData(extension);
395 }
396
397 void BackgroundApplicationListModel::OnExtensionPermissionsUpdated(
398     const Extension* extension,
399     UpdatedExtensionPermissionsInfo::Reason reason,
400     const PermissionSet* permissions) {
401   if (permissions->HasAPIPermission(APIPermission::kBackground)) {
402     switch (reason) {
403       case UpdatedExtensionPermissionsInfo::ADDED:
404         DCHECK(IsBackgroundApp(*extension, profile_));
405         OnExtensionLoaded(extension);
406         break;
407       case UpdatedExtensionPermissionsInfo::REMOVED:
408         DCHECK(!IsBackgroundApp(*extension, profile_));
409         Update();
410         DissociateApplicationData(extension);
411         break;
412       default:
413         NOTREACHED();
414     }
415   }
416 }
417
418 void BackgroundApplicationListModel::RemoveObserver(Observer* observer) {
419   observers_.RemoveObserver(observer);
420 }
421
422 // Update queries the extensions service of the profile with which the model was
423 // initialized to determine the current set of background applications.  If that
424 // differs from the old list, it generates OnApplicationListChanged events for
425 // each observer.
426 void BackgroundApplicationListModel::Update() {
427   ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->
428       extension_service();
429
430   // Discover current background applications, compare with previous list, which
431   // is consistently sorted, and notify observers if they differ.
432   ExtensionList extensions;
433   GetServiceApplications(service, &extensions);
434   ExtensionList::const_iterator old_cursor = extensions_.begin();
435   ExtensionList::const_iterator new_cursor = extensions.begin();
436   while (old_cursor != extensions_.end() &&
437          new_cursor != extensions.end() &&
438          (*old_cursor)->name() == (*new_cursor)->name() &&
439          (*old_cursor)->id() == (*new_cursor)->id()) {
440     ++old_cursor;
441     ++new_cursor;
442   }
443   if (old_cursor != extensions_.end() || new_cursor != extensions.end()) {
444     extensions_ = extensions;
445     FOR_EACH_OBSERVER(Observer, observers_, OnApplicationListChanged(profile_));
446   }
447 }