15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/background/background_application_list_model.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <set>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/app/chrome_command_ids.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/background/background_contents_service.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/background/background_contents_service_factory.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/background/background_mode_manager.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
177dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_prefs.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/image_loader.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/background_info.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension_constants.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension_icon_set.h"
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/permissions/permission_set.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_details.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/common/extension_resource.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util_collator.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_skia.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::APIPermission;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Extension;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::ExtensionList;
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::PermissionSet;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::UnloadedExtensionInfo;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::UpdatedExtensionPermissionsInfo;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ExtensionNameComparator {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit ExtensionNameComparator(icu::Collator* collator);
46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  bool operator()(const scoped_refptr<const Extension>& x,
47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                  const scoped_refptr<const Extension>& y);
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icu::Collator* collator_;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionNameComparator::ExtensionNameComparator(icu::Collator* collator)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  : collator_(collator) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool ExtensionNameComparator::operator()(
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const scoped_refptr<const Extension>& x,
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const scoped_refptr<const Extension>& y) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return l10n_util::StringComparator<string16>(collator_)(
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      UTF8ToUTF16(x->name()), UTF8ToUTF16(y->name()));
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Background application representation, private to the
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// BackgroundApplicationListModel class.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BackgroundApplicationListModel::Application
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : public base::SupportsWeakPtr<Application> {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Application(BackgroundApplicationListModel* model,
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              const Extension* an_extension);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~Application();
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Invoked when a request icon is available.
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void OnImageLoaded(const gfx::Image& image);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Uses the FILE thread to request this extension's icon, sized
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // appropriately.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void RequestIcon(extension_misc::ExtensionIcons size);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const Extension* extension_;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<gfx::ImageSkia> icon_;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundApplicationListModel* model_;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GetServiceApplications(ExtensionService* service,
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            ExtensionList* applications_result) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const ExtensionSet* extensions = service->extensions();
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionSet::const_iterator cursor = extensions->begin();
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       cursor != extensions->end();
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++cursor) {
947d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const Extension* extension = cursor->get();
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                        service->profile())) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      applications_result->push_back(extension);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Walk the list of terminated extensions also (just because an extension
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // crashed doesn't mean we should ignore it).
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extensions = service->terminated_extensions();
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionSet::const_iterator cursor = extensions->begin();
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       cursor != extensions->end();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++cursor) {
1077d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const Extension* extension = cursor->get();
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                        service->profile())) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      applications_result->push_back(extension);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string locale = g_browser_process->GetApplicationLocale();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icu::Locale loc(locale.c_str());
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UErrorCode error = U_ZERO_ERROR;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error));
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::sort(applications_result->begin(), applications_result->end(),
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ExtensionNameComparator(collator.get()));
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::Observer::OnApplicationDataChanged(
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension, Profile* profile) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::Observer::OnApplicationListChanged(
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::Observer::~Observer() {
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::Application::~Application() {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::Application::Application(
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    BackgroundApplicationListModel* model,
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension)
1437d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    : extension_(extension), model_(model) {}
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::Application::OnImageLoaded(
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const gfx::Image& image) {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (image.IsEmpty())
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_.reset(image.CopyImageSkia());
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model_->SendApplicationDataChangedNotifications(extension_);
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::Application::RequestIcon(
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extension_misc::ExtensionIcons size) {
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  extensions::ExtensionResource resource =
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extensions::IconsInfo::GetIconResource(
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          extension_, size, ExtensionIconSet::MATCH_BIGGER);
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  extensions::ImageLoader::Get(model_->profile_)->LoadImageAsync(
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extension_, resource, gfx::Size(size, size),
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&Application::OnImageLoaded, AsWeakPtr()));
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::~BackgroundApplicationListModel() {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  STLDeleteContainerPairSecondPointers(applications_.begin(),
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       applications_.end());
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::BackgroundApplicationListModel(Profile* profile)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : profile_(profile) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(profile_);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 chrome::NOTIFICATION_EXTENSION_LOADED,
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this,
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 chrome::NOTIFICATION_EXTENSION_UNLOADED,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this,
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 chrome::NOTIFICATION_EXTENSIONS_READY,
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this,
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this,
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 chrome::NOTIFICATION_BACKGROUND_CONTENTS_SERVICE_CHANGED,
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionService* service = extensions::ExtensionSystem::Get(profile)->
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extension_service();
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (service && service->is_ready())
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Update();
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::AddObserver(Observer* observer) {
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observers_.AddObserver(observer);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::AssociateApplicationData(
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(IsBackgroundApp(*extension, profile_));
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Application* application = FindApplication(extension);
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!application) {
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // App position is used as a dynamic command and so must be less than any
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // predefined command id.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (applications_.size() >= IDC_MinimumLabelValue) {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Background application limit of " << IDC_MinimumLabelValue
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " exceeded.  Ignoring.";
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    application = new Application(this, extension);
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    applications_[extension->id()] = application;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Update();
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    application->RequestIcon(extension_misc::EXTENSION_ICON_BITTY);
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::DissociateApplicationData(
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ApplicationMap::iterator found = applications_.find(extension->id());
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (found != applications_.end()) {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delete found->second;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    applications_.erase(found);
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const Extension* BackgroundApplicationListModel::GetExtension(
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int position) const {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(position >= 0 && static_cast<size_t>(position) < extensions_.size());
227868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return extensions_[position].get();
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const BackgroundApplicationListModel::Application*
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::FindApplication(
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) const {
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string& id = extension->id();
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ApplicationMap::const_iterator found = applications_.find(id);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (found == applications_.end()) ? NULL : found->second;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::Application*
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundApplicationListModel::FindApplication(
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string& id = extension->id();
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ApplicationMap::iterator found = applications_.find(id);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (found == applications_.end()) ? NULL : found->second;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const gfx::ImageSkia* BackgroundApplicationListModel::GetIcon(
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const Application* application = FindApplication(extension);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (application)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return application->icon_.get();
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AssociateApplicationData(extension);
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundApplicationListModel::GetPosition(
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) const {
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int position = 0;
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string& id = extension->id();
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (ExtensionList::const_iterator cursor = extensions_.begin();
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       cursor != extensions_.end();
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++cursor, ++position) {
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (id == cursor->get()->id())
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return position;
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED();
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return -1;
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundApplicationListModel::IsBackgroundApp(
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension& extension, Profile* profile) {
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // An extension is a "background app" if it has the "background API"
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // permission, and meets one of the following criteria:
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 1) It is an extension (not a hosted app).
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 2) It is a hosted app, and has a background contents registered or in the
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //    manifest.
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Not a background app if we don't have the background permission or
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the push messaging permission
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!extension.HasAPIPermission(APIPermission::kBackground) &&
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !extension.HasAPIPermission(APIPermission::kPushMessaging) )
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extensions and packaged apps with background permission are always treated
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // as background apps.
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!extension.is_hosted_app())
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hosted apps with manifest-provided background pages are background apps.
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (extensions::BackgroundInfo::HasBackgroundPage(&extension))
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundContentsService* service =
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BackgroundContentsServiceFactory::GetForProfile(profile);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 app_id = ASCIIToUTF16(extension.id());
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we have an active or registered background contents for this app, then
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // it's a background app. This covers the cases where the app has created its
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // background contents, but it hasn't navigated yet, or the background
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // contents crashed and hasn't yet been restarted - in both cases we still
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // want to treat the app as a background app.
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (service->GetAppBackgroundContents(app_id) ||
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      service->HasRegisteredBackgroundContents(app_id)) {
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Doesn't meet our criteria, so it's not a background app.
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::Observe(
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type == chrome::NOTIFICATION_EXTENSIONS_READY) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Update();
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extension_service();
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!service || !service->is_ready())
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSION_LOADED:
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      OnExtensionLoaded(content::Details<Extension>(details).ptr());
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSION_UNLOADED:
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      OnExtensionUnloaded(
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          content::Details<UnloadedExtensionInfo>(details)->extension);
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED:
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      OnExtensionPermissionsUpdated(
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          content::Details<UpdatedExtensionPermissionsInfo>(details)->extension,
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          content::Details<UpdatedExtensionPermissionsInfo>(details)->reason,
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          content::Details<UpdatedExtensionPermissionsInfo>(details)->
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              permissions);
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_BACKGROUND_CONTENTS_SERVICE_CHANGED:
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Update();
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED() << "Received unexpected notification";
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::SendApplicationDataChangedNotifications(
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension,
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                                   profile_));
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::OnExtensionLoaded(
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only care about extensions that are background applications
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsBackgroundApp(*extension, profile_))
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AssociateApplicationData(extension);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::OnExtensionUnloaded(
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsBackgroundApp(*extension, profile_))
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Update();
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DissociateApplicationData(extension);
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::OnExtensionPermissionsUpdated(
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension,
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdatedExtensionPermissionsInfo::Reason reason,
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PermissionSet* permissions) {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (permissions->HasAPIPermission(APIPermission::kBackground)) {
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (reason) {
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case UpdatedExtensionPermissionsInfo::ADDED:
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(IsBackgroundApp(*extension, profile_));
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        OnExtensionLoaded(extension);
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case UpdatedExtensionPermissionsInfo::REMOVED:
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(!IsBackgroundApp(*extension, profile_));
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Update();
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DissociateApplicationData(extension);
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::RemoveObserver(Observer* observer) {
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  observers_.RemoveObserver(observer);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Update queries the extensions service of the profile with which the model was
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// initialized to determine the current set of background applications.  If that
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// differs from the old list, it generates OnApplicationListChanged events for
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// each observer.
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundApplicationListModel::Update() {
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExtensionService* service = extensions::ExtensionSystem::Get(profile_)->
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extension_service();
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Discover current background applications, compare with previous list, which
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // is consistently sorted, and notify observers if they differ.
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList extensions;
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetServiceApplications(service, &extensions);
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList::const_iterator old_cursor = extensions_.begin();
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionList::const_iterator new_cursor = extensions.begin();
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (old_cursor != extensions_.end() &&
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         new_cursor != extensions.end() &&
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (*old_cursor)->name() == (*new_cursor)->name() &&
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (*old_cursor)->id() == (*new_cursor)->id()) {
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++old_cursor;
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++new_cursor;
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (old_cursor != extensions_.end() || new_cursor != extensions.end()) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extensions_ = extensions;
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FOR_EACH_OBSERVER(Observer, observers_, OnApplicationListChanged(profile_));
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
419