Commit 80dd7ea7 authored by Finnur Thorarinsson's avatar Finnur Thorarinsson Committed by Commit Bot

Win10 Native Notifications: Support images.

Introduce a class that supports hanging on to temporary
files long enough for the OS to pick them up (and clean
them up after restart of Chrome if it fails the first
time around). Necessary because Windows doesn't
guarantee temp file deletion on shutdown/restart.

Bug: 734095
Change-Id: I2926dc33b489f150a75f97792266e1a31a4ea5a2
Reviewed-on: https://chromium-review.googlesource.com/708742
Commit-Queue: Finnur Thorarinsson <finnur@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#515958}
parent cb0a297b
......@@ -2799,6 +2799,8 @@ split_static_library("browser") {
"downgrade/user_data_downgrade.cc",
"downgrade/user_data_downgrade.h",
"first_run/upgrade_util.cc",
"notifications/notification_image_retainer.cc",
"notifications/notification_image_retainer.h",
"notifications/notification_template_builder.cc",
"notifications/notification_template_builder.h",
]
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/notifications/mock_notification_image_retainer.h"
#include "base/files/file_path.h"
#include "base/strings/string_number_conversions.h"
#include "ui/gfx/image/image.h"
base::FilePath MockNotificationImageRetainer::RegisterTemporaryImage(
const gfx::Image& image,
const std::string& profile_id,
const GURL& origin) {
base::string16 file = base::string16(L"c:\\temp\\img") +
base::IntToString16(counter_++) +
base::string16(L".tmp");
return base::FilePath(file);
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_NOTIFICATIONS_MOCK_NOTIFICATION_IMAGE_RETAINER_H_
#define CHROME_BROWSER_NOTIFICATIONS_MOCK_NOTIFICATION_IMAGE_RETAINER_H_
#include <string>
#include "base/macros.h"
#include "chrome/browser/notifications/notification_image_retainer.h"
class GURL;
namespace gfx {
class Image;
}
// A mock NotificationImageRetainer class for use with unit tests. Returns
// predictable paths to callers wanting to register temporary files.
class MockNotificationImageRetainer : public NotificationImageRetainer {
public:
MockNotificationImageRetainer() : NotificationImageRetainer(nullptr) {}
~MockNotificationImageRetainer() override = default;
// NotificationImageRetainer implementation:
base::FilePath RegisterTemporaryImage(const gfx::Image& image,
const std::string& profile_id,
const GURL& origin) override;
private:
int counter_ = 0;
DISALLOW_COPY_AND_ASSIGN(MockNotificationImageRetainer);
};
#endif // CHROME_BROWSER_NOTIFICATIONS_MOCK_NOTIFICATION_IMAGE_RETAINER_H_
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/notifications/notification_image_retainer.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/hash.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/common/chrome_paths.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
using base::FilePath;
namespace {
constexpr base::FilePath::CharType kImageRoot[] =
FILE_PATH_LITERAL("Notification Resources");
// How long to keep the temp files before deleting them. The formula for picking
// the delay is t * (n + 1), where t is the default on-screen display time for
// an Action Center notification (6 seconds) and n is the number of
// notifications that can be shown on-screen at once (1).
constexpr base::TimeDelta kDeletionDelay = base::TimeDelta::FromSeconds(12);
// Writes |data| to a new temporary file and returns the path to the new file,
// or an empty path if the function fails.
FilePath WriteDataToTmpFile(const FilePath& file_path,
const base::string16& subdirectory,
const scoped_refptr<base::RefCountedMemory>& data) {
int data_len = data->size();
if (data_len == 0)
return FilePath();
FilePath new_temp = file_path.Append(subdirectory);
if (!base::CreateDirectoryAndGetError(new_temp, nullptr))
return FilePath();
FilePath temp_file;
if (!base::CreateTemporaryFileInDir(new_temp, &temp_file))
return FilePath();
if (base::WriteFile(temp_file, data->front_as<char>(), data_len) != data_len)
return FilePath();
return temp_file;
}
} // namespace
bool NotificationImageRetainer::override_file_destruction_ = false;
NotificationImageRetainer::NotificationImageRetainer(
scoped_refptr<base::SequencedTaskRunner> task_runner)
: task_runner_(task_runner) {}
NotificationImageRetainer::~NotificationImageRetainer() {
if (!image_directory_.empty())
base::DeleteFile(image_directory_, true);
}
FilePath NotificationImageRetainer::RegisterTemporaryImage(
const gfx::Image& image,
const std::string& profile_id,
const GURL& origin) {
base::AssertBlockingAllowed();
if (!initialized_) {
image_directory_ = DetermineImageDirectory();
// Delete the old image directory.
DeleteFile(image_directory_, /*recursive=*/true);
// Recreate the image directory.
if (!base::CreateDirectoryAndGetError(image_directory_, nullptr))
return FilePath();
initialized_ = true;
}
// To minimize the risk of collisions, separate each request by subdirectory
// generated from hashes of the profile and the origin. Each file within the
// subdirectory will also be given a unique filename.
base::string16 directory = base::UintToString16(base::Hash(
base::UTF8ToUTF16(profile_id) + base::UTF8ToUTF16(origin.spec())));
FilePath temp_file =
WriteDataToTmpFile(image_directory_, directory, image.As1xPNGBytes());
// Add a future task to try to delete the file. It is OK to fail, the file
// will get deleted later.
base::TimeDelta delay = override_file_destruction_
? base::TimeDelta::FromSeconds(0)
: kDeletionDelay;
task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(base::IgnoreResult(&base::DeleteFile), temp_file,
/*recursive=*/true),
delay);
return temp_file;
}
// static
void NotificationImageRetainer::OverrideTempFileLifespanForTesting(
bool override) {
override_file_destruction_ = override;
}
FilePath NotificationImageRetainer::DetermineImageDirectory() {
FilePath data_dir;
bool success = base::PathService::Get(chrome::DIR_USER_DATA, &data_dir);
DCHECK(success);
return data_dir.Append(kImageRoot);
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_IMAGE_RETAINER_H_
#define CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_IMAGE_RETAINER_H_
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
class GURL;
namespace gfx {
class Image;
} // namespace gfx
// The purpose of this class is to take data from memory, store it to disk as
// temp files and keep them alive long enough to hand over to external entities,
// such as the Action Center on Windows. The Action Center will read the files
// at some point in the future, which is why we can't do:
// [write file] -> [show notification] -> [delete file].
//
// Also, on Windows, temp file deletion is not guaranteed and, since the images
// can potentially be large, this presents a problem because Chrome might then
// be leaving chunks of dead bits lying around on user’s computers during
// unclean shutdowns.
class NotificationImageRetainer {
public:
explicit NotificationImageRetainer(
scoped_refptr<base::SequencedTaskRunner> task_runner);
virtual ~NotificationImageRetainer();
// Stores an |image| from a particular profile (|profile_id|) and |origin| on
// disk in a temporary (short-lived) file. Returns the path to the file
// created, which will be valid for a few seconds only. It will be deleted
// either after a short timeout or after a restart of Chrome (the next time
// this function is called). The function returns an empty FilePath if file
// creation fails.
virtual base::FilePath RegisterTemporaryImage(const gfx::Image& image,
const std::string& profile_id,
const GURL& origin);
// Sets whether to override temp file destruction time. If set to |true|, the
// temp files will be scheduled for deletion right after their creation. If
// |false|, the standard deletion delay will apply.
static void OverrideTempFileLifespanForTesting(bool override);
private:
// Returns the temporary directory within the user data directory. The
// regular temporary directory is not used to minimize the risk of files
// getting deleted by accident. It is also not profile-bound because the
// notification bridge handles images for multiple profiles and the separation
// is handled by RegisterTemporaryImage.
base::FilePath DetermineImageDirectory();
// The path to where to store the temporary files.
base::FilePath image_directory_;
// Whether this class has initialized.
bool initialized_ = false;
// Whether to override the time to wait before deleting the temp files. For
// testing use only.
static bool override_file_destruction_;
// The task runner to use.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(NotificationImageRetainer);
};
#endif // CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_IMAGE_RETAINER_H_
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/notifications/notification_image_retainer.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
namespace {
const char kProfileId1[] = "Default";
const char kProfileId2[] = "User";
} // namespace
class NotificationImageRetainerTest : public ::testing::Test {
public:
NotificationImageRetainerTest() = default;
~NotificationImageRetainerTest() override = default;
void SetUp() override {
NotificationImageRetainer::OverrideTempFileLifespanForTesting(true);
}
void TearDown() override {
NotificationImageRetainer::OverrideTempFileLifespanForTesting(false);
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::string16 CalculateHash(const std::string& profile_id,
const GURL& origin) {
return base::UintToString16(base::Hash(base::UTF8ToUTF16(profile_id) +
base::UTF8ToUTF16(origin.spec())));
}
private:
DISALLOW_COPY_AND_ASSIGN(NotificationImageRetainerTest);
};
TEST_F(NotificationImageRetainerTest, FileCreation) {
auto image_retainer = std::make_unique<NotificationImageRetainer>(
scoped_task_environment_.GetMainThreadTaskRunner());
SkBitmap icon;
icon.allocN32Pixels(64, 64);
icon.eraseARGB(255, 100, 150, 200);
gfx::Image image = gfx::Image::CreateFrom1xBitmap(icon);
GURL origin1("https://www.google.com");
GURL origin2("https://www.chromium.org");
// Expecting separate directories per profile and origin.
base::string16 dir_profile1_origin1 = CalculateHash(kProfileId1, origin1);
base::string16 dir_profile1_origin2 = CalculateHash(kProfileId1, origin2);
base::string16 dir_profile2_origin1 = CalculateHash(kProfileId2, origin1);
base::string16 dir_profile2_origin2 = CalculateHash(kProfileId2, origin2);
base::FilePath path1 =
image_retainer->RegisterTemporaryImage(image, kProfileId1, origin1);
ASSERT_TRUE(base::PathExists(path1));
ASSERT_TRUE(path1.value().find(dir_profile1_origin1) != std::string::npos)
<< path1.value().c_str();
std::vector<base::FilePath::StringType> components;
path1.GetComponents(&components);
base::FilePath image_dir;
for (size_t i = 0; i < components.size() - 2; ++i)
image_dir = image_dir.Append(components[i]);
base::FilePath path2 =
image_retainer->RegisterTemporaryImage(image, kProfileId1, origin2);
ASSERT_TRUE(base::PathExists(path2));
ASSERT_TRUE(path2.value().find(dir_profile1_origin2) != std::string::npos)
<< path2.value().c_str();
base::FilePath path3 =
image_retainer->RegisterTemporaryImage(image, kProfileId2, origin1);
ASSERT_TRUE(base::PathExists(path3));
ASSERT_TRUE(path3.value().find(dir_profile2_origin1) != std::string::npos)
<< path3.value().c_str();
base::FilePath path4 =
image_retainer->RegisterTemporaryImage(image, kProfileId2, origin2);
ASSERT_TRUE(base::PathExists(path4));
ASSERT_TRUE(path4.value().find(dir_profile2_origin2) != std::string::npos)
<< path4.value().c_str();
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(base::PathExists(path1));
ASSERT_FALSE(base::PathExists(path2));
ASSERT_FALSE(base::PathExists(path3));
ASSERT_FALSE(base::PathExists(path4));
image_retainer.reset(nullptr);
ASSERT_FALSE(base::PathExists(image_dir));
}
......@@ -117,13 +117,6 @@ base::string16 CreateNotificationTitle(
return title;
}
gfx::Image DeepCopyImage(const gfx::Image& image) {
if (image.IsEmpty())
return gfx::Image();
std::unique_ptr<gfx::ImageSkia> image_skia(image.CopyImageSkia());
return gfx::Image(*image_skia);
}
void EscapeUnsafeCharacters(std::string* message) {
// Canonical's notification development guidelines recommends only
// escaping the '&', '<', and '>' characters:
......@@ -291,20 +284,11 @@ class NotificationPlatformBridgeLinuxImpl
const message_center::Notification& notification,
std::unique_ptr<NotificationCommon::Metadata> metadata) override {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Notifications contain gfx::Image's which have reference counts
// that are not thread safe. Because of this, we duplicate the
// notification and its images. Wrap the notification in a
// unique_ptr to transfer ownership of the notification (and the
// non-thread-safe reference counts) to the task runner thread.
auto notification_copy =
std::make_unique<message_center::Notification>(notification);
notification_copy->set_icon(DeepCopyImage(notification_copy->icon()));
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());
// Make a deep copy of the notification as its resources cannot safely
// be passed between threads.
auto notification_copy = message_center::Notification::DeepCopy(
notification, body_images_supported_.value(),
/*include_small_image=*/false, /*include_icon_images=*/false);
PostTaskToTaskRunnerThread(base::BindOnce(
&NotificationPlatformBridgeLinuxImpl::DisplayOnTaskRunner, this,
......
......@@ -7,13 +7,12 @@
#include <windows.ui.notifications.h>
#include <string>
#include <unordered_map>
#include "base/macros.h"
#include "base/sequenced_task_runner.h"
#include "chrome/browser/notifications/notification_platform_bridge.h"
#include "url/gurl.h"
struct NotificationData;
class NotificationPlatformBridgeWinImpl;
class NotificationTemplateBuilder;
// Implementation of the NotificationPlatformBridge for Windows 10 Anniversary
......@@ -40,39 +39,18 @@ class NotificationPlatformBridgeWin : public NotificationPlatformBridge {
private:
friend class NotificationPlatformBridgeWinTest;
// Callbacks for toast events from Windows.
HRESULT OnActivated(
ABI::Windows::UI::Notifications::IToastNotification* notification,
IInspectable* inspectable);
HRESULT OnDismissed(
ABI::Windows::UI::Notifications::IToastNotification* notification,
ABI::Windows::UI::Notifications::IToastDismissedEventArgs* args);
// Obtain an IToastNotification interface from a given XML (provided by the
// NotificationTemplateBuilder).
HRESULT GetToastNotification(
// NotificationTemplateBuilder). For testing use only.
HRESULT GetToastNotificationForTesting(
const message_center::Notification& notification,
const NotificationTemplateBuilder& notification_template_builder,
ABI::Windows::UI::Notifications::IToastNotification** toast_notification);
// Returns a notification with properties |notification_id|, |profile_id|,
// |origin_url| and |incognito| if found in notifications_. Returns nullptr if
// not found.
NotificationData* FindNotificationData(const std::string& notification_id,
const std::string& profile_id,
const GURL& origin_url,
bool incognito);
void PostTaskToTaskRunnerThread(base::OnceClosure closure) const;
// Whether the required functions from combase.dll have been loaded.
bool com_functions_initialized_;
scoped_refptr<NotificationPlatformBridgeWinImpl> impl_;
// Stores the set of Notifications in a session.
// A std::set<std::unique_ptr<T>> doesn't work well because e.g.,
// std::set::erase(T) would require a std::unique_ptr<T> argument, so the data
// would get double-destructed.
template <typename T>
using UnorderedUniqueSet = std::unordered_map<T*, std::unique_ptr<T>>;
UnorderedUniqueSet<NotificationData> notifications_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(NotificationPlatformBridgeWin);
};
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/notifications/notification_platform_bridge_win.h"
#include <windows.ui.notifications.h>
#include <wrl/client.h>
#include <wrl/wrappers/corewrappers.h>
#include <memory>
......@@ -11,9 +12,11 @@
#include "base/hash.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_hstring.h"
#include "base/win/windows_version.h"
#include "chrome/browser/notifications/mock_notification_image_retainer.h"
#include "chrome/browser/notifications/notification_template_builder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/message_center/notification.h"
......@@ -28,6 +31,7 @@ namespace {
const char kOrigin[] = "https://www.google.com/";
const char kNotificationId[] = "id";
const char kProfileId[] = "Default";
} // namespace
......@@ -46,14 +50,18 @@ class NotificationPlatformBridgeWinTest : public testing::Test {
L"message", gfx::Image(), L"display_source", origin,
message_center::NotifierId(origin),
message_center::RichNotificationData(), nullptr /* delegate */);
MockNotificationImageRetainer image_retainer;
std::unique_ptr<NotificationTemplateBuilder> builder =
NotificationTemplateBuilder::Build(*notification);
NotificationTemplateBuilder::Build(&image_retainer, kProfileId,
*notification);
return notification_platform_bridge_win_->GetToastNotification(
return notification_platform_bridge_win_->GetToastNotificationForTesting(
*notification, *builder, toast);
}
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<NotificationPlatformBridgeWin>
notification_platform_bridge_win_;
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/notifications/notification_template_builder.h"
#include "base/files/file_path.h"
#include "base/i18n/time_formatting.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
......@@ -11,11 +12,13 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/notifications/notification_image_retainer.h"
#include "chrome/grit/chromium_strings.h"
#include "components/url_formatter/elide_url.h"
#include "third_party/libxml/chromium/libxml_utils.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/notification.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace {
......@@ -33,22 +36,29 @@ const char kButtonIndex[] = "buttonIndex=";
const char kContent[] = "content";
const char kContextMenu[] = "contextMenu";
const char kForeground[] = "foreground";
const char kHero[] = "hero";
const char kHintCrop[] = "hint-crop";
const char kHintCropNone[] = "none";
const char kImageElement[] = "image";
const char kImageUri[] = "imageUri";
const char kInputElement[] = "input";
const char kInputId[] = "id";
const char kInputType[] = "type";
const char kNotificationSettings[] = "notificationSettings";
const char kPlaceholderContent[] = "placeHolderContent";
const char kPlacement[] = "placement";
const char kPlacementAppLogoOverride[] = "appLogoOverride";
const char kReminder[] = "reminder";
const char kScenario[] = "scenario";
const char kSilent[] = "silent";
const char kSrc[] = "src";
const char kText[] = "text";
const char kTrue[] = "true";
const char kUserResponse[] = "userResponse";
const char kTextElement[] = "text";
const char kToastElement[] = "toast";
const char kToastElementDisplayTimestamp[] = "displayTimestamp";
const char kToastElementLaunchAttribute[] = "launch";
const char kTrue[] = "true";
const char kUserResponse[] = "userResponse";
const char kVisualElement[] = "visual";
// Name of the template used for default Chrome notifications.
......@@ -64,9 +74,11 @@ const char* NotificationTemplateBuilder::context_menu_label_override_ = nullptr;
// static
std::unique_ptr<NotificationTemplateBuilder> NotificationTemplateBuilder::Build(
NotificationImageRetainer* notification_image_retainer,
const std::string& profile_id,
const message_center::Notification& notification) {
std::unique_ptr<NotificationTemplateBuilder> builder =
base::WrapUnique(new NotificationTemplateBuilder);
std::unique_ptr<NotificationTemplateBuilder> builder = base::WrapUnique(
new NotificationTemplateBuilder(notification_image_retainer, profile_id));
builder->StartToastElement(notification.id(), notification);
builder->StartVisualElement();
......@@ -74,20 +86,25 @@ std::unique_ptr<NotificationTemplateBuilder> NotificationTemplateBuilder::Build(
builder->StartBindingElement(kDefaultTemplate);
// Content for the toast template.
builder->WriteTextElement("1", base::UTF16ToUTF8(notification.title()),
builder->WriteTextElement(base::UTF16ToUTF8(notification.title()),
TextType::NORMAL);
builder->WriteTextElement("2", base::UTF16ToUTF8(notification.message()),
builder->WriteTextElement(base::UTF16ToUTF8(notification.message()),
TextType::NORMAL);
builder->WriteTextElement("3",
builder->FormatOrigin(notification.origin_url()),
builder->WriteTextElement(builder->FormatOrigin(notification.origin_url()),
TextType::ATTRIBUTION);
if (!notification.icon().IsEmpty())
builder->WriteIconElement(notification);
if (!notification.image().IsEmpty())
builder->WriteLargeImageElement(notification);
builder->EndBindingElement();
builder->EndVisualElement();
builder->StartActionsElement();
if (!notification.buttons().empty())
builder->AddActions(notification.buttons());
builder->AddActions(notification);
builder->AddContextMenu();
builder->EndActionsElement();
......@@ -99,8 +116,12 @@ std::unique_ptr<NotificationTemplateBuilder> NotificationTemplateBuilder::Build(
return builder;
}
NotificationTemplateBuilder::NotificationTemplateBuilder()
: xml_writer_(std::make_unique<XmlWriter>()) {
NotificationTemplateBuilder::NotificationTemplateBuilder(
NotificationImageRetainer* notification_image_retainer,
const std::string& profile_id)
: xml_writer_(std::make_unique<XmlWriter>()),
image_retainer_(notification_image_retainer),
profile_id_(profile_id) {
xml_writer_->StartWriting();
}
......@@ -171,8 +192,7 @@ void NotificationTemplateBuilder::EndBindingElement() {
xml_writer_->EndElement();
}
void NotificationTemplateBuilder::WriteTextElement(const std::string& id,
const std::string& content,
void NotificationTemplateBuilder::WriteTextElement(const std::string& content,
TextType text_type) {
xml_writer_->StartElement(kTextElement);
if (text_type == TextType::ATTRIBUTION)
......@@ -181,8 +201,39 @@ void NotificationTemplateBuilder::WriteTextElement(const std::string& id,
xml_writer_->EndElement();
}
void NotificationTemplateBuilder::WriteIconElement(
const message_center::Notification& notification) {
WriteImageElement(notification.icon(), notification.origin_url(),
kPlacementAppLogoOverride, kHintCropNone);
}
void NotificationTemplateBuilder::WriteLargeImageElement(
const message_center::Notification& notification) {
WriteImageElement(notification.image(), notification.origin_url(), kHero,
std::string());
}
void NotificationTemplateBuilder::WriteImageElement(
const gfx::Image& image,
const GURL& origin,
const std::string& placement,
const std::string& hint_crop) {
base::FilePath path =
image_retainer_->RegisterTemporaryImage(image, profile_id_, origin);
if (!path.empty()) {
xml_writer_->StartElement(kImageElement);
xml_writer_->AddAttribute(kPlacement, placement);
xml_writer_->AddAttribute(kSrc, base::UTF16ToUTF8(path.value()));
if (!hint_crop.empty())
xml_writer_->AddAttribute(kHintCrop, hint_crop);
xml_writer_->EndElement();
}
}
void NotificationTemplateBuilder::AddActions(
const std::vector<message_center::ButtonInfo>& buttons) {
const message_center::Notification& notification) {
const std::vector<message_center::ButtonInfo>& buttons =
notification.buttons();
bool inline_reply = false;
std::string placeholder;
for (const auto& button : buttons) {