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