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