| Index: chrome/browser/notifications/notification_platform_bridge_linux.cc
|
| diff --git a/chrome/browser/notifications/notification_platform_bridge_linux.cc b/chrome/browser/notifications/notification_platform_bridge_linux.cc
|
| index ac81ee8b194849ca7e19d8931cf01f3f8c1af069..260d97802b3f5444150a6f842a10df7d2133b88d 100644
|
| --- a/chrome/browser/notifications/notification_platform_bridge_linux.cc
|
| +++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc
|
| @@ -7,6 +7,7 @@
|
| #include <algorithm>
|
| #include <memory>
|
| #include <set>
|
| +#include <sstream>
|
| #include <unordered_map>
|
| #include <unordered_set>
|
| #include <utility>
|
| @@ -36,6 +37,8 @@
|
| #include "dbus/bus.h"
|
| #include "dbus/message.h"
|
| #include "dbus/object_proxy.h"
|
| +#include "net/base/escape.h"
|
| +#include "skia/ext/image_operations.h"
|
| #include "ui/base/l10n/l10n_util.h"
|
| #include "ui/gfx/image/image_skia.h"
|
|
|
| @@ -71,6 +74,10 @@ const char kCapabilitySound[] = "sound";
|
| const char kDefaultButtonId[] = "default";
|
| const char kSettingsButtonId[] = "settings";
|
|
|
| +// Max image size; specified in the FDO notification specification.
|
| +const int kMaxImageWidth = 200;
|
| +const int kMaxImageHeight = 100;
|
| +
|
| // The values in this enumeration correspond to those of the
|
| // Linux.NotificationPlatformBridge.InitializationStatus histogram, so
|
| // the ordering should not be changed. New error codes should be
|
| @@ -84,6 +91,10 @@ enum class ConnectionInitializationStatusCode {
|
| NUM_ITEMS
|
| };
|
|
|
| +int ClampInt(int value, int low, int hi) {
|
| + return std::max(std::min(value, hi), low);
|
| +}
|
| +
|
| base::string16 CreateNotificationTitle(const Notification& notification) {
|
| base::string16 title;
|
| if (notification.type() == message_center::NOTIFICATION_TYPE_PROGRESS) {
|
| @@ -121,6 +132,28 @@ int NotificationPriorityToFdoUrgency(int priority) {
|
| }
|
| }
|
|
|
| +// Constrain |image|'s size to |kMaxImageWidth|x|kMaxImageHeight|. If
|
| +// the image does not need to be resized, or the image is empty,
|
| +// returns |image| directly.
|
| +gfx::Image ResizeImageToFdoMaxSize(const gfx::Image& image) {
|
| + if (image.IsEmpty())
|
| + return image;
|
| + int width = image.Width();
|
| + int height = image.Height();
|
| + if (width <= kMaxImageWidth && height <= kMaxImageHeight) {
|
| + return image;
|
| + }
|
| + const SkBitmap* image_bitmap = image.ToSkBitmap();
|
| + double scale = std::min(static_cast<double>(kMaxImageWidth) / width,
|
| + static_cast<double>(kMaxImageHeight) / height);
|
| + width = ClampInt(scale * width, 1, kMaxImageWidth);
|
| + height = ClampInt(scale * height, 1, kMaxImageHeight);
|
| + return gfx::Image(
|
| + gfx::ImageSkia::CreateFrom1xBitmap(skia::ImageOperations::Resize(
|
| + *image_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, width,
|
| + height)));
|
| +}
|
| +
|
| // Runs once the profile has been loaded in order to perform a given
|
| // |operation| on a notification.
|
| void ProfileLoadedCallback(NotificationCommon::Operation operation,
|
| @@ -235,7 +268,9 @@ class NotificationPlatformBridgeLinuxImpl
|
| // non-thread-safe reference counts) to the task runner thread.
|
| auto notification_copy = base::MakeUnique<Notification>(notification);
|
| notification_copy->set_icon(DeepCopyImage(notification_copy->icon()));
|
| - notification_copy->set_image(gfx::Image());
|
| + notification_copy->set_image(body_images_supported_.value()
|
| + ? DeepCopyImage(notification_copy->image())
|
| + : gfx::Image());
|
| notification_copy->set_small_image(gfx::Image());
|
| for (size_t i = 0; i < notification_copy->buttons().size(); i++)
|
| notification_copy->SetButtonIcon(i, gfx::Image());
|
| @@ -337,6 +372,11 @@ class NotificationPlatformBridgeLinuxImpl
|
| CleanUp();
|
| }
|
|
|
| + void SetBodyImagesSupported(bool body_images_supported) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + body_images_supported_ = body_images_supported;
|
| + }
|
| +
|
| void PostTaskToUiThread(base::OnceClosure closure) const {
|
| DCHECK(task_runner_->RunsTasksOnCurrentThread());
|
| bool success = content::BrowserThread::PostTask(
|
| @@ -386,6 +426,9 @@ class NotificationPlatformBridgeLinuxImpl
|
| capabilities_.insert(capability);
|
| }
|
| RecordMetricsForCapabilities();
|
| + PostTaskToUiThread(base::BindOnce(
|
| + &NotificationPlatformBridgeLinuxImpl::SetBodyImagesSupported, this,
|
| + base::ContainsKey(capabilities_, kCapabilityBodyImages)));
|
|
|
| dbus::MethodCall get_server_information_call(kFreedesktopNotificationsName,
|
| kMethodGetServerInformation);
|
| @@ -472,32 +515,47 @@ class NotificationPlatformBridgeLinuxImpl
|
| writer.AppendString(
|
| base::UTF16ToUTF8(CreateNotificationTitle(*notification)));
|
|
|
| - std::string body;
|
| + std::ostringstream body;
|
| if (base::ContainsKey(capabilities_, kCapabilityBody)) {
|
| - body = base::UTF16ToUTF8(notification->message());
|
| const bool body_markup =
|
| base::ContainsKey(capabilities_, kCapabilityBodyMarkup);
|
| + std::string message = base::UTF16ToUTF8(notification->message());
|
| if (body_markup) {
|
| - base::ReplaceSubstringsAfterOffset(&body, 0, "&", "&");
|
| - base::ReplaceSubstringsAfterOffset(&body, 0, "<", "<");
|
| - base::ReplaceSubstringsAfterOffset(&body, 0, ">", ">");
|
| + base::ReplaceSubstringsAfterOffset(&message, 0, "&", "&");
|
| + base::ReplaceSubstringsAfterOffset(&message, 0, "<", "<");
|
| + base::ReplaceSubstringsAfterOffset(&message, 0, ">", ">");
|
| }
|
| + body << message;
|
| +
|
| if (notification->type() == message_center::NOTIFICATION_TYPE_MULTIPLE) {
|
| for (const auto& item : notification->items()) {
|
| - if (!body.empty())
|
| - body += "\n";
|
| + if (body.tellp())
|
| + body << "\n";
|
| const std::string title = base::UTF16ToUTF8(item.title);
|
| const std::string message = base::UTF16ToUTF8(item.message);
|
| // TODO(peter): Figure out the right way to internationalize
|
| // this for RTL languages.
|
| if (body_markup)
|
| - body += "<b>" + title + "</b> " + message;
|
| + body << "<b>" << title << "</b> " << message;
|
| else
|
| - body += title + " - " + message;
|
| + body << title << " - " << message;
|
| + }
|
| + } else if (notification->type() ==
|
| + message_center::NOTIFICATION_TYPE_IMAGE &&
|
| + base::ContainsKey(capabilities_, kCapabilityBodyImages)) {
|
| + std::unique_ptr<ResourceFile> image_file = WriteDataToTmpFile(
|
| + ResizeImageToFdoMaxSize(notification->image()).As1xPNGBytes());
|
| + if (image_file) {
|
| + if (body.tellp())
|
| + body << "\n";
|
| + body << "<img src=\""
|
| + << net::EscapePath(image_file->file_path().value())
|
| + << "\" alt=\"\"/>";
|
| + data->resource_files.push_back(std::move(image_file));
|
| }
|
| }
|
| }
|
| - writer.AppendString(body);
|
| + writer.AppendString(body.str());
|
|
|
| // Even-indexed elements in this vector are action IDs passed back to
|
| // us in OnActionInvoked(). Odd-indexed ones contain the button text.
|
| @@ -783,6 +841,10 @@ class NotificationPlatformBridgeLinuxImpl
|
| base::Optional<bool> connected_;
|
| std::vector<NotificationBridgeReadyCallback> on_connected_callbacks_;
|
|
|
| + // Notification servers very rarely have the 'body-images'
|
| + // capability, so try to avoid an image copy if possible.
|
| + base::Optional<bool> body_images_supported_;
|
| +
|
| //////////////////////////////////////////////////////////////////////////////
|
| // Members used only on the task runner thread.
|
|
|
|
|