Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / chrome / browser / extensions / api / notifications / notifications_api.cc
blobb870ce898523ec6848cf56a46bbf29e244457af3
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.
5 #include "chrome/browser/extensions/api/notifications/notifications_api.h"
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/notification.h"
15 #include "chrome/browser/notifications/notification_conversion_helper.h"
16 #include "chrome/browser/notifications/notification_ui_manager.h"
17 #include "chrome/browser/notifications/notifier_state_tracker.h"
18 #include "chrome/browser/notifications/notifier_state_tracker_factory.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/extensions/api/notifications/notification_style.h"
21 #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
22 #include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/render_view_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "extensions/browser/event_router.h"
27 #include "extensions/browser/extension_system_provider.h"
28 #include "extensions/browser/extensions_browser_client.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/features/feature.h"
31 #include "third_party/skia/include/core/SkBitmap.h"
32 #include "ui/base/layout.h"
33 #include "ui/gfx/image/image.h"
34 #include "ui/gfx/image/image_skia.h"
35 #include "ui/gfx/image/image_skia_operations.h"
36 #include "ui/gfx/image/image_skia_rep.h"
37 #include "ui/message_center/message_center_style.h"
38 #include "ui/message_center/notifier_settings.h"
39 #include "url/gurl.h"
41 namespace extensions {
43 namespace notifications = api::notifications;
45 namespace {
47 const char kMissingRequiredPropertiesForCreateNotification[] =
48 "Some of the required properties are missing: type, iconUrl, title and "
49 "message.";
50 const char kUnableToDecodeIconError[] =
51 "Unable to successfully use the provided image.";
52 const char kUnexpectedProgressValueForNonProgressType[] =
53 "The progress value should not be specified for non-progress notification";
54 const char kInvalidProgressValue[] =
55 "The progress value should range from 0 to 100";
56 const char kExtraListItemsProvided[] =
57 "List items provided for notification type != list";
58 const char kExtraImageProvided[] =
59 "Image resource provided for notification type != image";
61 // Given an extension id and another id, returns an id that is unique
62 // relative to other extensions.
63 std::string CreateScopedIdentifier(const std::string& extension_id,
64 const std::string& id) {
65 return extension_id + "-" + id;
68 // Removes the unique internal identifier to send the ID as the
69 // extension expects it.
70 std::string StripScopeFromIdentifier(const std::string& extension_id,
71 const std::string& id) {
72 size_t index_of_separator = extension_id.length() + 1;
73 DCHECK_LT(index_of_separator, id.length());
75 return id.substr(index_of_separator);
78 const gfx::ImageSkia CreateSolidColorImage(int width,
79 int height,
80 SkColor color) {
81 SkBitmap bitmap;
82 bitmap.allocN32Pixels(width, height);
83 bitmap.eraseColor(color);
84 return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
87 // Take the alpha channel of small_image, mask it with the foreground,
88 // then add the masked foreground on top of the background
89 const gfx::Image GetMaskedSmallImage(const gfx::ImageSkia& small_image) {
90 int width = small_image.width();
91 int height = small_image.height();
93 // Background color grey
94 const gfx::ImageSkia background = CreateSolidColorImage(
95 width, height, message_center::kSmallImageMaskBackgroundColor);
96 // Foreground color white
97 const gfx::ImageSkia foreground = CreateSolidColorImage(
98 width, height, message_center::kSmallImageMaskForegroundColor);
99 const gfx::ImageSkia masked_small_image =
100 gfx::ImageSkiaOperations::CreateMaskedImage(foreground, small_image);
101 return gfx::Image(gfx::ImageSkiaOperations::CreateSuperimposedImage(
102 background, masked_small_image));
105 class ShutdownNotifierFactory
106 : public BrowserContextKeyedServiceShutdownNotifierFactory {
107 public:
108 static ShutdownNotifierFactory* GetInstance() {
109 return Singleton<ShutdownNotifierFactory>::get();
112 private:
113 friend struct DefaultSingletonTraits<ShutdownNotifierFactory>;
115 ShutdownNotifierFactory()
116 : BrowserContextKeyedServiceShutdownNotifierFactory(
117 "NotificationsApiDelegate") {
118 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
120 ~ShutdownNotifierFactory() override {}
122 DISALLOW_COPY_AND_ASSIGN(ShutdownNotifierFactory);
125 class NotificationsApiDelegate : public NotificationDelegate {
126 public:
127 NotificationsApiDelegate(ChromeAsyncExtensionFunction* api_function,
128 Profile* profile,
129 const std::string& extension_id,
130 const std::string& id)
131 : api_function_(api_function),
132 event_router_(EventRouter::Get(profile)),
133 extension_id_(extension_id),
134 id_(id),
135 scoped_id_(CreateScopedIdentifier(extension_id, id)) {
136 DCHECK(api_function_);
137 shutdown_notifier_subscription_ =
138 ShutdownNotifierFactory::GetInstance()->Get(profile)->Subscribe(
139 base::Bind(&NotificationsApiDelegate::Shutdown,
140 base::Unretained(this)));
143 void Close(bool by_user) override {
144 EventRouter::UserGestureState gesture =
145 by_user ? EventRouter::USER_GESTURE_ENABLED
146 : EventRouter::USER_GESTURE_NOT_ENABLED;
147 scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
148 args->Append(new base::FundamentalValue(by_user));
149 SendEvent(events::NOTIFICATIONS_ON_CLOSED,
150 notifications::OnClosed::kEventName, gesture, args.Pass());
153 void Click() override {
154 scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
155 SendEvent(events::NOTIFICATIONS_ON_CLICKED,
156 notifications::OnClicked::kEventName,
157 EventRouter::USER_GESTURE_ENABLED, args.Pass());
160 bool HasClickedListener() override {
161 if (!event_router_)
162 return false;
164 return event_router_->HasEventListener(
165 notifications::OnClicked::kEventName);
168 void ButtonClick(int index) override {
169 scoped_ptr<base::ListValue> args(CreateBaseEventArgs());
170 args->Append(new base::FundamentalValue(index));
171 SendEvent(events::NOTIFICATIONS_ON_BUTTON_CLICKED,
172 notifications::OnButtonClicked::kEventName,
173 EventRouter::USER_GESTURE_ENABLED, args.Pass());
176 std::string id() const override { return scoped_id_; }
178 private:
179 ~NotificationsApiDelegate() override {}
181 void SendEvent(events::HistogramValue histogram_value,
182 const std::string& name,
183 EventRouter::UserGestureState user_gesture,
184 scoped_ptr<base::ListValue> args) {
185 if (!event_router_)
186 return;
188 scoped_ptr<Event> event(new Event(histogram_value, name, args.Pass()));
189 event->user_gesture = user_gesture;
190 event_router_->DispatchEventToExtension(extension_id_, event.Pass());
193 void Shutdown() {
194 event_router_ = nullptr;
195 shutdown_notifier_subscription_.reset();
198 scoped_ptr<base::ListValue> CreateBaseEventArgs() {
199 scoped_ptr<base::ListValue> args(new base::ListValue());
200 args->Append(new base::StringValue(id_));
201 return args.Pass();
204 scoped_refptr<ChromeAsyncExtensionFunction> api_function_;
206 // Since this class is refcounted it may outlive the profile. We listen for
207 // profile-keyed service shutdown events and reset to nullptr at that time,
208 // so make sure to check for a valid pointer before use.
209 EventRouter* event_router_;
211 const std::string extension_id_;
212 const std::string id_;
213 const std::string scoped_id_;
215 scoped_ptr<KeyedServiceShutdownNotifier::Subscription>
216 shutdown_notifier_subscription_;
218 DISALLOW_COPY_AND_ASSIGN(NotificationsApiDelegate);
221 } // namespace
223 bool NotificationsApiFunction::IsNotificationsApiAvailable() {
224 // We need to check this explicitly rather than letting
225 // _permission_features.json enforce it, because we're sharing the
226 // chrome.notifications permissions namespace with WebKit notifications.
227 return extension()->is_platform_app() || extension()->is_extension();
230 NotificationsApiFunction::NotificationsApiFunction() {
233 NotificationsApiFunction::~NotificationsApiFunction() {
236 bool NotificationsApiFunction::CreateNotification(
237 const std::string& id,
238 api::notifications::NotificationOptions* options) {
239 // First, make sure the required fields exist: type, title, message, icon.
240 // These fields are defined as optional in IDL such that they can be used as
241 // optional for notification updates. But for notification creations, they
242 // should be present.
243 if (options->type == api::notifications::TEMPLATE_TYPE_NONE ||
244 !options->icon_url || !options->title || !options->message) {
245 SetError(kMissingRequiredPropertiesForCreateNotification);
246 return false;
249 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
251 float image_scale =
252 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
254 // Extract required fields: type, title, message, and icon.
255 message_center::NotificationType type =
256 MapApiTemplateTypeToType(options->type);
257 const base::string16 title(base::UTF8ToUTF16(*options->title));
258 const base::string16 message(base::UTF8ToUTF16(*options->message));
259 gfx::Image icon;
261 if (!options->icon_bitmap.get() ||
262 !NotificationConversionHelper::NotificationBitmapToGfxImage(
263 image_scale, bitmap_sizes.icon_size, *options->icon_bitmap, &icon)) {
264 SetError(kUnableToDecodeIconError);
265 return false;
268 // Then, handle any optional data that's been provided.
269 message_center::RichNotificationData optional_fields;
270 if (options->app_icon_mask_url.get()) {
271 gfx::Image small_icon_mask;
272 if (!NotificationConversionHelper::NotificationBitmapToGfxImage(
273 image_scale, bitmap_sizes.app_icon_mask_size,
274 *options->app_icon_mask_bitmap, &small_icon_mask)) {
275 SetError(kUnableToDecodeIconError);
276 return false;
278 optional_fields.small_image =
279 GetMaskedSmallImage(small_icon_mask.AsImageSkia());
282 if (options->priority.get())
283 optional_fields.priority = *options->priority;
285 if (options->event_time.get())
286 optional_fields.timestamp = base::Time::FromJsTime(*options->event_time);
288 if (options->buttons.get()) {
289 // Currently we allow up to 2 buttons.
290 size_t number_of_buttons = options->buttons->size();
291 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons;
293 for (size_t i = 0; i < number_of_buttons; i++) {
294 message_center::ButtonInfo info(
295 base::UTF8ToUTF16((*options->buttons)[i]->title));
296 extensions::api::notifications::NotificationBitmap* icon_bitmap_ptr =
297 (*options->buttons)[i]->icon_bitmap.get();
298 if (icon_bitmap_ptr) {
299 NotificationConversionHelper::NotificationBitmapToGfxImage(
300 image_scale, bitmap_sizes.button_icon_size, *icon_bitmap_ptr,
301 &info.icon);
303 optional_fields.buttons.push_back(info);
307 if (options->context_message) {
308 optional_fields.context_message =
309 base::UTF8ToUTF16(*options->context_message);
312 bool has_image = options->image_bitmap.get() &&
313 NotificationConversionHelper::NotificationBitmapToGfxImage(
314 image_scale, bitmap_sizes.image_size,
315 *options->image_bitmap, &optional_fields.image);
317 // We should have an image if and only if the type is an image type.
318 if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) {
319 SetError(kExtraImageProvided);
320 return false;
323 // We should have list items if and only if the type is a multiple type.
324 bool has_list_items = options->items.get() && options->items->size() > 0;
325 if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) {
326 SetError(kExtraListItemsProvided);
327 return false;
330 if (options->progress.get() != NULL) {
331 // We should have progress if and only if the type is a progress type.
332 if (type != message_center::NOTIFICATION_TYPE_PROGRESS) {
333 SetError(kUnexpectedProgressValueForNonProgressType);
334 return false;
336 optional_fields.progress = *options->progress;
337 // Progress value should range from 0 to 100.
338 if (optional_fields.progress < 0 || optional_fields.progress > 100) {
339 SetError(kInvalidProgressValue);
340 return false;
344 if (has_list_items) {
345 using api::notifications::NotificationItem;
346 std::vector<linked_ptr<NotificationItem> >::iterator i;
347 for (i = options->items->begin(); i != options->items->end(); ++i) {
348 message_center::NotificationItem item(
349 base::UTF8ToUTF16(i->get()->title),
350 base::UTF8ToUTF16(i->get()->message));
351 optional_fields.items.push_back(item);
355 if (options->is_clickable.get())
356 optional_fields.clickable = *options->is_clickable;
358 NotificationsApiDelegate* api_delegate(new NotificationsApiDelegate(
359 this, GetProfile(), extension_->id(), id)); // ownership is passed to
360 // Notification
361 Notification notification(type,
362 extension_->url(),
363 title,
364 message,
365 icon,
366 message_center::NotifierId(
367 message_center::NotifierId::APPLICATION,
368 extension_->id()),
369 base::UTF8ToUTF16(extension_->name()),
370 api_delegate->id(),
371 optional_fields,
372 api_delegate);
374 g_browser_process->notification_ui_manager()->Add(notification, GetProfile());
375 return true;
378 bool NotificationsApiFunction::UpdateNotification(
379 const std::string& id,
380 api::notifications::NotificationOptions* options,
381 Notification* notification) {
382 NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes();
383 float image_scale =
384 ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactors().back());
386 // Update optional fields if provided.
387 if (options->type != api::notifications::TEMPLATE_TYPE_NONE)
388 notification->set_type(MapApiTemplateTypeToType(options->type));
389 if (options->title)
390 notification->set_title(base::UTF8ToUTF16(*options->title));
391 if (options->message)
392 notification->set_message(base::UTF8ToUTF16(*options->message));
394 if (options->icon_bitmap.get()) {
395 gfx::Image icon;
396 if (!NotificationConversionHelper::NotificationBitmapToGfxImage(
397 image_scale, bitmap_sizes.icon_size, *options->icon_bitmap,
398 &icon)) {
399 SetError(kUnableToDecodeIconError);
400 return false;
402 notification->set_icon(icon);
405 if (options->app_icon_mask_bitmap.get()) {
406 gfx::Image app_icon_mask;
407 if (!NotificationConversionHelper::NotificationBitmapToGfxImage(
408 image_scale, bitmap_sizes.app_icon_mask_size,
409 *options->app_icon_mask_bitmap, &app_icon_mask)) {
410 SetError(kUnableToDecodeIconError);
411 return false;
413 notification->set_small_image(
414 GetMaskedSmallImage(app_icon_mask.AsImageSkia()));
417 if (options->priority)
418 notification->set_priority(*options->priority);
420 if (options->event_time)
421 notification->set_timestamp(base::Time::FromJsTime(*options->event_time));
423 if (options->buttons) {
424 // Currently we allow up to 2 buttons.
425 size_t number_of_buttons = options->buttons->size();
426 number_of_buttons = number_of_buttons > 2 ? 2 : number_of_buttons;
428 std::vector<message_center::ButtonInfo> buttons;
429 for (size_t i = 0; i < number_of_buttons; i++) {
430 message_center::ButtonInfo button(
431 base::UTF8ToUTF16((*options->buttons)[i]->title));
432 extensions::api::notifications::NotificationBitmap* icon_bitmap_ptr =
433 (*options->buttons)[i]->icon_bitmap.get();
434 if (icon_bitmap_ptr) {
435 NotificationConversionHelper::NotificationBitmapToGfxImage(
436 image_scale, bitmap_sizes.button_icon_size, *icon_bitmap_ptr,
437 &button.icon);
439 buttons.push_back(button);
441 notification->set_buttons(buttons);
444 if (options->context_message) {
445 notification->set_context_message(
446 base::UTF8ToUTF16(*options->context_message));
449 gfx::Image image;
450 bool has_image =
451 options->image_bitmap.get() &&
452 NotificationConversionHelper::NotificationBitmapToGfxImage(
453 image_scale, bitmap_sizes.image_size, *options->image_bitmap, &image);
455 if (has_image) {
456 // We should have an image if and only if the type is an image type.
457 if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) {
458 SetError(kExtraImageProvided);
459 return false;
461 notification->set_image(image);
464 if (options->progress) {
465 // We should have progress if and only if the type is a progress type.
466 if (notification->type() != message_center::NOTIFICATION_TYPE_PROGRESS) {
467 SetError(kUnexpectedProgressValueForNonProgressType);
468 return false;
470 int progress = *options->progress;
471 // Progress value should range from 0 to 100.
472 if (progress < 0 || progress > 100) {
473 SetError(kInvalidProgressValue);
474 return false;
476 notification->set_progress(progress);
479 if (options->items.get() && options->items->size() > 0) {
480 // We should have list items if and only if the type is a multiple type.
481 if (notification->type() != message_center::NOTIFICATION_TYPE_MULTIPLE) {
482 SetError(kExtraListItemsProvided);
483 return false;
486 std::vector<message_center::NotificationItem> items;
487 using api::notifications::NotificationItem;
488 std::vector<linked_ptr<NotificationItem> >::iterator i;
489 for (i = options->items->begin(); i != options->items->end(); ++i) {
490 message_center::NotificationItem item(
491 base::UTF8ToUTF16(i->get()->title),
492 base::UTF8ToUTF16(i->get()->message));
493 items.push_back(item);
495 notification->set_items(items);
498 // Then override if it's already set.
499 if (options->is_clickable.get())
500 notification->set_clickable(*options->is_clickable);
502 g_browser_process->notification_ui_manager()->Update(*notification,
503 GetProfile());
504 return true;
507 bool NotificationsApiFunction::AreExtensionNotificationsAllowed() const {
508 NotifierStateTracker* notifier_state_tracker =
509 NotifierStateTrackerFactory::GetForProfile(GetProfile());
511 return notifier_state_tracker->IsNotifierEnabled(
512 message_center::NotifierId(message_center::NotifierId::APPLICATION,
513 extension_->id()));
516 bool NotificationsApiFunction::IsNotificationsApiEnabled() const {
517 return CanRunWhileDisabled() || AreExtensionNotificationsAllowed();
520 bool NotificationsApiFunction::CanRunWhileDisabled() const {
521 return false;
524 bool NotificationsApiFunction::RunAsync() {
525 if (IsNotificationsApiAvailable() && IsNotificationsApiEnabled()) {
526 return RunNotificationsApi();
527 } else {
528 SendResponse(false);
529 return true;
533 message_center::NotificationType
534 NotificationsApiFunction::MapApiTemplateTypeToType(
535 api::notifications::TemplateType type) {
536 switch (type) {
537 case api::notifications::TEMPLATE_TYPE_NONE:
538 case api::notifications::TEMPLATE_TYPE_BASIC:
539 return message_center::NOTIFICATION_TYPE_BASE_FORMAT;
540 case api::notifications::TEMPLATE_TYPE_IMAGE:
541 return message_center::NOTIFICATION_TYPE_IMAGE;
542 case api::notifications::TEMPLATE_TYPE_LIST:
543 return message_center::NOTIFICATION_TYPE_MULTIPLE;
544 case api::notifications::TEMPLATE_TYPE_PROGRESS:
545 return message_center::NOTIFICATION_TYPE_PROGRESS;
546 default:
547 // Gracefully handle newer application code that is running on an older
548 // runtime that doesn't recognize the requested template.
549 return message_center::NOTIFICATION_TYPE_BASE_FORMAT;
553 NotificationsCreateFunction::NotificationsCreateFunction() {
556 NotificationsCreateFunction::~NotificationsCreateFunction() {
559 bool NotificationsCreateFunction::RunNotificationsApi() {
560 params_ = api::notifications::Create::Params::Create(*args_);
561 EXTENSION_FUNCTION_VALIDATE(params_.get());
563 const std::string extension_id(extension_->id());
564 std::string notification_id;
565 if (params_->notification_id.get() && !params_->notification_id->empty()) {
566 // If the caller provided a notificationId, use that.
567 notification_id = *params_->notification_id;
568 } else {
569 // Otherwise, use a randomly created GUID. In case that GenerateGUID returns
570 // the empty string, simply generate a random string.
571 notification_id = base::GenerateGUID();
572 if (notification_id.empty())
573 notification_id = base::RandBytesAsString(16);
576 SetResult(new base::StringValue(notification_id));
578 // TODO(dewittj): Add more human-readable error strings if this fails.
579 if (!CreateNotification(notification_id, &params_->options))
580 return false;
582 SendResponse(true);
584 return true;
587 NotificationsUpdateFunction::NotificationsUpdateFunction() {
590 NotificationsUpdateFunction::~NotificationsUpdateFunction() {
593 bool NotificationsUpdateFunction::RunNotificationsApi() {
594 params_ = api::notifications::Update::Params::Create(*args_);
595 EXTENSION_FUNCTION_VALIDATE(params_.get());
597 // We are in update. If the ID doesn't exist, succeed but call the callback
598 // with "false".
599 const Notification* matched_notification =
600 g_browser_process->notification_ui_manager()->FindById(
601 CreateScopedIdentifier(extension_->id(), params_->notification_id),
602 NotificationUIManager::GetProfileID(GetProfile()));
603 if (!matched_notification) {
604 SetResult(new base::FundamentalValue(false));
605 SendResponse(true);
606 return true;
609 // Copy the existing notification to get a writable version of it.
610 Notification notification = *matched_notification;
612 // If we have trouble updating the notification (could be improper use of API
613 // or some other reason), mark the function as failed, calling the callback
614 // with false.
615 // TODO(dewittj): Add more human-readable error strings if this fails.
616 bool could_update_notification = UpdateNotification(
617 params_->notification_id, &params_->options, &notification);
618 SetResult(new base::FundamentalValue(could_update_notification));
619 if (!could_update_notification)
620 return false;
622 // No trouble, created the notification, send true to the callback and
623 // succeed.
624 SendResponse(true);
625 return true;
628 NotificationsClearFunction::NotificationsClearFunction() {
631 NotificationsClearFunction::~NotificationsClearFunction() {
634 bool NotificationsClearFunction::RunNotificationsApi() {
635 params_ = api::notifications::Clear::Params::Create(*args_);
636 EXTENSION_FUNCTION_VALIDATE(params_.get());
638 bool cancel_result = g_browser_process->notification_ui_manager()->CancelById(
639 CreateScopedIdentifier(extension_->id(), params_->notification_id),
640 NotificationUIManager::GetProfileID(GetProfile()));
642 SetResult(new base::FundamentalValue(cancel_result));
643 SendResponse(true);
645 return true;
648 NotificationsGetAllFunction::NotificationsGetAllFunction() {}
650 NotificationsGetAllFunction::~NotificationsGetAllFunction() {}
652 bool NotificationsGetAllFunction::RunNotificationsApi() {
653 NotificationUIManager* notification_ui_manager =
654 g_browser_process->notification_ui_manager();
655 std::set<std::string> notification_ids =
656 notification_ui_manager->GetAllIdsByProfileAndSourceOrigin(
657 NotificationUIManager::GetProfileID(GetProfile()), extension_->url());
659 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
661 for (std::set<std::string>::iterator iter = notification_ids.begin();
662 iter != notification_ids.end(); iter++) {
663 result->SetBooleanWithoutPathExpansion(
664 StripScopeFromIdentifier(extension_->id(), *iter), true);
667 SetResult(result.release());
668 SendResponse(true);
670 return true;
673 NotificationsGetPermissionLevelFunction::
674 NotificationsGetPermissionLevelFunction() {}
676 NotificationsGetPermissionLevelFunction::
677 ~NotificationsGetPermissionLevelFunction() {}
679 bool NotificationsGetPermissionLevelFunction::CanRunWhileDisabled() const {
680 return true;
683 bool NotificationsGetPermissionLevelFunction::RunNotificationsApi() {
684 api::notifications::PermissionLevel result =
685 AreExtensionNotificationsAllowed()
686 ? api::notifications::PERMISSION_LEVEL_GRANTED
687 : api::notifications::PERMISSION_LEVEL_DENIED;
689 SetResult(new base::StringValue(api::notifications::ToString(result)));
690 SendResponse(true);
692 return true;
695 } // namespace extensions