| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/notifications/notification_platform_bridge_linux.h" | 5 #include "chrome/browser/notifications/notification_platform_bridge_linux.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/callback.h" | 10 #include "base/callback.h" |
| 11 #include "base/files/file_util.h" |
| 11 #include "base/i18n/number_formatting.h" | 12 #include "base/i18n/number_formatting.h" |
| 12 #include "base/logging.h" | 13 #include "base/logging.h" |
| 13 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 14 #include "base/memory/ref_counted.h" | 15 #include "base/memory/ref_counted.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 16 #include "chrome/browser/notifications/notification.h" | 17 #include "chrome/browser/notifications/notification.h" |
| 17 #include "chrome/browser/notifications/notification_test_util.h" | 18 #include "chrome/browser/notifications/notification_test_util.h" |
| 18 #include "content/public/test/test_browser_thread_bundle.h" | 19 #include "content/public/test/test_browser_thread_bundle.h" |
| 19 #include "content/public/test/test_utils.h" | 20 #include "content/public/test/test_utils.h" |
| 20 #include "dbus/mock_bus.h" | 21 #include "dbus/mock_bus.h" |
| 21 #include "dbus/mock_object_proxy.h" | 22 #include "dbus/mock_object_proxy.h" |
| 22 #include "testing/gmock/include/gmock/gmock.h" | 23 #include "testing/gmock/include/gmock/gmock.h" |
| 23 #include "testing/gtest/include/gtest/gtest.h" | 24 #include "testing/gtest/include/gtest/gtest.h" |
| 25 #include "third_party/re2/src/re2/re2.h" |
| 26 #include "ui/gfx/image/image_skia.h" |
| 24 | 27 |
| 25 using testing::_; | 28 using testing::_; |
| 26 using testing::Return; | 29 using testing::Return; |
| 27 using testing::StrictMock; | 30 using testing::StrictMock; |
| 28 | 31 |
| 29 namespace { | 32 namespace { |
| 30 | 33 |
| 31 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; | 34 const char kFreedesktopNotificationsName[] = "org.freedesktop.Notifications"; |
| 32 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; | 35 const char kFreedesktopNotificationsPath[] = "/org/freedesktop/Notifications"; |
| 33 | 36 |
| 34 class NotificationBuilder { | 37 class NotificationBuilder { |
| 35 public: | 38 public: |
| 36 explicit NotificationBuilder(const std::string& id) | 39 explicit NotificationBuilder(const std::string& id) |
| 37 : notification_(message_center::NOTIFICATION_TYPE_SIMPLE, | 40 : notification_(message_center::NOTIFICATION_TYPE_SIMPLE, |
| 38 base::string16(), | 41 base::string16(), |
| 39 base::string16(), | 42 base::string16(), |
| 40 gfx::Image(), | 43 gfx::Image(), |
| 41 message_center::NotifierId(GURL()), | 44 message_center::NotifierId(GURL()), |
| 42 base::string16(), | 45 base::string16(), |
| 43 GURL(), | 46 GURL(), |
| 44 id, | 47 id, |
| 45 message_center::RichNotificationData(), | 48 message_center::RichNotificationData(), |
| 46 new MockNotificationDelegate(id)) {} | 49 new MockNotificationDelegate(id)) {} |
| 47 | 50 |
| 48 Notification GetResult() { return notification_; } | 51 Notification GetResult() { return notification_; } |
| 49 | 52 |
| 53 NotificationBuilder& SetImage(const gfx::Image& image) { |
| 54 notification_.set_image(image); |
| 55 return *this; |
| 56 } |
| 57 |
| 50 NotificationBuilder& SetItems( | 58 NotificationBuilder& SetItems( |
| 51 const std::vector<message_center::NotificationItem>& items) { | 59 const std::vector<message_center::NotificationItem>& items) { |
| 52 notification_.set_items(items); | 60 notification_.set_items(items); |
| 53 return *this; | 61 return *this; |
| 54 } | 62 } |
| 55 | 63 |
| 56 NotificationBuilder& SetNeverTimeout(bool never_timeout) { | 64 NotificationBuilder& SetNeverTimeout(bool never_timeout) { |
| 57 notification_.set_never_timeout(never_timeout); | 65 notification_.set_never_timeout(never_timeout); |
| 58 return *this; | 66 return *this; |
| 59 } | 67 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 76 private: | 84 private: |
| 77 Notification notification_; | 85 Notification notification_; |
| 78 }; | 86 }; |
| 79 | 87 |
| 80 struct NotificationRequest { | 88 struct NotificationRequest { |
| 81 std::string summary; | 89 std::string summary; |
| 82 std::string body; | 90 std::string body; |
| 83 int32_t expire_timeout = 0; | 91 int32_t expire_timeout = 0; |
| 84 }; | 92 }; |
| 85 | 93 |
| 94 const SkBitmap CreateBitmap(int width, int height) { |
| 95 SkBitmap bitmap; |
| 96 bitmap.allocN32Pixels(width, height); |
| 97 bitmap.eraseARGB(255, 0, 255, 0); |
| 98 return bitmap; |
| 99 } |
| 100 |
| 101 gfx::ImageSkia CreateImageSkia(int width, int height) { |
| 102 return gfx::ImageSkia::CreateFrom1xBitmap(CreateBitmap(width, height)); |
| 103 } |
| 104 |
| 86 NotificationRequest ParseRequest(dbus::MethodCall* method_call) { | 105 NotificationRequest ParseRequest(dbus::MethodCall* method_call) { |
| 87 // The "Notify" message must have type (susssasa{sv}i). | 106 // The "Notify" message must have type (susssasa{sv}i). |
| 88 // https://developer.gnome.org/notification-spec/#command-notify | 107 // https://developer.gnome.org/notification-spec/#command-notify |
| 89 NotificationRequest request; | 108 NotificationRequest request; |
| 90 | 109 |
| 91 dbus::MessageReader reader(method_call); | 110 dbus::MessageReader reader(method_call); |
| 92 std::string str; | 111 std::string str; |
| 93 uint32_t uint32; | 112 uint32_t uint32; |
| 94 EXPECT_TRUE(reader.PopString(&str)); // app_name | 113 EXPECT_TRUE(reader.PopString(&str)); // app_name |
| 95 EXPECT_TRUE(reader.PopUint32(&uint32)); // replaces_id | 114 EXPECT_TRUE(reader.PopUint32(&uint32)); // replaces_id |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 192 mock_bus_.get(), kFreedesktopNotificationsName, | 211 mock_bus_.get(), kFreedesktopNotificationsName, |
| 193 dbus::ObjectPath(kFreedesktopNotificationsPath)); | 212 dbus::ObjectPath(kFreedesktopNotificationsPath)); |
| 194 | 213 |
| 195 EXPECT_CALL(*mock_bus_.get(), | 214 EXPECT_CALL(*mock_bus_.get(), |
| 196 GetObjectProxy(kFreedesktopNotificationsName, | 215 GetObjectProxy(kFreedesktopNotificationsName, |
| 197 dbus::ObjectPath(kFreedesktopNotificationsPath))) | 216 dbus::ObjectPath(kFreedesktopNotificationsPath))) |
| 198 .WillOnce(Return(mock_notification_proxy_.get())); | 217 .WillOnce(Return(mock_notification_proxy_.get())); |
| 199 | 218 |
| 200 EXPECT_CALL(*mock_notification_proxy_.get(), | 219 EXPECT_CALL(*mock_notification_proxy_.get(), |
| 201 MockCallMethodAndBlock(Calls("GetCapabilities"), _)) | 220 MockCallMethodAndBlock(Calls("GetCapabilities"), _)) |
| 202 .WillOnce(OnGetCapabilities(std::vector<std::string>{"body"})); | 221 .WillOnce( |
| 222 OnGetCapabilities(std::vector<std::string>{"body", "body-images"})); |
| 203 | 223 |
| 204 EXPECT_CALL(*mock_notification_proxy_.get(), | 224 EXPECT_CALL(*mock_notification_proxy_.get(), |
| 205 MockCallMethodAndBlock(Calls("GetServerInformation"), _)) | 225 MockCallMethodAndBlock(Calls("GetServerInformation"), _)) |
| 206 .WillOnce(OnGetServerInformation("1.2")); | 226 .WillOnce(OnGetServerInformation("1.2")); |
| 207 | 227 |
| 208 EXPECT_CALL( | 228 EXPECT_CALL( |
| 209 *mock_notification_proxy_.get(), | 229 *mock_notification_proxy_.get(), |
| 210 ConnectToSignal(kFreedesktopNotificationsName, "ActionInvoked", _, _)) | 230 ConnectToSignal(kFreedesktopNotificationsName, "ActionInvoked", _, _)) |
| 211 .WillOnce(RegisterSignalCallback(&action_invoked_callback_)); | 231 .WillOnce(RegisterSignalCallback(&action_invoked_callback_)); |
| 212 | 232 |
| 213 EXPECT_CALL(*mock_notification_proxy_.get(), | 233 EXPECT_CALL(*mock_notification_proxy_.get(), |
| 214 ConnectToSignal(kFreedesktopNotificationsName, | 234 ConnectToSignal(kFreedesktopNotificationsName, |
| 215 "NotificationClosed", _, _)) | 235 "NotificationClosed", _, _)) |
| 216 .WillOnce(RegisterSignalCallback(¬ification_closed_callback_)); | 236 .WillOnce(RegisterSignalCallback(¬ification_closed_callback_)); |
| 217 } | 237 } |
| 218 | 238 |
| 219 void TearDown() override { | 239 void TearDown() override { |
| 220 EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()); | 240 EXPECT_CALL(*mock_bus_.get(), ShutdownAndBlock()); |
| 221 notification_bridge_linux_->CleanUp(); | 241 notification_bridge_linux_->CleanUp(); |
| 222 content::RunAllBlockingPoolTasksUntilIdle(); | 242 content::RunAllBlockingPoolTasksUntilIdle(); |
| 223 notification_bridge_linux_.reset(); | 243 notification_bridge_linux_.reset(); |
| 224 mock_notification_proxy_ = nullptr; | 244 mock_notification_proxy_ = nullptr; |
| 225 mock_bus_ = nullptr; | 245 mock_bus_ = nullptr; |
| 226 } | 246 } |
| 227 | 247 |
| 228 protected: | 248 protected: |
| 229 void CreateNotificationBridgeLinux() { | 249 void CreateNotificationBridgeLinux() { |
| 230 notification_bridge_linux_ = | 250 notification_bridge_linux_ = |
| 231 base::WrapUnique(new NotificationPlatformBridgeLinux(mock_bus_)); | 251 base::WrapUnique(new NotificationPlatformBridgeLinux(mock_bus_)); |
| 252 content::RunAllBlockingPoolTasksUntilIdle(); |
| 232 } | 253 } |
| 233 | 254 |
| 234 content::TestBrowserThreadBundle thread_bundle_; | 255 content::TestBrowserThreadBundle thread_bundle_; |
| 235 | 256 |
| 236 scoped_refptr<dbus::MockBus> mock_bus_; | 257 scoped_refptr<dbus::MockBus> mock_bus_; |
| 237 scoped_refptr<dbus::MockObjectProxy> mock_notification_proxy_; | 258 scoped_refptr<dbus::MockObjectProxy> mock_notification_proxy_; |
| 238 | 259 |
| 239 base::Callback<void(dbus::Signal*)> action_invoked_callback_; | 260 base::Callback<void(dbus::Signal*)> action_invoked_callback_; |
| 240 base::Callback<void(dbus::Signal*)> notification_closed_callback_; | 261 base::Callback<void(dbus::Signal*)> notification_closed_callback_; |
| 241 | 262 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 2)); | 343 2)); |
| 323 | 344 |
| 324 CreateNotificationBridgeLinux(); | 345 CreateNotificationBridgeLinux(); |
| 325 notification_bridge_linux_->Display( | 346 notification_bridge_linux_->Display( |
| 326 NotificationCommon::PERSISTENT, "", "", false, | 347 NotificationCommon::PERSISTENT, "", "", false, |
| 327 NotificationBuilder("1").SetNeverTimeout(false).GetResult()); | 348 NotificationBuilder("1").SetNeverTimeout(false).GetResult()); |
| 328 notification_bridge_linux_->Display( | 349 notification_bridge_linux_->Display( |
| 329 NotificationCommon::PERSISTENT, "", "", false, | 350 NotificationCommon::PERSISTENT, "", "", false, |
| 330 NotificationBuilder("2").SetNeverTimeout(true).GetResult()); | 351 NotificationBuilder("2").SetNeverTimeout(true).GetResult()); |
| 331 } | 352 } |
| 353 |
| 354 TEST_F(NotificationPlatformBridgeLinuxTest, NotificationImages) { |
| 355 const int kMaxImageWidth = 200; |
| 356 const int kMaxImageHeight = 100; |
| 357 |
| 358 const int original_width = kMaxImageWidth * 2; |
| 359 const int original_height = kMaxImageHeight; |
| 360 const int expected_width = kMaxImageWidth; |
| 361 const int expected_height = kMaxImageHeight / 2; |
| 362 |
| 363 gfx::Image original_image = |
| 364 gfx::Image(CreateImageSkia(original_width, original_height)); |
| 365 |
| 366 EXPECT_CALL(*mock_notification_proxy_.get(), |
| 367 MockCallMethodAndBlock(Calls("Notify"), _)) |
| 368 .WillOnce(OnNotify( |
| 369 [=](const NotificationRequest& request) { |
| 370 std::string file_name; |
| 371 EXPECT_TRUE(RE2::FullMatch( |
| 372 request.body, "\\<img src=\\\"(.+)\\\" alt=\\\".*\\\"/\\>", |
| 373 &file_name)); |
| 374 std::string file_contents; |
| 375 EXPECT_TRUE(base::ReadFileToString(base::FilePath(file_name), |
| 376 &file_contents)); |
| 377 gfx::Image image = gfx::Image::CreateFrom1xPNGBytes( |
| 378 reinterpret_cast<const unsigned char*>(file_contents.c_str()), |
| 379 file_contents.size()); |
| 380 EXPECT_EQ(expected_width, image.Width()); |
| 381 EXPECT_EQ(expected_height, image.Height()); |
| 382 }, |
| 383 1)); |
| 384 |
| 385 CreateNotificationBridgeLinux(); |
| 386 notification_bridge_linux_->Display( |
| 387 NotificationCommon::PERSISTENT, "", "", false, |
| 388 NotificationBuilder("") |
| 389 .SetType(message_center::NOTIFICATION_TYPE_IMAGE) |
| 390 .SetImage(original_image) |
| 391 .GetResult()); |
| 392 } |
| OLD | NEW |