blob: 5c9156ceba449e125eb794c7a1dbf4a2a8c6e88d [file] [log] [blame]
// Copyright 2015 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_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_
#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_
#include <map>
#include <set>
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/engagement/site_engagement_metrics.h"
#include "components/history/core/browser/history_service_observer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "ui/base/page_transition_types.h"
namespace base {
class DictionaryValue;
class Clock;
}
class GURL;
class Profile;
class SiteEngagementScore {
public:
// The parameters which can be varied via field trial. All "points" values
// should be appended to the end of the enum prior to MAX_VARIATION.
enum Variation {
// The maximum number of points that can be accrued in one day.
MAX_POINTS_PER_DAY = 0,
// The period over which site engagement decays.
DECAY_PERIOD_IN_DAYS,
// The number of points to decay per period.
DECAY_POINTS,
// The number of points given for navigations.
NAVIGATION_POINTS,
// The number of points given for user input.
USER_INPUT_POINTS,
// The number of points given for media playing. Initially calibrated such
// that at least 30 minutes of foreground media would be required to allow a
// site to reach the daily engagement maximum.
VISIBLE_MEDIA_POINTS,
HIDDEN_MEDIA_POINTS,
// The number of points added to engagement when a site is launched from
// homescreen or added as a bookmark app. This bonus will apply for ten days
// following a launch; each new launch resets the ten days.
WEB_APP_INSTALLED_POINTS,
// The number of points given for the first engagement event of the day for
// each site.
FIRST_DAILY_ENGAGEMENT,
MAX_VARIATION
};
// The maximum number of points that are allowed.
static const double kMaxPoints;
static double GetMaxPointsPerDay();
static double GetDecayPeriodInDays();
static double GetDecayPoints();
static double GetNavigationPoints();
static double GetUserInputPoints();
static double GetVisibleMediaPoints();
static double GetHiddenMediaPoints();
static double GetWebAppInstalledPoints();
static double GetFirstDailyEngagementPoints();
// Update the default engagement settings via variations.
static void UpdateFromVariations();
// The SiteEngagementService does not take ownership of |clock|. It is the
// responsibility of the caller to make sure |clock| outlives this
// SiteEngagementScore.
SiteEngagementScore(base::Clock* clock,
const base::DictionaryValue& score_dict);
~SiteEngagementScore();
double Score() const;
void AddPoints(double points);
// Resets the score to |points| and reset the daily point limit.
void Reset(double points);
// Returns true if the maximum number of points today has been added.
bool MaxPointsPerDayAdded() const;
// Get/set the last time this origin was launched from an installed shortcut.
base::Time last_shortcut_launch_time() { return last_shortcut_launch_time_; }
void set_last_shortcut_launch_time(const base::Time& time) {
last_shortcut_launch_time_ = time;
}
// Updates the content settings dictionary |score_dict| with the current score
// fields. Returns true if |score_dict| changed, otherwise return false.
bool UpdateScoreDict(base::DictionaryValue* score_dict);
private:
FRIEND_TEST_ALL_PREFIXES(SiteEngagementScoreTest, PartiallyEmptyDictionary);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementScoreTest, PopulatedDictionary);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementScoreTest, Reset);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementScoreTest, FirstDailyEngagementBonus);
friend class SiteEngagementHelperTest;
friend class SiteEngagementScoreTest;
friend class SiteEngagementServiceTest;
// Array holding the values corresponding to each item in Variation array.
static double param_values[];
// Keys used in the content settings dictionary.
static const char* kRawScoreKey;
static const char* kPointsAddedTodayKey;
static const char* kLastEngagementTimeKey;
static const char* kLastShortcutLaunchTimeKey;
// This version of the constructor is used in unit tests.
explicit SiteEngagementScore(base::Clock* clock);
// Determine the score, accounting for any decay.
double DecayedScore() const;
// Determine any score bonus from having installed shortcuts.
double BonusScore() const;
// Sets fixed parameter values for testing site engagement. Ensure that any
// newly added parameters receive a fixed value here.
static void SetParamValuesForTesting();
// The clock used to vend times. Enables time travelling in tests. Owned by
// the SiteEngagementService.
base::Clock* clock_;
// |raw_score_| is the score before any decay is applied.
double raw_score_;
// The points added 'today' are tracked to avoid adding more than
// kMaxPointsPerDay on any one day. 'Today' is defined in local time.
double points_added_today_;
// The last time the score was updated for engagement. Used in conjunction
// with |points_added_today_| to avoid adding more than kMaxPointsPerDay on
// any one day.
base::Time last_engagement_time_;
// The last time the site with this score was launched from an installed
// shortcut.
base::Time last_shortcut_launch_time_;
DISALLOW_COPY_AND_ASSIGN(SiteEngagementScore);
};
class SiteEngagementScoreProvider {
public:
// Returns a non-negative integer representing the engagement score of the
// origin for this URL.
virtual double GetScore(const GURL& url) = 0;
// Returns the sum of engagement points awarded to all sites.
virtual double GetTotalEngagementPoints() = 0;
};
// Stores and retrieves the engagement score of an origin.
//
// An engagement score is a positive integer that represents how much a user has
// engaged with an origin - the higher it is, the more engagement the user has
// had with this site recently.
//
// Positive user activity, such as visiting the origin often and adding it to
// the homescreen, will increase the site engagement score. Negative activity,
// such as rejecting permission prompts or not responding to notifications, will
// decrease the site engagement score.
class SiteEngagementService : public KeyedService,
public history::HistoryServiceObserver,
public SiteEngagementScoreProvider {
public:
// The name of the site engagement variation field trial.
static const char kEngagementParams[];
static SiteEngagementService* Get(Profile* profile);
// Returns whether or not the SiteEngagementService is enabled.
static bool IsEnabled();
explicit SiteEngagementService(Profile* profile);
~SiteEngagementService() override;
// Returns a map of all stored origins and their engagement scores.
std::map<GURL, double> GetScoreMap();
// Update the engagement score of the origin matching |url| for navigation.
void HandleNavigation(const GURL& url, ui::PageTransition transition);
// Update the engagement score of the origin matching |url| for time-on-site,
// based on user input.
void HandleUserInput(const GURL& url,
SiteEngagementMetrics::EngagementType type);
// Update the engagement score of the origin matching |url| for media playing.
// The points awarded are discounted if the media is being played in a non-
// visible tab.
void HandleMediaPlaying(const GURL& url, bool is_hidden);
// Resets the engagement score |url| to |score|, clearing daily limits.
void ResetScoreForURL(const GURL& url, double score);
// Overridden from history::HistoryServiceObserver:
void OnURLsDeleted(history::HistoryService* history_service,
bool all_history,
bool expired,
const history::URLRows& deleted_rows,
const std::set<GURL>& favicon_urls) override;
// Update the last time |url| was opened from an installed shortcut to be
// clock_->Now().
void SetLastShortcutLaunchTime(const GURL& url);
// Overridden from SiteEngagementScoreProvider:
double GetScore(const GURL& url) override;
double GetTotalEngagementPoints() override;
private:
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CheckHistograms);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CleanupEngagementScores);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, ClearHistoryForURLs);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, GetMedianEngagement);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, GetTotalNavigationPoints);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, GetTotalUserInputPoints);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, LastShortcutLaunch);
FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest,
CleanupOriginsOnHistoryDeletion);
FRIEND_TEST_ALL_PREFIXES(AppBannerSettingsHelperTest, SiteEngagementTrigger);
// Only used in tests.
SiteEngagementService(Profile* profile, scoped_ptr<base::Clock> clock);
// Adds the specified number of points to the given origin, respecting the
// maximum limits for the day and overall.
void AddPoints(const GURL& url, double points);
// Post startup tasks: cleaning up origins which have decayed to 0, and
// logging UMA statistics.
void AfterStartupTask();
void CleanupEngagementScores();
void RecordMetrics();
// Returns the median engagement score of all recorded origins.
double GetMedianEngagement(std::map<GURL, double>& score_map);
// Returns the number of origins with maximum daily and total engagement
// respectively.
int OriginsWithMaxDailyEngagement();
int OriginsWithMaxEngagement(std::map<GURL, double>& score_map);
void GetCountsForOriginsComplete(
const history::OriginCountMap& origin_counts);
Profile* profile_;
// The clock used to vend times.
scoped_ptr<base::Clock> clock_;
// Metrics are recorded at non-incognito browser startup, and then
// approximately once per hour thereafter. Store the local time at which
// metrics were previously uploaded: the first event which affects any
// origin's engagement score after an hour has elapsed triggers the next
// upload.
base::Time last_metrics_time_;
base::WeakPtrFactory<SiteEngagementService> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(SiteEngagementService);
};
#endif // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SERVICE_H_