| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/background_application_list_model.h" |
| |
| #include <algorithm> |
| #include <set> |
| |
| #include "base/stl_util-inl.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/background_mode_manager.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/extension_prefs.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/image_loading_tracker.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/extensions/extension.h" |
| #include "chrome/common/extensions/extension_resource.h" |
| #include "content/common/notification_details.h" |
| #include "content/common/notification_source.h" |
| #include "ui/base/l10n/l10n_util_collator.h" |
| |
| class ExtensionNameComparator { |
| public: |
| explicit ExtensionNameComparator(icu::Collator* collator); |
| bool operator()(const Extension* x, const Extension* y); |
| |
| private: |
| icu::Collator* collator_; |
| }; |
| |
| ExtensionNameComparator::ExtensionNameComparator(icu::Collator* collator) |
| : collator_(collator) { |
| } |
| |
| bool ExtensionNameComparator::operator()(const Extension* x, |
| const Extension* y) { |
| return l10n_util::StringComparator<string16>(collator_)( |
| UTF8ToUTF16(x->name()), |
| UTF8ToUTF16(y->name())); |
| } |
| |
| // Background application representation, private to the |
| // BackgroundApplicationListModel class. |
| class BackgroundApplicationListModel::Application |
| : public ImageLoadingTracker::Observer { |
| public: |
| Application(BackgroundApplicationListModel* model, |
| const Extension* an_extension); |
| |
| virtual ~Application(); |
| |
| // Invoked when a request icon is available. |
| virtual void OnImageLoaded(SkBitmap* image, |
| const ExtensionResource& resource, |
| int index); |
| |
| // Uses the FILE thread to request this extension's icon, sized |
| // appropriately. |
| void RequestIcon(Extension::Icons size); |
| |
| const Extension* extension_; |
| scoped_ptr<SkBitmap> icon_; |
| BackgroundApplicationListModel* model_; |
| ImageLoadingTracker tracker_; |
| }; |
| |
| namespace { |
| void GetServiceApplications(ExtensionService* service, |
| ExtensionList* applications_result) { |
| const ExtensionList* extensions = service->extensions(); |
| |
| for (ExtensionList::const_iterator cursor = extensions->begin(); |
| cursor != extensions->end(); |
| ++cursor) { |
| const Extension* extension = *cursor; |
| if (BackgroundApplicationListModel::IsBackgroundApp(*extension)) |
| applications_result->push_back(extension); |
| } |
| std::string locale = g_browser_process->GetApplicationLocale(); |
| icu::Locale loc(locale.c_str()); |
| UErrorCode error = U_ZERO_ERROR; |
| scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error)); |
| sort(applications_result->begin(), applications_result->end(), |
| ExtensionNameComparator(collator.get())); |
| } |
| |
| bool HasBackgroundAppPermission( |
| const std::set<std::string>& api_permissions) { |
| return Extension::HasApiPermission( |
| api_permissions, Extension::kBackgroundPermission); |
| } |
| } // namespace |
| |
| void |
| BackgroundApplicationListModel::Observer::OnApplicationDataChanged( |
| const Extension* extension) { |
| } |
| |
| void |
| BackgroundApplicationListModel::Observer::OnApplicationListChanged() { |
| } |
| |
| BackgroundApplicationListModel::Observer::~Observer() { |
| } |
| |
| BackgroundApplicationListModel::Application::~Application() { |
| } |
| |
| BackgroundApplicationListModel::Application::Application( |
| BackgroundApplicationListModel* model, |
| const Extension* extension) |
| : extension_(extension), |
| icon_(NULL), |
| model_(model), |
| ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)) { |
| } |
| |
| void BackgroundApplicationListModel::Application::OnImageLoaded( |
| SkBitmap* image, |
| const ExtensionResource& resource, |
| int index) { |
| if (!image) |
| return; |
| icon_.reset(new SkBitmap(*image)); |
| model_->OnApplicationDataChanged(extension_); |
| } |
| |
| void BackgroundApplicationListModel::Application::RequestIcon( |
| Extension::Icons size) { |
| ExtensionResource resource = extension_->GetIconResource( |
| size, ExtensionIconSet::MATCH_BIGGER); |
| tracker_.LoadImage(extension_, resource, gfx::Size(size, size), |
| ImageLoadingTracker::CACHE); |
| } |
| |
| BackgroundApplicationListModel::~BackgroundApplicationListModel() { |
| STLDeleteContainerPairSecondPointers(applications_.begin(), |
| applications_.end()); |
| } |
| |
| BackgroundApplicationListModel::BackgroundApplicationListModel(Profile* profile) |
| : profile_(profile) { |
| DCHECK(profile_); |
| registrar_.Add(this, |
| NotificationType::EXTENSION_LOADED, |
| Source<Profile>(profile)); |
| registrar_.Add(this, |
| NotificationType::EXTENSION_UNLOADED, |
| Source<Profile>(profile)); |
| registrar_.Add(this, |
| NotificationType::EXTENSIONS_READY, |
| Source<Profile>(profile)); |
| ExtensionService* service = profile->GetExtensionService(); |
| if (service && service->is_ready()) |
| Update(); |
| } |
| |
| void BackgroundApplicationListModel::AddObserver(Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void BackgroundApplicationListModel::AssociateApplicationData( |
| const Extension* extension) { |
| DCHECK(IsBackgroundApp(*extension)); |
| Application* application = FindApplication(extension); |
| if (!application) { |
| // App position is used as a dynamic command and so must be less than any |
| // predefined command id. |
| if (applications_.size() >= IDC_MinimumLabelValue) { |
| LOG(ERROR) << "Background application limit of " << IDC_MinimumLabelValue |
| << " exceeded. Ignoring."; |
| return; |
| } |
| application = new Application(this, extension); |
| applications_[extension->id()] = application; |
| application->RequestIcon(Extension::EXTENSION_ICON_BITTY); |
| } |
| } |
| |
| void BackgroundApplicationListModel::DissociateApplicationData( |
| const Extension* extension) { |
| ApplicationMap::iterator found = applications_.find(extension->id()); |
| if (found != applications_.end()) { |
| delete found->second; |
| applications_.erase(found); |
| } |
| } |
| |
| const Extension* BackgroundApplicationListModel::GetExtension( |
| int position) const { |
| DCHECK(position >= 0 && static_cast<size_t>(position) < extensions_.size()); |
| return extensions_[position]; |
| } |
| |
| const BackgroundApplicationListModel::Application* |
| BackgroundApplicationListModel::FindApplication( |
| const Extension* extension) const { |
| const std::string& id = extension->id(); |
| ApplicationMap::const_iterator found = applications_.find(id); |
| return (found == applications_.end()) ? NULL : found->second; |
| } |
| |
| BackgroundApplicationListModel::Application* |
| BackgroundApplicationListModel::FindApplication(const Extension* extension) { |
| const std::string& id = extension->id(); |
| ApplicationMap::iterator found = applications_.find(id); |
| return (found == applications_.end()) ? NULL : found->second; |
| } |
| |
| const SkBitmap* BackgroundApplicationListModel::GetIcon( |
| const Extension* extension) { |
| const Application* application = FindApplication(extension); |
| if (application) |
| return application->icon_.get(); |
| AssociateApplicationData(extension); |
| return NULL; |
| } |
| |
| int BackgroundApplicationListModel::GetPosition( |
| const Extension* extension) const { |
| int position = 0; |
| const std::string& id = extension->id(); |
| for (ExtensionList::const_iterator cursor = extensions_.begin(); |
| cursor != extensions_.end(); |
| ++cursor, ++position) { |
| if (id == cursor->get()->id()) |
| return position; |
| } |
| NOTREACHED(); |
| return -1; |
| } |
| |
| // static |
| bool BackgroundApplicationListModel::IsBackgroundApp( |
| const Extension& extension) { |
| return HasBackgroundAppPermission(extension.api_permissions()); |
| } |
| |
| void BackgroundApplicationListModel::Observe( |
| NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| if (type == NotificationType::EXTENSIONS_READY) { |
| Update(); |
| return; |
| } |
| ExtensionService* service = profile_->GetExtensionService(); |
| if (!service || !service->is_ready()) |
| return; |
| |
| switch (type.value) { |
| case NotificationType::EXTENSION_LOADED: |
| OnExtensionLoaded(Details<Extension>(details).ptr()); |
| break; |
| case NotificationType::EXTENSION_UNLOADED: |
| OnExtensionUnloaded(Details<UnloadedExtensionInfo>(details)->extension); |
| break; |
| default: |
| NOTREACHED() << "Received unexpected notification"; |
| } |
| } |
| |
| void BackgroundApplicationListModel::OnApplicationDataChanged( |
| const Extension* extension) { |
| FOR_EACH_OBSERVER(Observer, observers_, OnApplicationDataChanged(extension)); |
| } |
| |
| void BackgroundApplicationListModel::OnExtensionLoaded(Extension* extension) { |
| // We only care about extensions that are background applications |
| if (!IsBackgroundApp(*extension)) |
| return; |
| AssociateApplicationData(extension); |
| Update(); |
| } |
| |
| void BackgroundApplicationListModel::OnExtensionUnloaded( |
| const Extension* extension) { |
| if (!IsBackgroundApp(*extension)) |
| return; |
| Update(); |
| DissociateApplicationData(extension); |
| } |
| |
| void BackgroundApplicationListModel::RemoveObserver(Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| // Update queries the extensions service of the profile with which the model was |
| // initialized to determine the current set of background applications. If that |
| // differs from the old list, it generates OnApplicationListChanged events for |
| // each observer. |
| void BackgroundApplicationListModel::Update() { |
| ExtensionService* service = profile_->GetExtensionService(); |
| |
| // Discover current background applications, compare with previous list, which |
| // is consistently sorted, and notify observers if they differ. |
| ExtensionList extensions; |
| GetServiceApplications(service, &extensions); |
| ExtensionList::const_iterator old_cursor = extensions_.begin(); |
| ExtensionList::const_iterator new_cursor = extensions.begin(); |
| while (old_cursor != extensions_.end() && |
| new_cursor != extensions.end() && |
| (*old_cursor)->name() == (*new_cursor)->name() && |
| (*old_cursor)->id() == (*new_cursor)->id()) { |
| ++old_cursor; |
| ++new_cursor; |
| } |
| if (old_cursor != extensions_.end() || new_cursor != extensions.end()) { |
| extensions_ = extensions; |
| FOR_EACH_OBSERVER(Observer, observers_, OnApplicationListChanged()); |
| } |
| } |