This source file includes following definitions.
- NotificationBitmapToGfxImage
- CreateScopedIdentifier
- StripScopeFromIdentifier
- process_id_
- Display
- Error
- Close
- Click
- HasClickedListener
- ButtonClick
- id
- process_id
- GetRenderViewHost
- ReleaseRenderViewHost
- SendEvent
- CreateBaseEventArgs
- IsNotificationsApiAvailable
- CreateNotification
- UpdateNotification
- AreExtensionNotificationsAllowed
- IsNotificationsApiEnabled
- CanRunWhileDisabled
- RunImpl
- MapApiTemplateTypeToType
- RunNotificationsApi
- RunNotificationsApi
- RunNotificationsApi
- RunNotificationsApi
- NotificationsGetPermissionLevelFunction
- NotificationsGetPermissionLevelFunction
- CanRunWhileDisabled
- RunNotificationsApi
#include "chrome/browser/extensions/api/notifications/notifications_api.h"
#include "base/callback.h"
#include "base/guid.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/desktop_notification_service.h"
#include "chrome/browser/notifications/desktop_notification_service_factory.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_version_info.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "extensions/common/features/feature.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/message_center_util.h"
#include "ui/message_center/notifier_settings.h"
#include "url/gurl.h"
namespace extensions {
namespace notifications = api::notifications;
namespace {
const char kMissingRequiredPropertiesForCreateNotification[] =
"Some of the required properties are missing: type, iconUrl, title and "
"message.";
const char kUnexpectedProgressValueForNonProgressType[] =
"The progress value should not be specified for non-progress notification";
const char kInvalidProgressValue[] =
"The progress value should range from 0 to 100";
bool NotificationBitmapToGfxImage(
api::notifications::NotificationBitmap* notification_bitmap,
gfx::Image* return_image) {
if (!notification_bitmap)
return false;
const int max_width = message_center::kNotificationPreferredImageWidth;
const int max_height = message_center::kNotificationPreferredImageHeight;
const int BYTES_PER_PIXEL = 4;
const int width = notification_bitmap->width;
const int height = notification_bitmap->height;
if (width < 0 || height < 0 || width > max_width || height > max_height)
return false;
std::string* rgba_data = notification_bitmap->data.get();
if (!rgba_data)
return false;
const size_t rgba_data_length = rgba_data->length();
const size_t rgba_area = width * height;
if (rgba_data_length != rgba_area * BYTES_PER_PIXEL)
return false;
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
if (!bitmap.allocPixels())
return false;
if (rgba_data_length != bitmap.getSafeSize())
return false;
uint32_t* pixels = bitmap.getAddr32(0, 0);
const char* c_rgba_data = rgba_data->data();
for (size_t t = 0; t < rgba_area; ++t) {
size_t rgba_index = t * BYTES_PER_PIXEL;
pixels[t] = SkPreMultiplyColor(
((c_rgba_data[rgba_index + 3] & 0xFF) << 24) |
((c_rgba_data[rgba_index + 0] & 0xFF) << 16) |
((c_rgba_data[rgba_index + 1] & 0xFF) << 8) |
((c_rgba_data[rgba_index + 2] & 0xFF) << 0));
}
gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f));
*return_image = gfx::Image(skia);
return true;
}
std::string CreateScopedIdentifier(const std::string& extension_id,
const std::string& id) {
return extension_id + "-" + id;
}
std::string StripScopeFromIdentifier(const std::string& extension_id,
const std::string& id) {
size_t index_of_separator = extension_id.length() + 1;
DCHECK_LT(index_of_separator, id.length());
return id.substr(index_of_separator);
}
class NotificationsApiDelegate : public NotificationDelegate {
public:
NotificationsApiDelegate(ChromeAsyncExtensionFunction* api_function,
Profile* profile,
const std::string& extension_id,
const std::string& id)
: api_function_(api_function),
profile_(profile),
extension_id_(extension_id),
id_(id),
scoped_id_(CreateScopedIdentifier(extension_id, id)),
process_id_(-1) {
DCHECK(api_function_.get());
if (api_function_->render_view_host())
process_id_ = api_function->render_view_host()->GetProcess()->GetID();
}
virtual void Display() OVERRIDE { }
virtual void Error() OVERRIDE {}
virtual void Close(bool by_user) OVERRIDE {
scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
args->Append(new base::FundamentalValue(by_user));
SendEvent(notifications::OnClosed::kEventName, args.Pass());
}
virtual void Click() OVERRIDE {
scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
SendEvent(notifications::OnClicked::kEventName, args.Pass());
}
virtual bool HasClickedListener() OVERRIDE {
return ExtensionSystem::Get(profile_)->event_router()->HasEventListener(
notifications::OnClicked::kEventName);
}
virtual void ButtonClick(int index) OVERRIDE {
scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
args->Append(new base::FundamentalValue(index));
SendEvent(notifications::OnButtonClicked::kEventName, args.Pass());
}
virtual std::string id() const OVERRIDE {
return scoped_id_;
}
virtual int process_id() const OVERRIDE {
return process_id_;
}
virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE {
if (!api_function_.get())
return NULL;
return api_function_->render_view_host();
}
virtual void ReleaseRenderViewHost() OVERRIDE {
api_function_ = NULL;
}
private:
virtual ~NotificationsApiDelegate() {}
void SendEvent(const std::string& name, scoped_ptr<base::ListValue> args) {
scoped_ptr<Event> event(new Event(name, args.Pass()));
ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension(
extension_id_, event.Pass());
}
scoped_ptr<base::ListValue> CreateBaseEventArgs() {
scoped_ptr<base::ListValue> args(new base::ListValue());
args->Append(new base::StringValue(id_));
return args.Pass();
}
scoped_refptr<ChromeAsyncExtensionFunction> api_function_;
Profile* profile_;
const std::string extension_id_;
const std::string id_;
const std::string scoped_id_;
int process_id_;
DISALLOW_COPY_AND_ASSIGN(NotificationsApiDelegate);
};
}
bool NotificationsApiFunction::IsNotificationsApiAvailable() {
return GetExtension()->is_platform_app() || GetExtension()->is_extension();
}
NotificationsApiFunction::NotificationsApiFunction() {
}
NotificationsApiFunction::~NotificationsApiFunction() {
}
bool NotificationsApiFunction::CreateNotification(
const std::string& id,
api::notifications::NotificationOptions* options) {
if (options->type == api::notifications::TEMPLATE_TYPE_NONE ||
!options->icon_url || !options->title || !options->message) {
SetError(kMissingRequiredPropertiesForCreateNotification);
return false;
}
message_center::NotificationType type =
MapApiTemplateTypeToType(options->type);
const base::string16 title(base::UTF8ToUTF16(*options->title));
const base::string16 message(base::UTF8ToUTF16(*options->message));
gfx::Image icon;
NotificationBitmapToGfxImage(options->icon_bitmap.get(), &icon);
message_center::RichNotificationData optional_fields;
if (message_center::IsRichNotificationEnabled()) {
if (options->priority.get())
optional_fields.priority = *options->priority;
if (options->event_time.get())
optional_fields.timestamp = base::Time::FromJsTime(*options->event_time);
if (options->buttons.get()) {
size_t number_of_buttons = options->buttons->size();
number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons;
for (size_t i = 0; i < number_of_buttons; i++) {
message_center::ButtonInfo info(
base::UTF8ToUTF16((*options->buttons)[i]->title));
NotificationBitmapToGfxImage((*options->buttons)[i]->icon_bitmap.get(),
&info.icon);
optional_fields.buttons.push_back(info);
}
}
if (options->context_message) {
optional_fields.context_message =
base::UTF8ToUTF16(*options->context_message);
}
bool has_image = NotificationBitmapToGfxImage(options->image_bitmap.get(),
&optional_fields.image);
if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE))
return false;
bool has_list_items = options->items.get() && options->items->size() > 0;
if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE))
return false;
if (options->progress.get() != NULL) {
if (type != message_center::NOTIFICATION_TYPE_PROGRESS) {
SetError(kUnexpectedProgressValueForNonProgressType);
return false;
}
optional_fields.progress = *options->progress;
if (optional_fields.progress < 0 || optional_fields.progress > 100) {
SetError(kInvalidProgressValue);
return false;
}
}
if (has_list_items) {
using api::notifications::NotificationItem;
std::vector<linked_ptr<NotificationItem> >::iterator i;
for (i = options->items->begin(); i != options->items->end(); ++i) {
message_center::NotificationItem item(
base::UTF8ToUTF16(i->get()->title),
base::UTF8ToUTF16(i->get()->message));
optional_fields.items.push_back(item);
}
}
}
if (options->is_clickable.get())
optional_fields.clickable = *options->is_clickable;
NotificationsApiDelegate* api_delegate(new NotificationsApiDelegate(
this, GetProfile(), extension_->id(), id));
Notification notification(type,
extension_->url(),
title,
message,
icon,
blink::WebTextDirectionDefault,
message_center::NotifierId(
message_center::NotifierId::APPLICATION,
extension_->id()),
base::UTF8ToUTF16(extension_->name()),
base::UTF8ToUTF16(api_delegate->id()),
optional_fields,
api_delegate);
g_browser_process->notification_ui_manager()->Add(notification, GetProfile());
return true;
}
bool NotificationsApiFunction::UpdateNotification(
const std::string& id,
api::notifications::NotificationOptions* options,
Notification* notification) {
if (options->type != api::notifications::TEMPLATE_TYPE_NONE)
notification->set_type(MapApiTemplateTypeToType(options->type));
if (options->title)
notification->set_title(base::UTF8ToUTF16(*options->title));
if (options->message)
notification->set_message(base::UTF8ToUTF16(*options->message));
if (options->icon_bitmap) {
gfx::Image icon;
NotificationBitmapToGfxImage(options->icon_bitmap.get(), &icon);
notification->set_icon(icon);
}
if (message_center::IsRichNotificationEnabled()) {
if (options->priority)
notification->set_priority(*options->priority);
if (options->event_time)
notification->set_timestamp(base::Time::FromJsTime(*options->event_time));
if (options->buttons) {
size_t number_of_buttons = options->buttons->size();
number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons;
std::vector<message_center::ButtonInfo> buttons;
for (size_t i = 0; i < number_of_buttons; i++) {
message_center::ButtonInfo button(
base::UTF8ToUTF16((*options->buttons)[i]->title));
NotificationBitmapToGfxImage((*options->buttons)[i]->icon_bitmap.get(),
&button.icon);
buttons.push_back(button);
}
notification->set_buttons(buttons);
}
if (options->context_message) {
notification->set_context_message(
base::UTF8ToUTF16(*options->context_message));
}
gfx::Image image;
if (NotificationBitmapToGfxImage(options->image_bitmap.get(), &image)) {
if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE)
return false;
notification->set_image(image);
}
if (options->progress) {
if (notification->type() != message_center::NOTIFICATION_TYPE_PROGRESS) {
SetError(kUnexpectedProgressValueForNonProgressType);
return false;
}
int progress = *options->progress;
if (progress < 0 || progress > 100) {
SetError(kInvalidProgressValue);
return false;
}
notification->set_progress(progress);
}
if (options->items.get() && options->items->size() > 0) {
if (notification->type() != message_center::NOTIFICATION_TYPE_MULTIPLE)
return false;
std::vector<message_center::NotificationItem> items;
using api::notifications::NotificationItem;
std::vector<linked_ptr<NotificationItem> >::iterator i;
for (i = options->items->begin(); i != options->items->end(); ++i) {
message_center::NotificationItem item(
base::UTF8ToUTF16(i->get()->title),
base::UTF8ToUTF16(i->get()->message));
items.push_back(item);
}
notification->set_items(items);
}
}
if (options->is_clickable.get())
notification->set_clickable(*options->is_clickable);
g_browser_process->notification_ui_manager()->Update(*notification,
GetProfile());
return true;
}
bool NotificationsApiFunction::AreExtensionNotificationsAllowed() const {
DesktopNotificationService* service =
DesktopNotificationServiceFactory::GetForProfile(GetProfile());
return service->IsNotifierEnabled(message_center::NotifierId(
message_center::NotifierId::APPLICATION, extension_->id()));
}
bool NotificationsApiFunction::IsNotificationsApiEnabled() const {
return CanRunWhileDisabled() || AreExtensionNotificationsAllowed();
}
bool NotificationsApiFunction::CanRunWhileDisabled() const {
return false;
}
bool NotificationsApiFunction::RunImpl() {
if (IsNotificationsApiAvailable() && IsNotificationsApiEnabled()) {
return RunNotificationsApi();
} else {
SendResponse(false);
return true;
}
}
message_center::NotificationType
NotificationsApiFunction::MapApiTemplateTypeToType(
api::notifications::TemplateType type) {
switch (type) {
case api::notifications::TEMPLATE_TYPE_NONE:
case api::notifications::TEMPLATE_TYPE_BASIC:
return message_center::NOTIFICATION_TYPE_BASE_FORMAT;
case api::notifications::TEMPLATE_TYPE_IMAGE:
return message_center::NOTIFICATION_TYPE_IMAGE;
case api::notifications::TEMPLATE_TYPE_LIST:
return message_center::NOTIFICATION_TYPE_MULTIPLE;
case api::notifications::TEMPLATE_TYPE_PROGRESS:
return message_center::NOTIFICATION_TYPE_PROGRESS;
default:
return message_center::NOTIFICATION_TYPE_BASE_FORMAT;
}
}
NotificationsCreateFunction::NotificationsCreateFunction() {
}
NotificationsCreateFunction::~NotificationsCreateFunction() {
}
bool NotificationsCreateFunction::RunNotificationsApi() {
params_ = api::notifications::Create::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
const std::string extension_id(extension_->id());
std::string notification_id;
if (!params_->notification_id.empty()) {
notification_id = params_->notification_id;
} else {
notification_id = base::GenerateGUID();
if (notification_id.empty())
notification_id = base::RandBytesAsString(16);
}
SetResult(new base::StringValue(notification_id));
if (!CreateNotification(notification_id, ¶ms_->options))
return false;
SendResponse(true);
return true;
}
NotificationsUpdateFunction::NotificationsUpdateFunction() {
}
NotificationsUpdateFunction::~NotificationsUpdateFunction() {
}
bool NotificationsUpdateFunction::RunNotificationsApi() {
params_ = api::notifications::Update::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
const Notification* matched_notification =
g_browser_process->notification_ui_manager()->FindById(
CreateScopedIdentifier(extension_->id(), params_->notification_id));
if (!matched_notification) {
SetResult(new base::FundamentalValue(false));
SendResponse(true);
return true;
}
Notification notification = *matched_notification;
bool could_update_notification = UpdateNotification(
params_->notification_id, ¶ms_->options, ¬ification);
SetResult(new base::FundamentalValue(could_update_notification));
if (!could_update_notification)
return false;
SendResponse(true);
return true;
}
NotificationsClearFunction::NotificationsClearFunction() {
}
NotificationsClearFunction::~NotificationsClearFunction() {
}
bool NotificationsClearFunction::RunNotificationsApi() {
params_ = api::notifications::Clear::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(params_.get());
bool cancel_result = g_browser_process->notification_ui_manager()->CancelById(
CreateScopedIdentifier(extension_->id(), params_->notification_id));
SetResult(new base::FundamentalValue(cancel_result));
SendResponse(true);
return true;
}
NotificationsGetAllFunction::NotificationsGetAllFunction() {}
NotificationsGetAllFunction::~NotificationsGetAllFunction() {}
bool NotificationsGetAllFunction::RunNotificationsApi() {
NotificationUIManager* notification_ui_manager =
g_browser_process->notification_ui_manager();
std::set<std::string> notification_ids =
notification_ui_manager->GetAllIdsByProfileAndSourceOrigin(
GetProfile(), extension_->url());
scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
for (std::set<std::string>::iterator iter = notification_ids.begin();
iter != notification_ids.end(); iter++) {
result->SetBooleanWithoutPathExpansion(
StripScopeFromIdentifier(extension_->id(), *iter), true);
}
SetResult(result.release());
SendResponse(true);
return true;
}
NotificationsGetPermissionLevelFunction::
NotificationsGetPermissionLevelFunction() {}
NotificationsGetPermissionLevelFunction::
~NotificationsGetPermissionLevelFunction() {}
bool NotificationsGetPermissionLevelFunction::CanRunWhileDisabled() const {
return true;
}
bool NotificationsGetPermissionLevelFunction::RunNotificationsApi() {
api::notifications::PermissionLevel result =
AreExtensionNotificationsAllowed()
? api::notifications::PERMISSION_LEVEL_GRANTED
: api::notifications::PERMISSION_LEVEL_DENIED;
SetResult(new base::StringValue(api::notifications::ToString(result)));
SendResponse(true);
return true;
}
}