| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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/notifications/notification_conversion_helper.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <stdint.h> | |
| 9 | |
| 10 #include <memory> | |
| 11 #include <string> | |
| 12 #include <utility> | |
| 13 #include <vector> | |
| 14 | |
| 15 #include "base/logging.h" | |
| 16 #include "base/strings/utf_string_conversions.h" | |
| 17 #include "chrome/common/extensions/api/notification_provider.h" | |
| 18 #include "chrome/common/extensions/api/notifications/notification_style.h" | |
| 19 #include "ui/gfx/image/image_skia.h" | |
| 20 #include "ui/gfx/image/image_skia_rep.h" | |
| 21 #include "ui/gfx/skia_util.h" | |
| 22 | |
| 23 void NotificationConversionHelper::NotificationToNotificationOptions( | |
| 24 const Notification& notification, | |
| 25 extensions::api::notifications::NotificationOptions* options) { | |
| 26 // Extract required fields: type, title, message, and icon. | |
| 27 std::string type = MapTypeToString(notification.type()); | |
| 28 options->type = extensions::api::notifications::ParseTemplateType(type); | |
| 29 | |
| 30 if (!notification.icon().IsEmpty()) { | |
| 31 std::unique_ptr<extensions::api::notifications::NotificationBitmap> icon( | |
| 32 new extensions::api::notifications::NotificationBitmap()); | |
| 33 GfxImageToNotificationBitmap(¬ification.icon(), icon.get()); | |
| 34 options->icon_bitmap = std::move(icon); | |
| 35 } | |
| 36 | |
| 37 options->title.reset( | |
| 38 new std::string(base::UTF16ToUTF8(notification.title()))); | |
| 39 options->message.reset( | |
| 40 new std::string(base::UTF16ToUTF8(notification.message()))); | |
| 41 | |
| 42 // Handle optional data provided. | |
| 43 const message_center::RichNotificationData* rich_data = | |
| 44 ¬ification.rich_notification_data(); | |
| 45 | |
| 46 if (!rich_data->small_image.IsEmpty()) { | |
| 47 std::unique_ptr<extensions::api::notifications::NotificationBitmap> | |
| 48 icon_mask(new extensions::api::notifications::NotificationBitmap()); | |
| 49 GfxImageToNotificationBitmap(&rich_data->small_image, icon_mask.get()); | |
| 50 options->app_icon_mask_bitmap = std::move(icon_mask); | |
| 51 } | |
| 52 | |
| 53 options->priority.reset(new int(rich_data->priority)); | |
| 54 | |
| 55 options->is_clickable.reset(new bool(rich_data->clickable)); | |
| 56 | |
| 57 options->event_time.reset(new double(rich_data->timestamp.ToDoubleT())); | |
| 58 | |
| 59 if (!rich_data->context_message.empty()) | |
| 60 options->context_message.reset( | |
| 61 new std::string(base::UTF16ToUTF8(rich_data->context_message))); | |
| 62 | |
| 63 if (!rich_data->buttons.empty()) { | |
| 64 options->buttons.reset( | |
| 65 new std::vector<extensions::api::notifications::NotificationButton>()); | |
| 66 for (const message_center::ButtonInfo& button_info : rich_data->buttons) { | |
| 67 extensions::api::notifications::NotificationButton button; | |
| 68 button.title = base::UTF16ToUTF8(button_info.title); | |
| 69 | |
| 70 if (!button_info.icon.IsEmpty()) { | |
| 71 std::unique_ptr<extensions::api::notifications::NotificationBitmap> | |
| 72 icon(new extensions::api::notifications::NotificationBitmap()); | |
| 73 GfxImageToNotificationBitmap(&button_info.icon, icon.get()); | |
| 74 button.icon_bitmap = std::move(icon); | |
| 75 } | |
| 76 options->buttons->push_back(std::move(button)); | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 // Only image type notifications should have images. | |
| 81 if (type == "image" && !rich_data->image.IsEmpty()) { | |
| 82 std::unique_ptr<extensions::api::notifications::NotificationBitmap> image( | |
| 83 new extensions::api::notifications::NotificationBitmap()); | |
| 84 GfxImageToNotificationBitmap(¬ification.image(), image.get()); | |
| 85 options->image_bitmap = std::move(image); | |
| 86 } else if (type != "image" && !rich_data->image.IsEmpty()) { | |
| 87 DVLOG(1) << "Only image type notifications should have images."; | |
| 88 } | |
| 89 | |
| 90 // Only progress type notifications should have progress bars. | |
| 91 if (type == "progress") | |
| 92 options->progress.reset(new int(rich_data->progress)); | |
| 93 else if (rich_data->progress != 0) | |
| 94 DVLOG(1) << "Only progress type notifications should have progress."; | |
| 95 | |
| 96 // Only list type notifications should have lists. | |
| 97 if (type == "list" && !rich_data->items.empty()) { | |
| 98 options->items.reset( | |
| 99 new std::vector<extensions::api::notifications::NotificationItem>()); | |
| 100 for (const message_center::NotificationItem& item : rich_data->items) { | |
| 101 extensions::api::notifications::NotificationItem api_item; | |
| 102 api_item.title = base::UTF16ToUTF8(item.title); | |
| 103 api_item.message = base::UTF16ToUTF8(item.message); | |
| 104 options->items->push_back(std::move(api_item)); | |
| 105 } | |
| 106 } else if (type != "list" && !rich_data->items.empty()) { | |
| 107 DVLOG(1) << "Only list type notifications should have lists."; | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 void NotificationConversionHelper::GfxImageToNotificationBitmap( | |
| 112 const gfx::Image* gfx_image, | |
| 113 extensions::api::notifications::NotificationBitmap* notification_bitmap) { | |
| 114 SkBitmap sk_bitmap = gfx_image->AsBitmap(); | |
| 115 sk_bitmap.lockPixels(); | |
| 116 | |
| 117 notification_bitmap->width = sk_bitmap.width(); | |
| 118 notification_bitmap->height = sk_bitmap.height(); | |
| 119 int pixel_count = sk_bitmap.width() * sk_bitmap.height(); | |
| 120 const int BYTES_PER_PIXEL = 4; | |
| 121 | |
| 122 uint32_t* bitmap_pixels = sk_bitmap.getAddr32(0, 0); | |
| 123 const unsigned char* bitmap = | |
| 124 reinterpret_cast<const unsigned char*>(bitmap_pixels); | |
| 125 std::unique_ptr<std::vector<char>> rgba_bitmap_data( | |
| 126 new std::vector<char>(pixel_count * BYTES_PER_PIXEL)); | |
| 127 | |
| 128 gfx::ConvertSkiaToRGBA(bitmap, pixel_count, reinterpret_cast<unsigned char*>( | |
| 129 rgba_bitmap_data->data())); | |
| 130 sk_bitmap.unlockPixels(); | |
| 131 | |
| 132 notification_bitmap->data = std::move(rgba_bitmap_data); | |
| 133 return; | |
| 134 } | |
| 135 | |
| 136 bool NotificationConversionHelper::NotificationBitmapToGfxImage( | |
| 137 float max_scale, | |
| 138 const gfx::Size& target_size_dips, | |
| 139 const extensions::api::notifications::NotificationBitmap& | |
| 140 notification_bitmap, | |
| 141 gfx::Image* return_image) { | |
| 142 const int max_device_pixel_width = target_size_dips.width() * max_scale; | |
| 143 const int max_device_pixel_height = target_size_dips.height() * max_scale; | |
| 144 | |
| 145 const int BYTES_PER_PIXEL = 4; | |
| 146 | |
| 147 const int width = notification_bitmap.width; | |
| 148 const int height = notification_bitmap.height; | |
| 149 | |
| 150 if (width < 0 || height < 0 || width > max_device_pixel_width || | |
| 151 height > max_device_pixel_height) | |
| 152 return false; | |
| 153 | |
| 154 // Ensure we have rgba data. | |
| 155 std::vector<char>* rgba_data = notification_bitmap.data.get(); | |
| 156 if (!rgba_data) | |
| 157 return false; | |
| 158 | |
| 159 const size_t rgba_data_length = rgba_data->size(); | |
| 160 const size_t rgba_area = width * height; | |
| 161 | |
| 162 if (rgba_data_length != rgba_area * BYTES_PER_PIXEL) | |
| 163 return false; | |
| 164 | |
| 165 SkBitmap bitmap; | |
| 166 // Allocate the actual backing store with the sanitized dimensions. | |
| 167 if (!bitmap.tryAllocN32Pixels(width, height)) | |
| 168 return false; | |
| 169 | |
| 170 // Ensure that our bitmap and our data now refer to the same number of pixels. | |
| 171 if (rgba_data_length != bitmap.getSafeSize()) | |
| 172 return false; | |
| 173 | |
| 174 uint32_t* pixels = bitmap.getAddr32(0, 0); | |
| 175 const char* c_rgba_data = rgba_data->data(); | |
| 176 | |
| 177 for (size_t t = 0; t < rgba_area; ++t) { | |
| 178 // |c_rgba_data| is RGBA, pixels is ARGB. | |
| 179 size_t rgba_index = t * BYTES_PER_PIXEL; | |
| 180 pixels[t] = | |
| 181 SkPreMultiplyColor(((c_rgba_data[rgba_index + 3] & 0xFF) << 24) | | |
| 182 ((c_rgba_data[rgba_index + 0] & 0xFF) << 16) | | |
| 183 ((c_rgba_data[rgba_index + 1] & 0xFF) << 8) | | |
| 184 ((c_rgba_data[rgba_index + 2] & 0xFF) << 0)); | |
| 185 } | |
| 186 | |
| 187 // TODO(dewittj): Handle HiDPI images with more than one scale factor | |
| 188 // representation. | |
| 189 gfx::ImageSkia skia(gfx::ImageSkiaRep(bitmap, 1.0f)); | |
| 190 *return_image = gfx::Image(skia); | |
| 191 return true; | |
| 192 } | |
| 193 | |
| 194 std::string NotificationConversionHelper::MapTypeToString( | |
| 195 message_center::NotificationType type) { | |
| 196 switch (type) { | |
| 197 case message_center::NOTIFICATION_TYPE_BASE_FORMAT: | |
| 198 return "basic"; | |
| 199 case message_center::NOTIFICATION_TYPE_IMAGE: | |
| 200 return "image"; | |
| 201 case message_center::NOTIFICATION_TYPE_MULTIPLE: | |
| 202 return "list"; | |
| 203 case message_center::NOTIFICATION_TYPE_PROGRESS: | |
| 204 return "progress"; | |
| 205 case message_center::NOTIFICATION_TYPE_CUSTOM: | |
| 206 return "custom"; | |
| 207 default: | |
| 208 NOTREACHED(); | |
| 209 return ""; | |
| 210 } | |
| 211 } | |
| OLD | NEW |