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