| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/api/notifications/notifications_api.h" |
| |
| #include <stddef.h> |
| #include <string.h> |
| |
| #include <utility> |
| |
| #include "base/functional/callback.h" |
| #include "base/rand_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "base/uuid.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h" |
| #include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h" |
| #include "chrome/browser/extensions/api/notifications/extension_notification_handler.h" |
| #include "chrome/browser/notifications/notification_common.h" |
| #include "chrome/browser/notifications/notification_handler.h" |
| #include "chrome/browser/notifications/notifier_state_tracker.h" |
| #include "chrome/browser/notifications/notifier_state_tracker_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/extensions/api/notifications/notification_style.h" |
| #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h" |
| #include "components/keyed_service/core/keyed_service_shutdown_notifier.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/browser/event_router.h" |
| #include "extensions/browser/extension_system_provider.h" |
| #include "extensions/browser/extensions_browser_client.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_id.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/base/resource/resource_scale_factor.h" |
| #include "ui/gfx/geometry/skia_conversions.h" |
| #include "ui/gfx/image/image.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| #include "ui/gfx/image/image_skia_rep.h" |
| #include "ui/message_center/public/cpp/message_center_constants.h" |
| #include "ui/message_center/public/cpp/notification.h" |
| #include "ui/message_center/public/cpp/notification_delegate.h" |
| #include "ui/message_center/public/cpp/notifier_id.h" |
| #include "url/gurl.h" |
| |
| #if BUILDFLAG(ENABLE_PLATFORM_APPS) |
| #include "extensions/browser/app_window/app_window.h" |
| #include "extensions/browser/app_window/app_window_registry.h" |
| #include "extensions/browser/app_window/native_app_window.h" |
| #endif // BUILDFLAG(ENABLE_PLATFORM_APPS) |
| |
| using message_center::NotifierId; |
| |
| namespace extensions { |
| |
| namespace notifications = api::notifications; |
| |
| namespace { |
| |
| // The maximum length of a notification ID, in number of characters. Some |
| // platforms have limitattions on the length of the ID. |
| constexpr int kNotificationIdLengthLimit = 500; |
| |
| const char kMissingRequiredPropertiesForCreateNotification[] = |
| "Some of the required properties are missing: type, iconUrl, title and " |
| "message."; |
| const char kUnableToDecodeIconError[] = |
| "Unable to successfully use the provided image."; |
| 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"; |
| const char kExtraListItemsProvided[] = |
| "List items provided for notification type != list"; |
| const char kExtraImageProvided[] = |
| "Image resource provided for notification type != image"; |
| const char kNotificationIdTooLong[] = |
| "The notification's ID should be %d characters or less"; |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| const char kLowPriorityDeprecatedOnPlatform[] = |
| "Low-priority notifications are deprecated on this platform."; |
| #endif |
| |
| // Given an extension id and another id, returns an id that is unique |
| // relative to other extensions. |
| std::string CreateScopedIdentifier(const ExtensionId& extension_id, |
| const std::string& id) { |
| return extension_id + "-" + id; |
| } |
| |
| // Removes the unique internal identifier to send the ID as the |
| // extension expects it. |
| std::string StripScopeFromIdentifier(const ExtensionId& extension_id, |
| const std::string& scoped_id) { |
| size_t index_of_separator = extension_id.length() + 1; |
| DCHECK_LT(index_of_separator, scoped_id.length()); |
| |
| return scoped_id.substr(index_of_separator); |
| } |
| |
| // Converts the |notification_bitmap| (in RGBA format) to the |*return_image| |
| // (which is in ARGB format). |
| bool NotificationBitmapToGfxImage( |
| float max_scale, |
| const gfx::Size& target_size_dips, |
| const notifications::NotificationBitmap& notification_bitmap, |
| gfx::Image* return_image) { |
| const int max_device_pixel_width = target_size_dips.width() * max_scale; |
| const int max_device_pixel_height = target_size_dips.height() * max_scale; |
| |
| const int kBytesPerPixel = 4; |
| |
| const int width = notification_bitmap.width; |
| const int height = notification_bitmap.height; |
| |
| if (width < 0 || height < 0 || width > max_device_pixel_width || |
| height > max_device_pixel_height) |
| return false; |
| |
| // Ensure we have rgba data. |
| if (!notification_bitmap.data) { |
| return false; |
| } |
| |
| const std::vector<uint8_t>& rgba_data = *notification_bitmap.data; |
| |
| const size_t rgba_data_length = rgba_data.size(); |
| const size_t rgba_area = width * height; |
| |
| if (rgba_data_length != rgba_area * kBytesPerPixel) |
| return false; |
| |
| SkBitmap bitmap; |
| // Allocate the actual backing store with the sanitized dimensions. |
| std::vector<uint32_t> pixels(rgba_area); |
| SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); |
| bitmap.setInfo(info, width * kBytesPerPixel); |
| bitmap.setPixels(&pixels[0]); |
| |
| // Ensure that our bitmap and our data now refer to the same number of pixels. |
| if (rgba_data_length != bitmap.computeByteSize()) |
| return false; |
| |
| for (size_t t = 0; t < rgba_area; ++t) { |
| // `rgba_data` is RGBA, pixels is ARGB. |
| size_t rgba_index = t * kBytesPerPixel; |
| pixels[t] = SkPreMultiplyColor(((rgba_data[rgba_index + 3] & 0xFF) << 24) | |
| ((rgba_data[rgba_index + 0] & 0xFF) << 16) | |
| ((rgba_data[rgba_index + 1] & 0xFF) << 8) | |
| ((rgba_data[rgba_index + 2] & 0xFF) << 0)); |
| } |
| |
| // Make a copy, since the current bitmap is using a local std::vector for |
| // storage. |
| SkBitmap copy; |
| if (!copy.tryAllocPixels(bitmap.info())) { |
| return false; |
| } |
| |
| // Copy the bitmap. |
| bitmap.readPixels(copy.pixmap()); |
| |
| // TODO(dewittj): Handle HiDPI images with more than one scale factor |
| // representation. |
| gfx::ImageSkia skia = gfx::ImageSkia::CreateFromBitmap(copy, 1.0f); |
| *return_image = gfx::Image(skia); |
| return true; |
| } |
| |
| // Returns true if a notification with the given origin should show over the |
| // currently fullscreen app window. If there is no fullscreen app window, |
| // returns false. |
| bool ShouldShowOverCurrentFullscreenWindow(Profile* profile, |
| const GURL& origin) { |
| #if BUILDFLAG(ENABLE_PLATFORM_APPS) |
| DCHECK(profile); |
| ExtensionId extension_id = |
| ExtensionNotificationHandler::GetExtensionId(origin); |
| DCHECK(!extension_id.empty()); |
| AppWindowRegistry::AppWindowList windows = |
| AppWindowRegistry::Get(profile)->GetAppWindowsForApp(extension_id); |
| for (AppWindow* window : windows) { |
| if (window->IsFullscreen() && window->GetBaseWindow()->IsActive()) |
| return true; |
| } |
| #endif // BUILDFLAG(ENABLE_PLATFORM_APPS) |
| return false; |
| } |
| |
| } // namespace |
| |
| bool NotificationsApiFunction::IsNotificationsApiAvailable() { |
| // We need to check this explicitly rather than letting |
| // _permission_features.json enforce it, because we're sharing the |
| // chrome.notifications permissions namespace with WebKit notifications. |
| return extension()->is_platform_app() || extension()->is_extension(); |
| } |
| |
| NotificationsApiFunction::NotificationsApiFunction() = default; |
| |
| NotificationsApiFunction::~NotificationsApiFunction() = default; |
| |
| bool NotificationsApiFunction::CreateNotification( |
| const std::string& id, |
| api::notifications::NotificationOptions* options, |
| std::string* error) { |
| // First, make sure the required fields exist: type, title, message, icon. |
| // These fields are defined as optional in IDL such that they can be used as |
| // optional for notification updates. But for notification creations, they |
| // should be present. |
| if (options->type == api::notifications::TemplateType::kNone || |
| !options->icon_url || !options->title || !options->message) { |
| *error = kMissingRequiredPropertiesForCreateNotification; |
| return false; |
| } |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| if (options->priority && |
| *options->priority < message_center::DEFAULT_PRIORITY) { |
| *error = kLowPriorityDeprecatedOnPlatform; |
| return false; |
| } |
| #endif |
| |
| NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes(); |
| |
| const float image_scale = ui::GetScaleForMaxSupportedResourceScaleFactor(); |
| |
| // Extract required fields: type, title, message, and icon. |
| message_center::NotificationType type = |
| MapApiTemplateTypeToType(options->type); |
| |
| const std::u16string title(base::UTF8ToUTF16(*options->title)); |
| const std::u16string message(base::UTF8ToUTF16(*options->message)); |
| gfx::Image icon; |
| |
| if (!options->icon_bitmap || |
| !NotificationBitmapToGfxImage(image_scale, bitmap_sizes.icon_size, |
| *options->icon_bitmap, &icon)) { |
| *error = kUnableToDecodeIconError; |
| return false; |
| } |
| |
| // Then, handle any optional data that's been provided. |
| message_center::RichNotificationData optional_fields; |
| if (options->app_icon_mask_url) { |
| gfx::Image small_icon_mask; |
| if (!NotificationBitmapToGfxImage( |
| image_scale, bitmap_sizes.app_icon_mask_size, |
| *options->app_icon_mask_bitmap, &small_icon_mask)) { |
| *error = kUnableToDecodeIconError; |
| return false; |
| } |
| optional_fields.small_image = small_icon_mask; |
| optional_fields.small_image_needs_additional_masking = true; |
| } |
| |
| if (options->priority) |
| optional_fields.priority = *options->priority; |
| |
| if (options->event_time) |
| optional_fields.timestamp = |
| base::Time::FromMillisecondsSinceUnixEpoch(*options->event_time); |
| |
| if (options->silent) |
| optional_fields.silent = *options->silent; |
| |
| if (options->buttons) { |
| // Currently we allow up to 2 buttons. |
| 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)); |
| const auto& icon_bitmap = (*options->buttons)[i].icon_bitmap; |
| if (icon_bitmap) { |
| NotificationBitmapToGfxImage(image_scale, bitmap_sizes.button_icon_size, |
| *icon_bitmap, &info.icon); |
| } |
| optional_fields.buttons.push_back(info); |
| } |
| } |
| |
| if (options->context_message) { |
| optional_fields.context_message = |
| base::UTF8ToUTF16(*options->context_message); |
| } |
| |
| bool has_image = options->image_bitmap && |
| NotificationBitmapToGfxImage( |
| image_scale, bitmap_sizes.image_size, |
| *options->image_bitmap, &optional_fields.image); |
| |
| // We should have an image if and only if the type is an image type. |
| if (has_image != (type == message_center::NOTIFICATION_TYPE_IMAGE)) { |
| *error = kExtraImageProvided; |
| return false; |
| } |
| |
| // We should have list items if and only if the type is a multiple type. |
| bool has_list_items = options->items && !options->items->empty(); |
| if (has_list_items != (type == message_center::NOTIFICATION_TYPE_MULTIPLE)) { |
| *error = kExtraListItemsProvided; |
| return false; |
| } |
| |
| if (options->progress) { |
| // We should have progress if and only if the type is a progress type. |
| if (type != message_center::NOTIFICATION_TYPE_PROGRESS) { |
| *error = kUnexpectedProgressValueForNonProgressType; |
| return false; |
| } |
| optional_fields.progress = *options->progress; |
| // Progress value should range from 0 to 100. |
| if (optional_fields.progress < 0 || optional_fields.progress > 100) { |
| *error = kInvalidProgressValue; |
| return false; |
| } |
| } |
| |
| if (has_list_items) { |
| using api::notifications::NotificationItem; |
| for (const NotificationItem& api_item : *options->items) { |
| optional_fields.items.push_back({base::UTF8ToUTF16(api_item.title), |
| base::UTF8ToUTF16(api_item.message)}); |
| } |
| } |
| |
| optional_fields.settings_button_handler = |
| message_center::SettingsButtonHandler::INLINE; |
| |
| // TODO(crbug.com/41348342): Remove the manual limitation in favor of an IDL |
| // annotation once supported. |
| if (id.size() > kNotificationIdLengthLimit) { |
| *error = |
| base::StringPrintf(kNotificationIdTooLong, kNotificationIdLengthLimit); |
| return false; |
| } |
| |
| std::string notification_id = CreateScopedIdentifier(extension_->id(), id); |
| message_center::Notification notification( |
| type, notification_id, title, message, ui::ImageModel::FromImage(icon), |
| base::UTF8ToUTF16(extension_->name()), extension_->url(), |
| message_center::NotifierId(message_center::NotifierType::APPLICATION, |
| extension_->id()), |
| optional_fields, nullptr /* delegate */); |
| |
| // Apply the "requireInteraction" flag. The value defaults to false. |
| notification.set_never_timeout(options->require_interaction && |
| *options->require_interaction); |
| |
| // For a progress notification the message parameter won't be displayed in the |
| // notification. Therefore, its value is passed to progress_status which will |
| // be displayed. |
| if (type == message_center::NOTIFICATION_TYPE_PROGRESS) { |
| notification.set_progress_status(message); |
| } |
| |
| if (ShouldShowOverCurrentFullscreenWindow(GetProfile(), |
| notification.origin_url())) { |
| notification.set_fullscreen_visibility( |
| message_center::FullscreenVisibility::OVER_USER); |
| } |
| |
| GetDisplayHelper()->Display(notification); |
| return true; |
| } |
| |
| bool NotificationsApiFunction::UpdateNotification( |
| const std::string& id, |
| api::notifications::NotificationOptions* options, |
| message_center::Notification* notification, |
| std::string* error) { |
| #if !BUILDFLAG(IS_CHROMEOS) |
| if (options->priority && |
| *options->priority < message_center::DEFAULT_PRIORITY) { |
| *error = kLowPriorityDeprecatedOnPlatform; |
| return false; |
| } |
| #endif |
| |
| NotificationBitmapSizes bitmap_sizes = GetNotificationBitmapSizes(); |
| const float image_scale = ui::GetScaleForMaxSupportedResourceScaleFactor(); |
| |
| // Update optional fields if provided. |
| if (options->type != api::notifications::TemplateType::kNone) { |
| 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; |
| if (!NotificationBitmapToGfxImage( |
| image_scale, bitmap_sizes.icon_size, *options->icon_bitmap, |
| &icon)) { |
| *error = kUnableToDecodeIconError; |
| return false; |
| } |
| notification->set_icon(ui::ImageModel::FromImage(icon)); |
| } |
| |
| if (options->app_icon_mask_bitmap) { |
| gfx::Image app_icon_mask; |
| if (!NotificationBitmapToGfxImage( |
| image_scale, bitmap_sizes.app_icon_mask_size, |
| *options->app_icon_mask_bitmap, &app_icon_mask)) { |
| *error = kUnableToDecodeIconError; |
| return false; |
| } |
| notification->SetSmallImage(app_icon_mask); |
| notification->set_small_image_needs_additional_masking(true); |
| } |
| |
| if (options->priority) |
| notification->set_priority(*options->priority); |
| |
| if (options->event_time) |
| notification->set_timestamp( |
| base::Time::FromMillisecondsSinceUnixEpoch(*options->event_time)); |
| |
| if (options->silent) |
| notification->set_silent(*options->silent); |
| |
| if (options->buttons) { |
| // Currently we allow up to 2 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)); |
| const auto& icon_bitmap = (*options->buttons)[i].icon_bitmap; |
| if (icon_bitmap) { |
| NotificationBitmapToGfxImage(image_scale, bitmap_sizes.button_icon_size, |
| *icon_bitmap, &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; |
| bool has_image = |
| options->image_bitmap && |
| NotificationBitmapToGfxImage(image_scale, bitmap_sizes.image_size, |
| *options->image_bitmap, &image); |
| |
| if (has_image) { |
| // We should have an image if and only if the type is an image type. |
| if (notification->type() != message_center::NOTIFICATION_TYPE_IMAGE) { |
| *error = kExtraImageProvided; |
| return false; |
| } |
| notification->SetImage(image); |
| } |
| |
| if (options->progress) { |
| // We should have progress if and only if the type is a progress type. |
| if (notification->type() != message_center::NOTIFICATION_TYPE_PROGRESS) { |
| *error = kUnexpectedProgressValueForNonProgressType; |
| return false; |
| } |
| int progress = *options->progress; |
| // Progress value should range from 0 to 100. |
| if (progress < 0 || progress > 100) { |
| *error = kInvalidProgressValue; |
| return false; |
| } |
| notification->set_progress(progress); |
| } |
| |
| // For a progress notification the message parameter won't be displayed in the |
| // notification. Therefore, its value is passed to progress_status which will |
| // be displayed. |
| if (options->message && |
| notification->type() == message_center::NOTIFICATION_TYPE_PROGRESS) { |
| notification->set_progress_status(base::UTF8ToUTF16(*options->message)); |
| } |
| |
| if (options->items && !options->items->empty()) { |
| // We should have list items if and only if the type is a multiple type. |
| if (notification->type() != message_center::NOTIFICATION_TYPE_MULTIPLE) { |
| *error = kExtraListItemsProvided; |
| return false; |
| } |
| |
| std::vector<message_center::NotificationItem> items; |
| using api::notifications::NotificationItem; |
| for (const NotificationItem& api_item : *options->items) { |
| items.push_back({base::UTF8ToUTF16(api_item.title), |
| base::UTF8ToUTF16(api_item.message)}); |
| } |
| notification->set_items(items); |
| } |
| |
| // It's safe to follow the regular path for adding a new notification as it's |
| // already been verified that there is a notification that can be updated. |
| GetDisplayHelper()->Display(*notification); |
| |
| return true; |
| } |
| |
| bool NotificationsApiFunction::AreExtensionNotificationsAllowed() const { |
| NotifierStateTracker* notifier_state_tracker = |
| NotifierStateTrackerFactory::GetForProfile(GetProfile()); |
| |
| return notifier_state_tracker->IsNotifierEnabled(message_center::NotifierId( |
| message_center::NotifierType::APPLICATION, extension_->id())); |
| } |
| |
| bool NotificationsApiFunction::IsNotificationsApiEnabled() const { |
| return CanRunWhileDisabled() || AreExtensionNotificationsAllowed(); |
| } |
| |
| bool NotificationsApiFunction::CanRunWhileDisabled() const { |
| return false; |
| } |
| |
| ExtensionNotificationDisplayHelper* NotificationsApiFunction::GetDisplayHelper() |
| const { |
| return ExtensionNotificationDisplayHelperFactory::GetForProfile(GetProfile()); |
| } |
| |
| Profile* NotificationsApiFunction::GetProfile() const { |
| return Profile::FromBrowserContext(browser_context()); |
| } |
| |
| ExtensionFunction::ResponseAction NotificationsApiFunction::Run() { |
| if (IsNotificationsApiAvailable() && IsNotificationsApiEnabled()) { |
| return RunNotificationsApi(); |
| } else { |
| return RespondNow(Error("")); |
| } |
| } |
| |
| message_center::NotificationType |
| NotificationsApiFunction::MapApiTemplateTypeToType( |
| api::notifications::TemplateType type) { |
| switch (type) { |
| case api::notifications::TemplateType::kNone: |
| case api::notifications::TemplateType::kBasic: |
| return message_center::NOTIFICATION_TYPE_SIMPLE; |
| case api::notifications::TemplateType::kImage: |
| return message_center::NOTIFICATION_TYPE_IMAGE; |
| case api::notifications::TemplateType::kList: |
| return message_center::NOTIFICATION_TYPE_MULTIPLE; |
| case api::notifications::TemplateType::kProgress: |
| return message_center::NOTIFICATION_TYPE_PROGRESS; |
| default: |
| // Gracefully handle newer application code that is running on an older |
| // runtime that doesn't recognize the requested template. |
| return message_center::NOTIFICATION_TYPE_SIMPLE; |
| } |
| } |
| |
| NotificationsCreateFunction::NotificationsCreateFunction() = default; |
| |
| NotificationsCreateFunction::~NotificationsCreateFunction() = default; |
| |
| ExtensionFunction::ResponseAction |
| NotificationsCreateFunction::RunNotificationsApi() { |
| params_ = api::notifications::Create::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params_); |
| |
| std::string notification_id; |
| if (params_->notification_id && !params_->notification_id->empty()) { |
| // If the caller provided a notificationId, use that. |
| notification_id = *params_->notification_id; |
| } else { |
| // Otherwise, use a randomly created GUID. In case that |
| // Uuid::GenerateRandomV4().AsLowercaseString returns the empty string, |
| // simply generate a random string. |
| notification_id = base::Uuid::GenerateRandomV4().AsLowercaseString(); |
| if (notification_id.empty()) |
| notification_id = base::RandBytesAsString(16); |
| } |
| |
| // TODO(dewittj): Add more human-readable error strings if this fails. |
| std::string error; |
| if (!CreateNotification(notification_id, ¶ms_->options, &error)) { |
| return RespondNow(ErrorWithArgumentsDoNotUse( |
| api::notifications::Create::Results::Create(notification_id), error)); |
| } |
| |
| return RespondNow(WithArguments(notification_id)); |
| } |
| |
| NotificationsUpdateFunction::NotificationsUpdateFunction() = default; |
| |
| NotificationsUpdateFunction::~NotificationsUpdateFunction() = default; |
| |
| ExtensionFunction::ResponseAction |
| NotificationsUpdateFunction::RunNotificationsApi() { |
| params_ = api::notifications::Update::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params_); |
| |
| // We are in update. If the ID doesn't exist, succeed but call the callback |
| // with "false". |
| const message_center::Notification* matched_notification = |
| GetDisplayHelper()->GetByNotificationId( |
| CreateScopedIdentifier(extension_->id(), params_->notification_id)); |
| |
| if (!matched_notification) { |
| return RespondNow(WithArguments(false)); |
| } |
| |
| // Copy the existing notification to get a writable version of it. |
| message_center::Notification notification = *matched_notification; |
| |
| // If we have trouble updating the notification (could be improper use of API |
| // or some other reason), mark the function as failed, calling the callback |
| // with false. |
| // TODO(dewittj): Add more human-readable error strings if this fails. |
| std::string error; |
| bool could_update_notification = UpdateNotification( |
| params_->notification_id, ¶ms_->options, ¬ification, &error); |
| if (!could_update_notification) { |
| return RespondNow(ErrorWithArgumentsDoNotUse( |
| api::notifications::Update::Results::Create(false), error)); |
| } |
| |
| // No trouble, created the notification, send true to the callback and |
| // succeed. |
| return RespondNow(WithArguments(true)); |
| } |
| |
| NotificationsClearFunction::NotificationsClearFunction() = default; |
| |
| NotificationsClearFunction::~NotificationsClearFunction() = default; |
| |
| ExtensionFunction::ResponseAction |
| NotificationsClearFunction::RunNotificationsApi() { |
| params_ = api::notifications::Clear::Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params_); |
| |
| bool cancel_result = GetDisplayHelper()->Close( |
| CreateScopedIdentifier(extension_->id(), params_->notification_id)); |
| |
| return RespondNow(WithArguments(cancel_result)); |
| } |
| |
| NotificationsGetAllFunction::NotificationsGetAllFunction() = default; |
| |
| NotificationsGetAllFunction::~NotificationsGetAllFunction() = default; |
| |
| ExtensionFunction::ResponseAction |
| NotificationsGetAllFunction::RunNotificationsApi() { |
| std::set<std::string> notification_ids = |
| GetDisplayHelper()->GetNotificationIdsForExtension(extension_->url()); |
| |
| base::Value::Dict result; |
| |
| for (const auto& entry : notification_ids) { |
| result.Set(StripScopeFromIdentifier(extension_->id(), entry), true); |
| } |
| |
| return RespondNow(WithArguments(std::move(result))); |
| } |
| |
| NotificationsGetPermissionLevelFunction:: |
| NotificationsGetPermissionLevelFunction() = default; |
| |
| NotificationsGetPermissionLevelFunction:: |
| ~NotificationsGetPermissionLevelFunction() = default; |
| |
| bool NotificationsGetPermissionLevelFunction::CanRunWhileDisabled() const { |
| return true; |
| } |
| |
| ExtensionFunction::ResponseAction |
| NotificationsGetPermissionLevelFunction::RunNotificationsApi() { |
| api::notifications::PermissionLevel result = |
| AreExtensionNotificationsAllowed() |
| ? api::notifications::PermissionLevel::kGranted |
| : api::notifications::PermissionLevel::kDenied; |
| |
| return RespondNow(WithArguments(api::notifications::ToString(result))); |
| } |
| |
| } // namespace extensions |