blob: 4c1acf32cfaff155cc03c4029bf1193a2c30c4bd [file] [log] [blame]
// 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_platform_bridge_win.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <windows.ui.notifications.h>
#include <wrl/client.h>
#include <wrl/implements.h>
#include "base/hash.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_hstring.h"
#include "base/win/windows_version.h"
#include "chrome/browser/notifications/notification_common.h"
#include "chrome/browser/notifications/win/fake_itoastnotification.h"
#include "chrome/browser/notifications/win/fake_notification_image_retainer.h"
#include "chrome/browser/notifications/win/notification_launch_id.h"
#include "chrome/browser/notifications/win/notification_template_builder.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notifier_id.h"
namespace mswr = Microsoft::WRL;
namespace winui = ABI::Windows::UI;
using message_center::Notification;
namespace {
constexpr char kLaunchId[] =
"0|0|Default|0|https://example.com/|notification_id";
constexpr char kOrigin[] = "https://www.google.com/";
constexpr char kNotificationId[] = "id";
constexpr char kProfileId[] = "Default";
} // namespace
class NotificationPlatformBridgeWinTest : public testing::Test {
public:
NotificationPlatformBridgeWinTest()
: thread_bundle_(
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {}
~NotificationPlatformBridgeWinTest() override = default;
protected:
mswr::ComPtr<winui::Notifications::IToastNotification2> GetToast(
NotificationPlatformBridgeWin* bridge,
const NotificationLaunchId& launch_id,
bool renotify,
const std::string& profile_id,
bool incognito) {
DCHECK(bridge);
GURL origin(kOrigin);
auto notification = std::make_unique<message_center::Notification>(
message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, L"title",
L"message", gfx::Image(), L"display_source", origin,
message_center::NotifierId(origin),
message_center::RichNotificationData(), nullptr /* delegate */);
notification->set_renotify(renotify);
FakeNotificationImageRetainer image_retainer;
base::string16 xml_template =
BuildNotificationTemplate(&image_retainer, launch_id, *notification);
mswr::ComPtr<winui::Notifications::IToastNotification> toast =
bridge->GetToastNotificationForTesting(*notification, xml_template,
profile_id, incognito);
if (!toast) {
LOG(ERROR) << "GetToastNotificationForTesting failed";
return nullptr;
}
mswr::ComPtr<winui::Notifications::IToastNotification2> toast2;
HRESULT hr = toast.As<winui::Notifications::IToastNotification2>(&toast2);
if (FAILED(hr)) {
LOG(ERROR) << "Converting to IToastNotification2 failed";
return nullptr;
}
return toast2;
}
content::TestBrowserThreadBundle thread_bundle_;
private:
DISALLOW_COPY_AND_ASSIGN(NotificationPlatformBridgeWinTest);
};
TEST_F(NotificationPlatformBridgeWinTest, GroupAndTag) {
// This test requires WinRT core functions, which are not available in
// older versions of Windows.
if (base::win::GetVersion() < base::win::VERSION_WIN8)
return;
base::win::ScopedCOMInitializer com_initializer;
NotificationPlatformBridgeWin bridge;
NotificationLaunchId launch_id(kLaunchId);
ASSERT_TRUE(launch_id.is_valid());
mswr::ComPtr<winui::Notifications::IToastNotification2> toast2 =
GetToast(&bridge, launch_id, /*renotify=*/false, kProfileId,
/*incognito=*/false);
ASSERT_TRUE(toast2);
HSTRING hstring_group;
ASSERT_HRESULT_SUCCEEDED(toast2->get_Group(&hstring_group));
base::win::ScopedHString group(hstring_group);
// NOTE: If you find yourself needing to change this value, make sure that
// NotificationPlatformBridgeWinImpl::Close supports specifying the right
// group value for RemoveGroupedTagWithId.
ASSERT_STREQ(L"Notifications", group.Get().as_string().c_str());
HSTRING hstring_tag;
ASSERT_HRESULT_SUCCEEDED(toast2->get_Tag(&hstring_tag));
base::win::ScopedHString tag(hstring_tag);
std::string tag_data = std::string(kNotificationId) + "|" + kProfileId + "|0";
ASSERT_STREQ(base::UintToString16(base::Hash(tag_data)).c_str(),
tag.Get().as_string().c_str());
}
TEST_F(NotificationPlatformBridgeWinTest, GroupAndTagUniqueness) {
// This test requires WinRT core functions, which are not available in
// older versions of Windows.
if (base::win::GetVersion() < base::win::VERSION_WIN8)
return;
base::win::ScopedCOMInitializer com_initializer;
NotificationPlatformBridgeWin bridge;
NotificationLaunchId launch_id(kLaunchId);
ASSERT_TRUE(launch_id.is_valid());
mswr::ComPtr<winui::Notifications::IToastNotification2> toastA;
mswr::ComPtr<winui::Notifications::IToastNotification2> toastB;
HSTRING hstring_tagA;
HSTRING hstring_tagB;
// Different profiles, same incognito status -> Unique tags.
{
toastA = GetToast(&bridge, launch_id, /*renotify=*/false, "Profile1",
/*incognito=*/true);
toastB = GetToast(&bridge, launch_id, /*renotify=*/false, "Profile2",
/*incognito=*/true);
ASSERT_TRUE(toastA);
ASSERT_TRUE(toastB);
ASSERT_HRESULT_SUCCEEDED(toastA->get_Tag(&hstring_tagA));
base::win::ScopedHString tagA(hstring_tagA);
ASSERT_HRESULT_SUCCEEDED(toastB->get_Tag(&hstring_tagB));
base::win::ScopedHString tagB(hstring_tagB);
ASSERT_TRUE(tagA.Get().as_string() != tagB.Get().as_string());
}
// Same profile, different incognito status -> Unique tags.
{
toastA = GetToast(&bridge, launch_id, /*renotify=*/false, "Profile1",
/*incognito=*/true);
toastB = GetToast(&bridge, launch_id, /*renotify=*/false, "Profile1",
/*incognito=*/false);
ASSERT_TRUE(toastA);
ASSERT_TRUE(toastB);
ASSERT_HRESULT_SUCCEEDED(toastA->get_Tag(&hstring_tagA));
base::win::ScopedHString tagA(hstring_tagA);
ASSERT_HRESULT_SUCCEEDED(toastB->get_Tag(&hstring_tagB));
base::win::ScopedHString tagB(hstring_tagB);
ASSERT_TRUE(tagA.Get().as_string() != tagB.Get().as_string());
}
// Same profile, same incognito status -> Identical tags.
{
toastA = GetToast(&bridge, launch_id, /*renotify=*/false, "Profile1",
/*incognito=*/true);
toastB = GetToast(&bridge, launch_id, /*renotify=*/false, "Profile1",
/*incognito=*/true);
ASSERT_TRUE(toastA);
ASSERT_TRUE(toastB);
ASSERT_HRESULT_SUCCEEDED(toastA->get_Tag(&hstring_tagA));
base::win::ScopedHString tagA(hstring_tagA);
ASSERT_HRESULT_SUCCEEDED(toastB->get_Tag(&hstring_tagB));
base::win::ScopedHString tagB(hstring_tagB);
ASSERT_STREQ(tagA.Get().as_string().c_str(),
tagB.Get().as_string().c_str());
}
}
TEST_F(NotificationPlatformBridgeWinTest, Suppress) {
// This test requires WinRT core functions, which are not available in
// older versions of Windows.
if (base::win::GetVersion() < base::win::VERSION_WIN8)
return;
base::win::ScopedCOMInitializer com_initializer;
NotificationPlatformBridgeWin bridge;
std::vector<mswr::ComPtr<winui::Notifications::IToastNotification>>
notifications;
bridge.SetDisplayedNotificationsForTesting(&notifications);
mswr::ComPtr<winui::Notifications::IToastNotification2> toast2;
boolean suppress;
NotificationLaunchId launch_id(kLaunchId);
ASSERT_TRUE(launch_id.is_valid());
// Make sure this works a toast is not suppressed when no notifications are
// registered.
toast2 = GetToast(&bridge, launch_id, /*renotify=*/false, kProfileId,
/*incognito=*/false);
ASSERT_TRUE(toast2);
ASSERT_HRESULT_SUCCEEDED(toast2->get_SuppressPopup(&suppress));
ASSERT_FALSE(suppress);
toast2.Reset();
// Register a single notification with a specific tag.
std::string tag_data = std::string(kNotificationId) + "|" + kProfileId + "|0";
base::string16 tag = base::UintToString16(base::Hash(tag_data));
// Microsoft::WRL::Make() requires FakeIToastNotification to derive from
// RuntimeClass.
notifications.push_back(Microsoft::WRL::Make<FakeIToastNotification>(
L"<toast launch=\"0|0|Default|0|https://foo.com/|id\"></toast>", tag));
// Request this notification with renotify true (should not be suppressed).
toast2 = GetToast(&bridge, launch_id, /*renotify=*/true, kProfileId,
/*incognito=*/false);
ASSERT_TRUE(toast2);
ASSERT_HRESULT_SUCCEEDED(toast2->get_SuppressPopup(&suppress));
ASSERT_FALSE(suppress);
toast2.Reset();
// Request this notification with renotify false (should be suppressed).
toast2 = GetToast(&bridge, launch_id, /*renotify=*/false, kProfileId,
/*incognito=*/false);
ASSERT_TRUE(toast2);
ASSERT_HRESULT_SUCCEEDED(toast2->get_SuppressPopup(&suppress));
ASSERT_TRUE(suppress);
toast2.Reset();
bridge.SetDisplayedNotificationsForTesting(nullptr);
}