Commit b88ff2d3 authored by Xing Liu's avatar Xing Liu Committed by Commit Bot

Announcement notification: Handle case where Finch is not ready.

Finch parameter is not guaranteed to be ready on first run. This may
introduce unexpected behaviors, like the user see the notification on
second time running Chrome.

Now we always persist the first run timestamp and uses a Finch
specified timestamp from the server to check if first run happens
recently. If so then just skip the notification.

Also address an issue that origin checks for
NotificationPlatformBridgeMac is removed, now we let the empty GURL
pass.

Bug: 1047286,1046457
Change-Id: I59723c7d35230345ca9d77ccc810a07646d34b0b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2032346
Commit-Queue: Xing Liu <xingliu@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737193}
parent ca4420c7
...@@ -460,6 +460,16 @@ bool NotificationPlatformBridgeMac::VerifyNotificationData( ...@@ -460,6 +460,16 @@ bool NotificationPlatformBridgeMac::VerifyNotificationData(
return false; return false;
} }
// Origin is not actually required but if it's there it should be a valid one.
NSString* origin =
[response objectForKey:notification_constants::kNotificationOrigin];
if (origin && origin.length) {
std::string notificationOrigin = base::SysNSStringToUTF8(origin);
GURL url(notificationOrigin);
if (!url.is_valid())
return false;
}
return true; return true;
} }
......
...@@ -206,6 +206,21 @@ TEST_F(NotificationPlatformBridgeMacTest, ...@@ -206,6 +206,21 @@ TEST_F(NotificationPlatformBridgeMacTest,
EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response)); EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
} }
TEST_F(NotificationPlatformBridgeMacTest, TestNotificationVerifyOrigin) {
NSMutableDictionary* response = BuildDefaultNotificationResponse();
[response setValue:@"invalidorigin"
forKey:notification_constants::kNotificationOrigin];
EXPECT_FALSE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
// If however the origin is not present the response should be fine.
[response removeObjectForKey:notification_constants::kNotificationOrigin];
EXPECT_TRUE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
// Empty origin should be fine.
[response setValue:@"" forKey:notification_constants::kNotificationOrigin];
EXPECT_TRUE(NotificationPlatformBridgeMac::VerifyNotificationData(response));
}
TEST_F(NotificationPlatformBridgeMacTest, TestDisplayNoButtons) { TEST_F(NotificationPlatformBridgeMacTest, TestDisplayNoButtons) {
std::unique_ptr<Notification> notification = std::unique_ptr<Notification> notification =
CreateBanner("Title", "Context", "https://gmail.com", nullptr, nullptr); CreateBanner("Title", "Context", "https://gmail.com", nullptr, nullptr);
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h" #include "base/metrics/field_trial_params.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_attributes_entry.h" #include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_attributes_storage.h"
...@@ -35,22 +36,24 @@ class AnnouncementNotificationServiceImpl ...@@ -35,22 +36,24 @@ class AnnouncementNotificationServiceImpl
AnnouncementNotificationServiceImpl(const base::FilePath& profile_path, AnnouncementNotificationServiceImpl(const base::FilePath& profile_path,
bool new_profile, bool new_profile,
PrefService* pref_service, PrefService* pref_service,
std::unique_ptr<Delegate> delegate) std::unique_ptr<Delegate> delegate,
base::Clock* clock)
: profile_path_(profile_path), : profile_path_(profile_path),
new_profile_(new_profile), new_profile_(new_profile),
pref_service_(pref_service), pref_service_(pref_service),
delegate_(std::move(delegate)), delegate_(std::move(delegate)),
clock_(clock),
skip_first_run_(true), skip_first_run_(true),
skip_new_profile_(false), skip_new_profile_(false),
skip_display_(false), skip_display_(false),
remote_version_(kInvalidVersion), remote_version_(kInvalidVersion),
require_signout_(false), require_signout_(false),
show_one_(false) { show_one_(false) {
DCHECK(delegate_);
if (!IsFeatureEnabled()) if (!IsFeatureEnabled())
return; return;
DCHECK(delegate_);
// By default do nothing in first run. // By default do nothing in first run.
skip_first_run_ = base::GetFieldTrialParamByFeatureAsBool( skip_first_run_ = base::GetFieldTrialParamByFeatureAsBool(
kAnnouncementNotification, kSkipFirstRun, true); kAnnouncementNotification, kSkipFirstRun, true);
...@@ -66,6 +69,14 @@ class AnnouncementNotificationServiceImpl ...@@ -66,6 +69,14 @@ class AnnouncementNotificationServiceImpl
kAnnouncementNotification, kShowOneAllProfiles, false); kAnnouncementNotification, kShowOneAllProfiles, false);
remote_url_ = base::GetFieldTrialParamValueByFeature( remote_url_ = base::GetFieldTrialParamValueByFeature(
kAnnouncementNotification, kAnnouncementUrl); kAnnouncementNotification, kAnnouncementUrl);
bool success = base::Time::FromString(
base::GetFieldTrialParamValueByFeature(kAnnouncementNotification,
kSkipFirstRunAfterTime)
.c_str(),
&skip_first_run_after_);
if (!success)
skip_first_run_after_ = base::Time();
} }
~AnnouncementNotificationServiceImpl() override = default; ~AnnouncementNotificationServiceImpl() override = default;
...@@ -73,6 +84,10 @@ class AnnouncementNotificationServiceImpl ...@@ -73,6 +84,10 @@ class AnnouncementNotificationServiceImpl
private: private:
// AnnouncementNotificationService implementation. // AnnouncementNotificationService implementation.
void MaybeShowNotification() override { void MaybeShowNotification() override {
// Finch config may not be delivered on first run. Records the first run
// timestamp to check whether we want to skip the notification.
RecordFirstRunIfNeeded();
if (!IsFeatureEnabled()) if (!IsFeatureEnabled())
return; return;
...@@ -92,6 +107,13 @@ class AnnouncementNotificationServiceImpl ...@@ -92,6 +107,13 @@ class AnnouncementNotificationServiceImpl
} }
} }
void RecordFirstRunIfNeeded() {
if (!delegate_->IsFirstRun())
return;
pref_service_->SetTime(kAnnouncementFirstRunTimePrefName, clock_->Now());
}
bool IsFeatureEnabled() const { bool IsFeatureEnabled() const {
return base::FeatureList::IsEnabled(kAnnouncementNotification); return base::FeatureList::IsEnabled(kAnnouncementNotification);
} }
...@@ -99,14 +121,9 @@ class AnnouncementNotificationServiceImpl ...@@ -99,14 +121,9 @@ class AnnouncementNotificationServiceImpl
bool IsVersionValid(int version) const { return version >= 0; } bool IsVersionValid(int version) const { return version >= 0; }
void OnNewVersion() { void OnNewVersion() {
// Skip first run if needed. if (ShouldSkipDueToFirstRun())
if (delegate_->IsFirstRun() && skip_first_run_)
return; return;
ShowNotification();
}
void ShowNotification() {
if (skip_display_) if (skip_display_)
return; return;
...@@ -122,10 +139,38 @@ class AnnouncementNotificationServiceImpl ...@@ -122,10 +139,38 @@ class AnnouncementNotificationServiceImpl
if (show_one_ && notification_shown) if (show_one_ && notification_shown)
return; return;
ShowNotification();
}
void ShowNotification() {
notification_shown = true; notification_shown = true;
delegate_->ShowNotification(); delegate_->ShowNotification();
} }
// Returns whether the notification should be skipped based on first run
// controls defined in Finch.
bool ShouldSkipDueToFirstRun() const {
// Don't show notification for first run if Finch parameter specified
// "skip_first_run" to true.
if (delegate_->IsFirstRun() && skip_first_run_)
return true;
// Finch parameters is not guaranteed to receive by Chrome on first run.
// Don't show notification if first run happens after the timestamp
// specified in Finch parameter "skip_first_run_after_time".
base::Time first_run_time =
pref_service_->GetTime(kAnnouncementFirstRunTimePrefName);
if (!first_run_time.is_null() && !skip_first_run_after_.is_null() &&
first_run_time >= skip_first_run_after_) {
return true;
}
// Notice that the first run can happen before
// kAnnouncementFirstRunTimePrefName was added to the code base . In this
// case, we do want to show the announcement.
return false;
}
bool IsUserSignIn() { bool IsUserSignIn() {
DCHECK(g_browser_process); DCHECK(g_browser_process);
// No browser process, assume the user is not signed in. // No browser process, assume the user is not signed in.
...@@ -147,6 +192,7 @@ class AnnouncementNotificationServiceImpl ...@@ -147,6 +192,7 @@ class AnnouncementNotificationServiceImpl
bool new_profile_; bool new_profile_;
PrefService* pref_service_; PrefService* pref_service_;
std::unique_ptr<Delegate> delegate_; std::unique_ptr<Delegate> delegate_;
base::Clock* clock_;
// Whether to skip first Chrome launch. Parsed from Finch. // Whether to skip first Chrome launch. Parsed from Finch.
bool skip_first_run_; bool skip_first_run_;
...@@ -171,6 +217,9 @@ class AnnouncementNotificationServiceImpl ...@@ -171,6 +217,9 @@ class AnnouncementNotificationServiceImpl
// URL. // URL.
std::string remote_url_; std::string remote_url_;
// If first run happens after this time, then notification should not show.
base::Time skip_first_run_after_;
base::WeakPtrFactory<AnnouncementNotificationServiceImpl> weak_ptr_factory_{ base::WeakPtrFactory<AnnouncementNotificationServiceImpl> weak_ptr_factory_{
this}; this};
...@@ -185,6 +234,7 @@ void AnnouncementNotificationService::RegisterProfilePrefs( ...@@ -185,6 +234,7 @@ void AnnouncementNotificationService::RegisterProfilePrefs(
PrefRegistrySimple* registry) { PrefRegistrySimple* registry) {
DCHECK(registry); DCHECK(registry);
registry->RegisterIntegerPref(kCurrentVersionPrefName, kInvalidVersion); registry->RegisterIntegerPref(kCurrentVersionPrefName, kInvalidVersion);
registry->RegisterTimePref(kAnnouncementFirstRunTimePrefName, base::Time());
} }
// static // static
...@@ -192,9 +242,10 @@ AnnouncementNotificationService* AnnouncementNotificationService::Create( ...@@ -192,9 +242,10 @@ AnnouncementNotificationService* AnnouncementNotificationService::Create(
const base::FilePath& profile_path, const base::FilePath& profile_path,
bool new_profile, bool new_profile,
PrefService* pref_service, PrefService* pref_service,
std::unique_ptr<Delegate> delegate) { std::unique_ptr<Delegate> delegate,
base::Clock* clock) {
return new AnnouncementNotificationServiceImpl( return new AnnouncementNotificationServiceImpl(
profile_path, new_profile, pref_service, std::move(delegate)); profile_path, new_profile, pref_service, std::move(delegate), clock);
} }
// static // static
......
...@@ -15,9 +15,10 @@ ...@@ -15,9 +15,10 @@
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace { namespace base {
class Clock;
class FilePath; class FilePath;
} // namespace } // namespace base
class PrefRegistrySimple; class PrefRegistrySimple;
class PrefService; class PrefService;
...@@ -29,6 +30,12 @@ extern const base::Feature kAnnouncementNotification; ...@@ -29,6 +30,12 @@ extern const base::Feature kAnnouncementNotification;
// notification on first run. // notification on first run.
constexpr char kSkipFirstRun[] = "skip_first_run"; constexpr char kSkipFirstRun[] = "skip_first_run";
// The Finch parameter name for a string value that represents a time.
// If first run happens after this time, notification will not show.
// The string defined in Finch config should specify UTC time, and will be
// parsed by base::Time::FromString().
constexpr char kSkipFirstRunAfterTime[] = "skip_first_run_after_time";
// The Finch parameter name for a boolean value that whether to show // The Finch parameter name for a boolean value that whether to show
// notification for new profile. // notification for new profile.
constexpr char kSkipNewProfile[] = "skip_new_profile"; constexpr char kSkipNewProfile[] = "skip_new_profile";
...@@ -54,6 +61,10 @@ constexpr char kAnnouncementUrl[] = "announcement_url"; ...@@ -54,6 +61,10 @@ constexpr char kAnnouncementUrl[] = "announcement_url";
constexpr char kCurrentVersionPrefName[] = constexpr char kCurrentVersionPrefName[] =
"announcement_notification_service_current_version"; "announcement_notification_service_current_version";
// Preference name to persist the time of Chrome first run.
constexpr char kAnnouncementFirstRunTimePrefName[] =
"announcement_notification_service_first_run_time";
// Used to show a notification when the version defined in Finch parameter is // Used to show a notification when the version defined in Finch parameter is
// higher than the last version saved in the preference service. // higher than the last version saved in the preference service.
class AnnouncementNotificationService : public KeyedService { class AnnouncementNotificationService : public KeyedService {
...@@ -78,7 +89,8 @@ class AnnouncementNotificationService : public KeyedService { ...@@ -78,7 +89,8 @@ class AnnouncementNotificationService : public KeyedService {
const base::FilePath& profile_path, const base::FilePath& profile_path,
bool new_profile, bool new_profile,
PrefService* pref_service, PrefService* pref_service,
std::unique_ptr<Delegate> delegate); std::unique_ptr<Delegate> delegate,
base::Clock* clock);
static GURL GetAnnouncementURL(); static GURL GetAnnouncementURL();
AnnouncementNotificationService(); AnnouncementNotificationService();
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include "base/time/default_clock.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/notifications/notification_display_service_factory.h" #include "chrome/browser/notifications/notification_display_service_factory.h"
#include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/incognito_helpers.h"
...@@ -54,7 +55,8 @@ KeyedService* AnnouncementNotificationServiceFactory::BuildServiceInstanceFor( ...@@ -54,7 +55,8 @@ KeyedService* AnnouncementNotificationServiceFactory::BuildServiceInstanceFor(
std::make_unique<AnnouncementNotificationDelegate>(display_service); std::make_unique<AnnouncementNotificationDelegate>(display_service);
#endif // OS_ANDROID #endif // OS_ANDROID
return AnnouncementNotificationService::Create( return AnnouncementNotificationService::Create(
profile->GetPath(), profile->IsNewProfile(), pref, std::move(delegate)); profile->GetPath(), profile->IsNewProfile(), pref, std::move(delegate),
base::DefaultClock::GetInstance());
} }
content::BrowserContext* content::BrowserContext*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment