Update V8 to version 4.6.22.
[chromium-blink-merge.git] / chrome / browser / background / background_application_list_model.cc
bloba53322fb07427b6086e5556d7edaa44f4b030776
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.
5 #include "chrome/browser/background/background_application_list_model.h"
7 #include <algorithm>
8 #include <set>
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/app/chrome_command_ids.h"
14 #include "chrome/browser/background/background_contents_service.h"
15 #include "chrome/browser/background/background_contents_service_factory.h"
16 #include "chrome/browser/background/background_mode_manager.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/common/extensions/extension_constants.h"
22 #include "components/crx_file/id_util.h"
23 #include "components/variations/variations_associated_data.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_source.h"
26 #include "extensions/browser/extension_prefs.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/browser/extension_system.h"
29 #include "extensions/browser/extension_util.h"
30 #include "extensions/browser/image_loader.h"
31 #include "extensions/browser/notification_types.h"
32 #include "extensions/common/extension.h"
33 #include "extensions/common/extension_icon_set.h"
34 #include "extensions/common/extension_resource.h"
35 #include "extensions/common/extension_set.h"
36 #include "extensions/common/manifest_handlers/background_info.h"
37 #include "extensions/common/manifest_handlers/icons_handler.h"
38 #include "extensions/common/permissions/permission_set.h"
39 #include "extensions/common/permissions/permissions_data.h"
40 #include "ui/base/l10n/l10n_util_collator.h"
41 #include "ui/gfx/image/image.h"
42 #include "ui/gfx/image/image_skia.h"
44 using extensions::APIPermission;
45 using extensions::Extension;
46 using extensions::ExtensionList;
47 using extensions::ExtensionRegistry;
48 using extensions::ExtensionSet;
49 using extensions::PermissionSet;
50 using extensions::UnloadedExtensionInfo;
51 using extensions::UpdatedExtensionPermissionsInfo;
53 class ExtensionNameComparator {
54 public:
55 explicit ExtensionNameComparator(icu::Collator* collator);
56 bool operator()(const scoped_refptr<const Extension>& x,
57 const scoped_refptr<const Extension>& y);
59 private:
60 icu::Collator* collator_;
63 ExtensionNameComparator::ExtensionNameComparator(icu::Collator* collator)
64 : collator_(collator) {
67 bool ExtensionNameComparator::operator()(
68 const scoped_refptr<const Extension>& x,
69 const scoped_refptr<const Extension>& y) {
70 return l10n_util::StringComparator<base::string16>(collator_)(
71 base::UTF8ToUTF16(x->name()), base::UTF8ToUTF16(y->name()));
74 class ExtensionIdComparator {
75 public:
76 bool operator()(const scoped_refptr<const Extension>& x,
77 const scoped_refptr<const Extension>& y) {
78 return x->id() < y->id();
82 // Background application representation, private to the
83 // BackgroundApplicationListModel class.
84 class BackgroundApplicationListModel::Application
85 : public base::SupportsWeakPtr<Application> {
86 public:
87 Application(BackgroundApplicationListModel* model,
88 const Extension* an_extension);
90 virtual ~Application();
92 // Invoked when a request icon is available.
93 void OnImageLoaded(const gfx::Image& image);
95 // Uses the FILE thread to request this extension's icon, sized
96 // appropriately.
97 void RequestIcon(extension_misc::ExtensionIcons size);
99 const Extension* extension_;
100 scoped_ptr<gfx::ImageSkia> icon_;
101 BackgroundApplicationListModel* model_;
104 namespace {
105 void GetServiceApplications(ExtensionService* service,
106 ExtensionList* applications_result) {
107 ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
108 const ExtensionSet& enabled_extensions = registry->enabled_extensions();
110 for (ExtensionSet::const_iterator cursor = enabled_extensions.begin();
111 cursor != enabled_extensions.end();
112 ++cursor) {
113 const Extension* extension = cursor->get();
114 if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
115 service->profile())) {
116 applications_result->push_back(extension);
120 // Walk the list of terminated extensions also (just because an extension
121 // crashed doesn't mean we should ignore it).
122 const ExtensionSet& terminated_extensions = registry->terminated_extensions();
123 for (ExtensionSet::const_iterator cursor = terminated_extensions.begin();
124 cursor != terminated_extensions.end();
125 ++cursor) {
126 const Extension* extension = cursor->get();
127 if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
128 service->profile())) {
129 applications_result->push_back(extension);
133 if (!variations::GetVariationParamValue("LightSpeed", "AvoidMMap").empty()) {
134 std::sort(applications_result->begin(), applications_result->end(),
135 ExtensionIdComparator());
136 } else {
137 std::string locale = g_browser_process->GetApplicationLocale();
138 icu::Locale loc(locale.c_str());
139 UErrorCode error = U_ZERO_ERROR;
140 scoped_ptr<icu::Collator> collator(
141 icu::Collator::createInstance(loc, error));
142 std::sort(applications_result->begin(), applications_result->end(),
143 ExtensionNameComparator(collator.get()));
147 } // namespace
149 void
150 BackgroundApplicationListModel::Observer::OnApplicationDataChanged(
151 const Extension* extension, Profile* profile) {
154 void
155 BackgroundApplicationListModel::Observer::OnApplicationListChanged(
156 Profile* profile) {
159 BackgroundApplicationListModel::Observer::~Observer() {
162 BackgroundApplicationListModel::Application::~Application() {
165 BackgroundApplicationListModel::Application::Application(
166 BackgroundApplicationListModel* model,
167 const Extension* extension)
168 : extension_(extension), model_(model) {}
170 void BackgroundApplicationListModel::Application::OnImageLoaded(
171 const gfx::Image& image) {
172 if (image.IsEmpty())
173 return;
174 icon_.reset(image.CopyImageSkia());
175 model_->SendApplicationDataChangedNotifications(extension_);
178 void BackgroundApplicationListModel::Application::RequestIcon(
179 extension_misc::ExtensionIcons size) {
180 extensions::ExtensionResource resource =
181 extensions::IconsInfo::GetIconResource(
182 extension_, size, ExtensionIconSet::MATCH_BIGGER);
183 extensions::ImageLoader::Get(model_->profile_)->LoadImageAsync(
184 extension_, resource, gfx::Size(size, size),
185 base::Bind(&Application::OnImageLoaded, AsWeakPtr()));
188 BackgroundApplicationListModel::~BackgroundApplicationListModel() {
189 STLDeleteContainerPairSecondPointers(applications_.begin(),
190 applications_.end());
193 BackgroundApplicationListModel::BackgroundApplicationListModel(Profile* profile)
194 : profile_(profile),
195 ready_(false) {
196 DCHECK(profile_);
197 registrar_.Add(this,
198 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
199 content::Source<Profile>(profile));
200 registrar_.Add(this,
201 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
202 content::Source<Profile>(profile));
203 registrar_.Add(this,
204 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
205 content::Source<Profile>(profile));
206 registrar_.Add(this,
207 extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
208 content::Source<Profile>(profile));
209 registrar_.Add(this,
210 chrome::NOTIFICATION_BACKGROUND_CONTENTS_SERVICE_CHANGED,
211 content::Source<Profile>(profile));
212 ExtensionService* service = extensions::ExtensionSystem::Get(profile)->
213 extension_service();
214 if (service && service->is_ready()) {
215 Update();
216 ready_ = true;
220 void BackgroundApplicationListModel::AddObserver(Observer* observer) {
221 observers_.AddObserver(observer);
224 void BackgroundApplicationListModel::AssociateApplicationData(
225 const Extension* extension) {
226 DCHECK(IsBackgroundApp(*extension, profile_));
227 Application* application = FindApplication(extension);
228 if (!application) {
229 // App position is used as a dynamic command and so must be less than any
230 // predefined command id.
231 if (applications_.size() >= IDC_MinimumLabelValue) {
232 LOG(ERROR) << "Background application limit of " << IDC_MinimumLabelValue
233 << " exceeded. Ignoring.";
234 return;
236 application = new Application(this, extension);
237 applications_[extension->id()] = application;
238 Update();
239 application->RequestIcon(extension_misc::EXTENSION_ICON_BITTY);
243 void BackgroundApplicationListModel::DissociateApplicationData(
244 const Extension* extension) {
245 ApplicationMap::iterator found = applications_.find(extension->id());
246 if (found != applications_.end()) {
247 delete found->second;
248 applications_.erase(found);
252 const Extension* BackgroundApplicationListModel::GetExtension(
253 int position) const {
254 DCHECK(position >= 0 && static_cast<size_t>(position) < extensions_.size());
255 return extensions_[position].get();
258 const BackgroundApplicationListModel::Application*
259 BackgroundApplicationListModel::FindApplication(
260 const Extension* extension) const {
261 const std::string& id = extension->id();
262 ApplicationMap::const_iterator found = applications_.find(id);
263 return (found == applications_.end()) ? NULL : found->second;
266 BackgroundApplicationListModel::Application*
267 BackgroundApplicationListModel::FindApplication(
268 const Extension* extension) {
269 const std::string& id = extension->id();
270 ApplicationMap::iterator found = applications_.find(id);
271 return (found == applications_.end()) ? NULL : found->second;
274 const gfx::ImageSkia* BackgroundApplicationListModel::GetIcon(
275 const Extension* extension) {
276 const Application* application = FindApplication(extension);
277 if (application)
278 return application->icon_.get();
279 AssociateApplicationData(extension);
280 return NULL;
283 int BackgroundApplicationListModel::GetPosition(
284 const Extension* extension) const {
285 int position = 0;
286 const std::string& id = extension->id();
287 for (ExtensionList::const_iterator cursor = extensions_.begin();
288 cursor != extensions_.end();
289 ++cursor, ++position) {
290 if (id == cursor->get()->id())
291 return position;
293 NOTREACHED();
294 return -1;
297 // static
298 bool BackgroundApplicationListModel::IsBackgroundApp(
299 const Extension& extension, Profile* profile) {
300 // An extension is a "background app" if it has the "background API"
301 // permission, and meets one of the following criteria:
302 // 1) It is an extension (not a hosted app).
303 // 2) It is a hosted app, and has a background contents registered or in the
304 // manifest.
306 // Ephemeral apps are denied any background activity after their event page
307 // has been destroyed, thus they cannot be background apps.
308 if (extensions::util::IsEphemeralApp(extension.id(), profile))
309 return false;
311 // Not a background app if we don't have the background permission.
312 if (!extension.permissions_data()->HasAPIPermission(
313 APIPermission::kBackground)) {
314 return false;
317 // Extensions and packaged apps with background permission are always treated
318 // as background apps.
319 if (!extension.is_hosted_app())
320 return true;
322 // Hosted apps with manifest-provided background pages are background apps.
323 if (extensions::BackgroundInfo::HasBackgroundPage(&extension))
324 return true;
326 BackgroundContentsService* service =
327 BackgroundContentsServiceFactory::GetForProfile(profile);
328 base::string16 app_id = base::ASCIIToUTF16(extension.id());
329 // If we have an active or registered background contents for this app, then
330 // it's a background app. This covers the cases where the app has created its
331 // background contents, but it hasn't navigated yet, or the background
332 // contents crashed and hasn't yet been restarted - in both cases we still
333 // want to treat the app as a background app.
334 if (service->GetAppBackgroundContents(app_id) ||
335 service->HasRegisteredBackgroundContents(app_id)) {
336 return true;
339 // Doesn't meet our criteria, so it's not a background app.
340 return false;
343 void BackgroundApplicationListModel::Observe(
344 int type,
345 const content::NotificationSource& source,
346 const content::NotificationDetails& details) {
347 if (type == extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED) {
348 Update();
349 ready_ = true;
350 return;
352 ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->
353 extension_service();
354 if (!service || !service->is_ready())
355 return;
357 switch (type) {
358 case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED:
359 OnExtensionLoaded(content::Details<Extension>(details).ptr());
360 break;
361 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED:
362 OnExtensionUnloaded(
363 content::Details<UnloadedExtensionInfo>(details)->extension);
364 break;
365 case extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED:
366 OnExtensionPermissionsUpdated(
367 content::Details<UpdatedExtensionPermissionsInfo>(details)->extension,
368 content::Details<UpdatedExtensionPermissionsInfo>(details)->reason,
369 content::Details<UpdatedExtensionPermissionsInfo>(details)->
370 permissions);
371 break;
372 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_SERVICE_CHANGED:
373 Update();
374 break;
375 default:
376 NOTREACHED() << "Received unexpected notification";
380 void BackgroundApplicationListModel::SendApplicationDataChangedNotifications(
381 const Extension* extension) {
382 FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension,
383 profile_));
386 void BackgroundApplicationListModel::OnExtensionLoaded(
387 const Extension* extension) {
388 // We only care about extensions that are background applications
389 if (!IsBackgroundApp(*extension, profile_))
390 return;
391 AssociateApplicationData(extension);
394 void BackgroundApplicationListModel::OnExtensionUnloaded(
395 const Extension* extension) {
396 if (!IsBackgroundApp(*extension, profile_))
397 return;
398 Update();
399 DissociateApplicationData(extension);
402 void BackgroundApplicationListModel::OnExtensionPermissionsUpdated(
403 const Extension* extension,
404 UpdatedExtensionPermissionsInfo::Reason reason,
405 const PermissionSet* permissions) {
406 if (permissions->HasAPIPermission(APIPermission::kBackground)) {
407 switch (reason) {
408 case UpdatedExtensionPermissionsInfo::ADDED:
409 DCHECK(IsBackgroundApp(*extension, profile_));
410 OnExtensionLoaded(extension);
411 break;
412 case UpdatedExtensionPermissionsInfo::REMOVED:
413 DCHECK(!IsBackgroundApp(*extension, profile_));
414 Update();
415 DissociateApplicationData(extension);
416 break;
417 default:
418 NOTREACHED();
423 void BackgroundApplicationListModel::RemoveObserver(Observer* observer) {
424 observers_.RemoveObserver(observer);
427 // Update queries the extensions service of the profile with which the model was
428 // initialized to determine the current set of background applications. If that
429 // differs from the old list, it generates OnApplicationListChanged events for
430 // each observer.
431 void BackgroundApplicationListModel::Update() {
432 ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->
433 extension_service();
435 // Discover current background applications, compare with previous list, which
436 // is consistently sorted, and notify observers if they differ.
437 ExtensionList extensions;
438 GetServiceApplications(service, &extensions);
439 ExtensionList::const_iterator old_cursor = extensions_.begin();
440 ExtensionList::const_iterator new_cursor = extensions.begin();
441 while (old_cursor != extensions_.end() &&
442 new_cursor != extensions.end() &&
443 (*old_cursor)->name() == (*new_cursor)->name() &&
444 (*old_cursor)->id() == (*new_cursor)->id()) {
445 ++old_cursor;
446 ++new_cursor;
448 if (old_cursor != extensions_.end() || new_cursor != extensions.end()) {
449 extensions_ = extensions;
450 FOR_EACH_OBSERVER(Observer, observers_, OnApplicationListChanged(profile_));