1 // Copyright (c) 2012 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/extensions/api/notifications/notifications_api.h"
6
7 #include "base/callback.h"
8 #include "base/guid.h"
9 #include "base/rand_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/notifications/desktop_notification_service.h"
15 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
16 #include "chrome/browser/notifications/notification.h"
17 #include "chrome/browser/notifications/notification_ui_manager.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "chrome/common/extensions/api/notifications/notification_style.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/web_contents.h"
24 #include "extensions/browser/event_router.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/features/feature.h"
27 #include "third_party/skia/include/core/SkBitmap.h"
28 #include "ui/base/layout.h"
29 #include "ui/gfx/image/image.h"
30 #include "ui/gfx/image/image_skia.h"
31 #include "ui/gfx/image/image_skia_rep.h"
32 #include "ui/message_center/message_center_style.h"
33 #include "ui/message_center/notifier_settings.h"
34 #include "url/gurl.h"
35
36 namespace extensions {
37
38 namespace notifications = api::notifications;
39
40 namespace {
41
42 const char kMissingRequiredPropertiesForCreateNotification[] =
43 "Some of the required properties are missing: type, iconUrl, title and "
44 "message.";
45 const char kUnableToDecodeIconError[] =
46 "Unable to successfully use the provided image.";
47 const char kUnexpectedProgressValueForNonProgressType[] =
48 "The progress value should not be specified for non-progress notification";
49 const char kInvalidProgressValue[] =
50 "The progress value should range from 0 to 100";
51 const char kExtraListItemsProvided[] =
52 "List items provided for notification type != list";
53 const char kExtraImageProvided[] =
54 "Image resource provided for notification type != image";
55
56 // Converts an object with width, height, and data in RGBA format into an
57 // gfx::Image (in ARGB format).
NotificationBitmapToGfxImage(float max_scale,const gfx::Size & target_size_dips,api::notifications::NotificationBitmap * notification_bitmap,gfx::Image * return_image)58 bool NotificationBitmapToGfxImage(
59 float max_scale,
60 const gfx::Size& target_size_dips,
61 api::notifications::NotificationBitmap* notification_bitmap,
62 gfx::Image* return_image) {
63 if (!notification_bitmap)
64 return false;
65
66 const int max_device_pixel_width = target_size_dips.width() * max_scale;
67 const int max_device_pixel_height = target_size_dips.height() * max_scale;
68
69 const int BYTES_PER_PIXEL = 4;
70
71 const int width = notification_bitmap->width;
72 const int height = notification_bitmap->height;
73
74 if (width < 0 || height < 0 || width > max_device_pixel_width ||
75 height > max_device_pixel_height)
76 return false;
77
78 // Ensure we have rgba data.
79 std::string* rgba_data = notification_bitmap->data.get();
80 if (!rgba_data)
81 return false;
82
83 const size_t rgba_data_length = rgba_data->length();
84 const size_t rgba_area = width * height;
85
86 if (rgba_data_length != rgba_area * BYTES_PER_PIXEL)
87 return false;
88
89 // Now configure the bitmap with the sanitized dimensions.
90 SkBitmap bitmap;
91 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
92
93 // Allocate the actual backing store.
94 if (!bitmap.allocPixels())
95 return false;
96
97 // Ensure that our bitmap and our data now refer to the same number of pixels.
98 if (rgba_data_length != bitmap.getSafeSize())
99 return false;
100
101 uint32_t* pixels = bitmap.getAddr32(0, 0);
102 const char* c_rgba_data = rgba_data->data();
103
104 for (size_t t = 0; t < rgba_area; ++t) {
105 // |c_rgba_data| is RGBA, pixels is ARGB.
106 size_t rgba_index = t * BYTES_PER_PIXEL;
107 pixels[t] = SkPreMultiplyColor(
108 ((c_rgba_data[rgba_index + 3] & 0xFF) << 24) |
109 ((c_rgba_data[rgba_index + 0] & 0xFF) << 16) |
110 ((c_rgba_data[rgba_index + 1] & 0xFF) << 8) |
111 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0));
112 }
113
114 // TODO(dewittj): Handle HiDPI images with more than one scale factor
115 // representation.
116 gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f));
117 *return_image = gfx::Image(skia);
118 return true;
119 }
120
121 // Given an extension id and another id, returns an id that is unique
122 // relative to other extensions.
CreateScopedIdentifier(const std::string & extension_id,const std::string & id)123 std::string CreateScopedIdentifier(const std::string& extension_id,
124 const std::string& id) {
125 return extension_id + "-" + id;
126 }
127
128 // Removes the unique internal identifier to send the ID as the
129 // extension expects it.
StripScopeFromIdentifier(const std::string & extension_id,const std::string & id)130 std::string StripScopeFromIdentifier(const std::string& extension_id,
131 const std::string& id) {
132 size_t index_of_separator = extension_id.length() + 1;
133 DCHECK_LT(index_of_separator, id.length());
134
135 return id.substr(index_of_separator);
136 }
137
138 class NotificationsApiDelegate : public NotificationDelegate {
139 public:
NotificationsApiDelegate(ChromeAsyncExtensionFunction * api_function,Profile * profile,const std::string & extension_id,const std::string & id)140 NotificationsApiDelegate(ChromeAsyncExtensionFunction* api_function,
141 Profile* profile,
142 const std::string& extension_id,
143 const std::string& id)
144 : api_function_(api_function),
145 profile_(profile),
146 extension_id_(extension_id),
147 id_(id),
148 scoped_id_(CreateScopedIdentifier(extension_id, id)),
149 process_id_(-1) {
150 DCHECK(api_function_.get());
151 if (api_function_->render_view_host())
152 process_id_ = api_function->render_view_host()->GetProcess()->GetID();
153 }
154
Display()155 virtual void Display() OVERRIDE { }
156
Error()157 virtual void Error() OVERRIDE {}
158
Close(bool by_user)159 virtual void Close(bool by_user) OVERRIDE {
160 EventRouter::UserGestureState gesture =
161 by_user ? EventRouter::USER_GESTURE_ENABLED
162 : EventRouter::USER_GESTURE_NOT_ENABLED;
163 scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
164 args->Append(new base::FundamentalValue(by_user));
165 SendEvent(notifications::OnClosed::kEventName, gesture, args.Pass());
166 }
167
Click()168 virtual void Click() OVERRIDE {
169 scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
170 SendEvent(notifications::OnClicked::kEventName,
171 EventRouter::USER_GESTURE_ENABLED,
172 args.Pass());
173 }
174
HasClickedListener()175 virtual bool HasClickedListener() OVERRIDE {
176 return EventRouter::Get(profile_)->HasEventListener(
177 notifications::OnClicked::kEventName);
178 }
179
ButtonClick(int index)180 virtual void ButtonClick(int index) OVERRIDE {
181 scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
182 args->Append(new base::FundamentalValue(index));
183 SendEvent(notifications::OnButtonClicked::kEventName,
184 EventRouter::USER_GESTURE_ENABLED,
185 args.Pass());
186 }
187
id() const188 virtual std::string id() const OVERRIDE {
189 return scoped_id_;
190 }
191
process_id() const192 virtual int process_id() const OVERRIDE {
193 return process_id_;
194 }
195
GetWebContents() const196 virtual content::WebContents* GetWebContents() const OVERRIDE {
197 // We're holding a reference to api_function_, so we know it'll be valid
198 // until ReleaseRVH is called, and api_function_ (as a
199 // AsyncExtensionFunction) will zero out its copy of render_view_host
200 // when the RVH goes away.
201 if (!api_function_.get())
202 return NULL;
203 content::RenderViewHost* rvh = api_function_->render_view_host();
204 if (!rvh)
205 return NULL;
206 return content::WebContents::FromRenderViewHost(rvh);
207 }
208
ReleaseRenderViewHost()209 virtual void ReleaseRenderViewHost() OVERRIDE {
210 api_function_ = NULL;
211 }
212
213 private:
~NotificationsApiDelegate()214 virtual ~NotificationsApiDelegate() {}
215
SendEvent(const std::string & name,EventRouter::UserGestureState user_gesture,scoped_ptr<base::ListValue> args)216 void SendEvent(const std::string& name,
217 EventRouter::UserGestureState user_gesture,
218 scoped_ptr<base::ListValue> args) {
219 scoped_ptr<Event> event(new Event(name, args.Pass()));
220 event->user_gesture = user_gesture;
221 EventRouter::Get(profile_)->DispatchEventToExtension(extension_id_,
222 event.Pass());
223 }
224
CreateBaseEventArgs()225 scoped_ptr<base::ListValue> CreateBaseEventArgs() {
226 scoped_ptr<base::ListValue> args(new base::ListValue());
227 args->Append(new base::StringValue(id_));
228 return args.Pass();
229 }
230
231 scoped_refptr<ChromeAsyncExtensionFunction> api_function_;
232 Profile* profile_;
233 const std::string extension_id_;
234 const std::string id_;
235 const std::string scoped_id_;
236 int process_id_;
237
238 DISALLOW_COPY_AND_ASSIGN(NotificationsApiDelegate);
239 };
240
241 } // namespace
242
IsNotificationsApiAvailable()243 bool NotificationsApiFunction::IsNotificationsApiAvailable() {
244 // We need to check this explicitly rather than letting
245 // _permission_features.json enforce it, because we're sharing the
246 // chrome.notifications permissions namespace with WebKit notifications.
247 return GetExtension()->is_platform_app() || GetExtension()->is_extension();
248 }
249
NotificationsApiFunction()250 NotificationsApiFunction::NotificationsApiFunction() {
251 }
252
~NotificationsApiFunction()253 NotificationsApiFunction::~NotificationsApiFunction() {
254 }
255
CreateNotification(const std::string & id,api::notifications::NotificationOptions * options)256 bool NotificationsApiFunction::CreateNotification(
257 const std::string& id,
258 api::notifications::NotificationOptions* options) {
259 // First, make sure the required fields exist: type, title, message, icon.
260 // These fields are defined as optional in IDL such that they can be used as
261 // optional for notification updates. But for notification creations, they
262 // should be present.
263 if (options->type == api::notifications::TEMPLATE_TYPE_NONE ||
264 !options->icon_url || !options->title || !options->message) {
265 SetError(kMissingRequiredPropertiesForCreateNotification);
266 return false;
267 }
268
269 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
270
271 float image_scale =
272 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
273
274 // Extract required fields: type, title, message, and icon.
275 message_center::NotificationType type =
276 MapApiTemplateTypeToType(options->type);
277 const base::string16 title(base::UTF8ToUTF16(*options->title));
278 const base::string16 message(base::UTF8ToUTF16(*options->message));
279 gfx::Image icon;
280
281 if (!NotificationBitmapToGfxImage(image_scale,
282 bitmap_sizes.icon_size,
283 options->icon_bitmap.get(),
284 &icon)) {
285 SetError(kUnableToDecodeIconError);
286 return false;
287 }
288
289 // Then, handle any optional data that's been provided.
290 message_center::RichNotificationData optional_fields;
291 if (options->priority.get())
292 optional_fields.priority = *options->priority;
293
294 if (options->event_time.get())
295 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time);
296
297 if (options->buttons.get()) {
298 // Currently we allow up to 2 buttons.
299 size_t number_of_buttons = options->buttons->size();
300 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons;
301
302 for (size_t i = 0; i < number_of_buttons; i++) {
303 message_center::ButtonInfo info(
304 base::UTF8ToUTF16((*options->buttons)[i]->title));
305 NotificationBitmapToGfxImage(image_scale,
306 bitmap_sizes.button_icon_size,
307 (*options->buttons)[i]->icon_bitmap.get(),
308 &info.icon);
309 optional_fields.buttons.push_back(info);
310 }
311 }
312
313 if (options->context_message) {
314 optional_fields.context_message =
315 base::UTF8ToUTF16(*options->context_message);
316 }
317
318 bool has_image = NotificationBitmapToGfxImage(image_scale,
319 bitmap_sizes.image_size,
320 options->image_bitmap.get(),
321 &optional_fields.image);
322 // We should have an image if and only if the type is an image type.
323 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) {
324 SetError(kExtraImageProvided);
325 return false;
326 }
327
328 // We should have list items if and only if the type is a multiple type.
329 bool has_list_items = options->items.get() && options->items->size() > 0;
330 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) {
331 SetError(kExtraListItemsProvided);
332 return false;
333 }
334
335 if (options->progress.get() != NULL) {
336 // We should have progress if and only if the type is a progress type.
337 if (type != message_center::NOTIFICATION_TYPE_PROGRESS) {
338 SetError(kUnexpectedProgressValueForNonProgressType);
339 return false;
340 }
341 optional_fields.progress = *options->progress;
342 // Progress value should range from 0 to 100.
343 if (optional_fields.progress < 0 || optional_fields.progress > 100) {
344 SetError(kInvalidProgressValue);
345 return false;
346 }
347 }
348
349 if (has_list_items) {
350 using api::notifications::NotificationItem;
351 std::vector<linked_ptr<NotificationItem> >::iterator i;
352 for (i = options->items->begin(); i != options->items->end(); ++i) {
353 message_center::NotificationItem item(
354 base::UTF8ToUTF16(i->get()->title),
355 base::UTF8ToUTF16(i->get()->message));
356 optional_fields.items.push_back(item);
357 }
358 }
359
360 if (options->is_clickable.get())
361 optional_fields.clickable = *options->is_clickable;
362
363 NotificationsApiDelegate* api_delegate(new NotificationsApiDelegate(
364 this, GetProfile(), extension_->id(), id)); // ownership is passed to
365 // Notification
366 Notification notification(type,
367 extension_->url(),
368 title,
369 message,
370 icon,
371 blink::WebTextDirectionDefault,
372 message_center::NotifierId(
373 message_center::NotifierId::APPLICATION,
374 extension_->id()),
375 base::UTF8ToUTF16(extension_->name()),
376 base::UTF8ToUTF16(api_delegate->id()),
377 optional_fields,
378 api_delegate);
379
380 g_browser_process->notification_ui_manager()->Add(notification, GetProfile());
381 return true;
382 }
383
UpdateNotification(const std::string & id,api::notifications::NotificationOptions * options,Notification * notification)384 bool NotificationsApiFunction::UpdateNotification(
385 const std::string& id,
386 api::notifications::NotificationOptions* options,
387 Notification* notification) {
388 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
389 float image_scale =
390 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
391
392 // Update optional fields if provided.
393 if (options->type != api::notifications::TEMPLATE_TYPE_NONE)
394 notification->set_type(MapApiTemplateTypeToType(options->type));
395 if (options->title)
396 notification->set_title(base::UTF8ToUTF16(*options->title));
397 if (options->message)
398 notification->set_message(base::UTF8ToUTF16(*options->message));
399
400 // TODO(dewittj): Return error if this fails.
401 if (options->icon_bitmap) {
402 gfx::Image icon;
403 NotificationBitmapToGfxImage(
404 image_scale, bitmap_sizes.icon_size, options->icon_bitmap.get(), &icon);
405 notification->set_icon(icon);
406 }
407
408 if (options->priority)
409 notification->set_priority(*options->priority);
410
411 if (options->event_time)
412 notification->set_timestamp(base::Time::FromJsTime(*options->event_time));
413
414 if (options->buttons) {
415 // Currently we allow up to 2 buttons.
416 size_t number_of_buttons = options->buttons->size();
417 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons;
418
419 std::vector<message_center::ButtonInfo> buttons;
420 for (size_t i = 0; i < number_of_buttons; i++) {
421 message_center::ButtonInfo button(
422 base::UTF8ToUTF16((*options->buttons)[i]->title));
423 NotificationBitmapToGfxImage(image_scale,
424 bitmap_sizes.button_icon_size,
425 (*options->buttons)[i]->icon_bitmap.get(),
426 &button.icon);
427 buttons.push_back(button);
428 }
429 notification->set_buttons(buttons);
430 }
431
432 if (options->context_message) {
433 notification->set_context_message(
434 base::UTF8ToUTF16(*options->context_message));
435 }
436
437 gfx::Image image;
438 bool has_image = NotificationBitmapToGfxImage(image_scale,
439 bitmap_sizes.image_size,
440 options->image_bitmap.get(),
441 &image);
442 if (has_image) {
443 // We should have an image if and only if the type is an image type.
444 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) {
445 SetError(kExtraImageProvided);
446 return false;
447 }
448 notification->set_image(image);
449 }
450
451 if (options->progress) {
452 // We should have progress if and only if the type is a progress type.
453 if (notification->type() != message_center::NOTIFICATION_TYPE_PROGRESS) {
454 SetError(kUnexpectedProgressValueForNonProgressType);
455 return false;
456 }
457 int progress = *options->progress;
458 // Progress value should range from 0 to 100.
459 if (progress < 0 || progress > 100) {
460 SetError(kInvalidProgressValue);
461 return false;
462 }
463 notification->set_progress(progress);
464 }
465
466 if (options->items.get() && options->items->size() > 0) {
467 // We should have list items if and only if the type is a multiple type.
468 if (notification->type() != message_center::NOTIFICATION_TYPE_MULTIPLE) {
469 SetError(kExtraListItemsProvided);
470 return false;
471 }
472
473 std::vector<message_center::NotificationItem> items;
474 using api::notifications::NotificationItem;
475 std::vector<linked_ptr<NotificationItem> >::iterator i;
476 for (i = options->items->begin(); i != options->items->end(); ++i) {
477 message_center::NotificationItem item(
478 base::UTF8ToUTF16(i->get()->title),
479 base::UTF8ToUTF16(i->get()->message));
480 items.push_back(item);
481 }
482 notification->set_items(items);
483 }
484
485 // Then override if it's already set.
486 if (options->is_clickable.get())
487 notification->set_clickable(*options->is_clickable);
488
489 g_browser_process->notification_ui_manager()->Update(*notification,
490 GetProfile());
491 return true;
492 }
493
AreExtensionNotificationsAllowed() const494 bool NotificationsApiFunction::AreExtensionNotificationsAllowed() const {
495 DesktopNotificationService* service =
496 DesktopNotificationServiceFactory::GetForProfile(GetProfile());
497 return service->IsNotifierEnabled(message_center::NotifierId(
498 message_center::NotifierId::APPLICATION, extension_->id()));
499 }
500
IsNotificationsApiEnabled() const501 bool NotificationsApiFunction::IsNotificationsApiEnabled() const {
502 return CanRunWhileDisabled() || AreExtensionNotificationsAllowed();
503 }
504
CanRunWhileDisabled() const505 bool NotificationsApiFunction::CanRunWhileDisabled() const {
506 return false;
507 }
508
RunAsync()509 bool NotificationsApiFunction::RunAsync() {
510 if (IsNotificationsApiAvailable() && IsNotificationsApiEnabled()) {
511 return RunNotificationsApi();
512 } else {
513 SendResponse(false);
514 return true;
515 }
516 }
517
518 message_center::NotificationType
MapApiTemplateTypeToType(api::notifications::TemplateType type)519 NotificationsApiFunction::MapApiTemplateTypeToType(
520 api::notifications::TemplateType type) {
521 switch (type) {
522 case api::notifications::TEMPLATE_TYPE_NONE:
523 case api::notifications::TEMPLATE_TYPE_BASIC:
524 return message_center::NOTIFICATION_TYPE_BASE_FORMAT;
525 case api::notifications::TEMPLATE_TYPE_IMAGE:
526 return message_center::NOTIFICATION_TYPE_IMAGE;
527 case api::notifications::TEMPLATE_TYPE_LIST:
528 return message_center::NOTIFICATION_TYPE_MULTIPLE;
529 case api::notifications::TEMPLATE_TYPE_PROGRESS:
530 return message_center::NOTIFICATION_TYPE_PROGRESS;
531 default:
532 // Gracefully handle newer application code that is running on an older
533 // runtime that doesn't recognize the requested template.
534 return message_center::NOTIFICATION_TYPE_BASE_FORMAT;
535 }
536 }
537
NotificationsCreateFunction()538 NotificationsCreateFunction::NotificationsCreateFunction() {
539 }
540
~NotificationsCreateFunction()541 NotificationsCreateFunction::~NotificationsCreateFunction() {
542 }
543
RunNotificationsApi()544 bool NotificationsCreateFunction::RunNotificationsApi() {
545 params_ = api::notifications::Create::Params::Create(*args_);
546 EXTENSION_FUNCTION_VALIDATE(params_.get());
547
548 const std::string extension_id(extension_->id());
549 std::string notification_id;
550 if (!params_->notification_id.empty()) {
551 // If the caller provided a notificationId, use that.
552 notification_id = params_->notification_id;
553 } else {
554 // Otherwise, use a randomly created GUID. In case that GenerateGUID returns
555 // the empty string, simply generate a random string.
556 notification_id = base::GenerateGUID();
557 if (notification_id.empty())
558 notification_id = base::RandBytesAsString(16);
559 }
560
561 SetResult(new base::StringValue(notification_id));
562
563 // TODO(dewittj): Add more human-readable error strings if this fails.
564 if (!CreateNotification(notification_id, ¶ms_->options))
565 return false;
566
567 SendResponse(true);
568
569 return true;
570 }
571
NotificationsUpdateFunction()572 NotificationsUpdateFunction::NotificationsUpdateFunction() {
573 }
574
~NotificationsUpdateFunction()575 NotificationsUpdateFunction::~NotificationsUpdateFunction() {
576 }
577
RunNotificationsApi()578 bool NotificationsUpdateFunction::RunNotificationsApi() {
579 params_ = api::notifications::Update::Params::Create(*args_);
580 EXTENSION_FUNCTION_VALIDATE(params_.get());
581
582 // We are in update. If the ID doesn't exist, succeed but call the callback
583 // with "false".
584 const Notification* matched_notification =
585 g_browser_process->notification_ui_manager()->FindById(
586 CreateScopedIdentifier(extension_->id(), params_->notification_id));
587 if (!matched_notification) {
588 SetResult(new base::FundamentalValue(false));
589 SendResponse(true);
590 return true;
591 }
592
593 // Copy the existing notification to get a writable version of it.
594 Notification notification = *matched_notification;
595
596 // If we have trouble updating the notification (could be improper use of API
597 // or some other reason), mark the function as failed, calling the callback
598 // with false.
599 // TODO(dewittj): Add more human-readable error strings if this fails.
600 bool could_update_notification = UpdateNotification(
601 params_->notification_id, ¶ms_->options, ¬ification);
602 SetResult(new base::FundamentalValue(could_update_notification));
603 if (!could_update_notification)
604 return false;
605
606 // No trouble, created the notification, send true to the callback and
607 // succeed.
608 SendResponse(true);
609 return true;
610 }
611
NotificationsClearFunction()612 NotificationsClearFunction::NotificationsClearFunction() {
613 }
614
~NotificationsClearFunction()615 NotificationsClearFunction::~NotificationsClearFunction() {
616 }
617
RunNotificationsApi()618 bool NotificationsClearFunction::RunNotificationsApi() {
619 params_ = api::notifications::Clear::Params::Create(*args_);
620 EXTENSION_FUNCTION_VALIDATE(params_.get());
621
622 bool cancel_result = g_browser_process->notification_ui_manager()->CancelById(
623 CreateScopedIdentifier(extension_->id(), params_->notification_id));
624
625 SetResult(new base::FundamentalValue(cancel_result));
626 SendResponse(true);
627
628 return true;
629 }
630
NotificationsGetAllFunction()631 NotificationsGetAllFunction::NotificationsGetAllFunction() {}
632
~NotificationsGetAllFunction()633 NotificationsGetAllFunction::~NotificationsGetAllFunction() {}
634
RunNotificationsApi()635 bool NotificationsGetAllFunction::RunNotificationsApi() {
636 NotificationUIManager* notification_ui_manager =
637 g_browser_process->notification_ui_manager();
638 std::set<std::string> notification_ids =
639 notification_ui_manager->GetAllIdsByProfileAndSourceOrigin(
640 GetProfile(), extension_->url());
641
642 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
643
644 for (std::set<std::string>::iterator iter = notification_ids.begin();
645 iter != notification_ids.end(); iter++) {
646 result->SetBooleanWithoutPathExpansion(
647 StripScopeFromIdentifier(extension_->id(), *iter), true);
648 }
649
650 SetResult(result.release());
651 SendResponse(true);
652
653 return true;
654 }
655
656 NotificationsGetPermissionLevelFunction::
NotificationsGetPermissionLevelFunction()657 NotificationsGetPermissionLevelFunction() {}
658
659 NotificationsGetPermissionLevelFunction::
~NotificationsGetPermissionLevelFunction()660 ~NotificationsGetPermissionLevelFunction() {}
661
CanRunWhileDisabled() const662 bool NotificationsGetPermissionLevelFunction::CanRunWhileDisabled() const {
663 return true;
664 }
665
RunNotificationsApi()666 bool NotificationsGetPermissionLevelFunction::RunNotificationsApi() {
667 api::notifications::PermissionLevel result =
668 AreExtensionNotificationsAllowed()
669 ? api::notifications::PERMISSION_LEVEL_GRANTED
670 : api::notifications::PERMISSION_LEVEL_DENIED;
671
672 SetResult(new base::StringValue(api::notifications::ToString(result)));
673 SendResponse(true);
674
675 return true;
676 }
677
678 } // namespace extensions
679