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