Home | History | Annotate | Download | only in notifications
      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).
     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.
    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.
    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:
    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 
    155   virtual void Display() OVERRIDE { }
    156 
    157   virtual void Error() OVERRIDE {}
    158 
    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 
    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 
    175   virtual bool HasClickedListener() OVERRIDE {
    176     return EventRouter::Get(profile_)->HasEventListener(
    177         notifications::OnClicked::kEventName);
    178   }
    179 
    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 
    188   virtual std::string id() const OVERRIDE {
    189     return scoped_id_;
    190   }
    191 
    192   virtual int process_id() const OVERRIDE {
    193     return process_id_;
    194   }
    195 
    196   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 
    209   virtual void ReleaseRenderViewHost() OVERRIDE {
    210     api_function_ = NULL;
    211   }
    212 
    213  private:
    214   virtual ~NotificationsApiDelegate() {}
    215 
    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 
    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 
    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 
    250 NotificationsApiFunction::NotificationsApiFunction() {
    251 }
    252 
    253 NotificationsApiFunction::~NotificationsApiFunction() {
    254 }
    255 
    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 
    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 
    494 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 
    501 bool NotificationsApiFunction::IsNotificationsApiEnabled() const {
    502   return CanRunWhileDisabled() || AreExtensionNotificationsAllowed();
    503 }
    504 
    505 bool NotificationsApiFunction::CanRunWhileDisabled() const {
    506   return false;
    507 }
    508 
    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
    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 
    538 NotificationsCreateFunction::NotificationsCreateFunction() {
    539 }
    540 
    541 NotificationsCreateFunction::~NotificationsCreateFunction() {
    542 }
    543 
    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, &params_->options))
    565     return false;
    566 
    567   SendResponse(true);
    568 
    569   return true;
    570 }
    571 
    572 NotificationsUpdateFunction::NotificationsUpdateFunction() {
    573 }
    574 
    575 NotificationsUpdateFunction::~NotificationsUpdateFunction() {
    576 }
    577 
    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, &params_->options, &notification);
    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 
    612 NotificationsClearFunction::NotificationsClearFunction() {
    613 }
    614 
    615 NotificationsClearFunction::~NotificationsClearFunction() {
    616 }
    617 
    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 
    631 NotificationsGetAllFunction::NotificationsGetAllFunction() {}
    632 
    633 NotificationsGetAllFunction::~NotificationsGetAllFunction() {}
    634 
    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::
    657 NotificationsGetPermissionLevelFunction() {}
    658 
    659 NotificationsGetPermissionLevelFunction::
    660 ~NotificationsGetPermissionLevelFunction() {}
    661 
    662 bool NotificationsGetPermissionLevelFunction::CanRunWhileDisabled() const {
    663   return true;
    664 }
    665 
    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