1 // Copyright 2016 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SCORE_H_ 6 #define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SCORE_H_ 7 8 #include <array> 9 #include <memory> 10 #include <string> 11 #include <utility> 12 13 #include "base/gtest_prod_util.h" 14 #include "base/macros.h" 15 #include "base/time/time.h" 16 #include "base/values.h" 17 #include "components/site_engagement/core/mojom/site_engagement_details.mojom-forward.h" 18 #include "third_party/blink/public/mojom/site_engagement/site_engagement.mojom-forward.h" 19 #include "url/gurl.h" 20 21 namespace base { 22 class Clock; 23 } 24 25 class HostContentSettingsMap; 26 27 class SiteEngagementScore { 28 public: 29 // The parameters which can be varied via field trial. 30 enum Variation { 31 // The maximum number of points that can be accrued in one day. 32 MAX_POINTS_PER_DAY = 0, 33 34 // The period over which site engagement decays. 35 DECAY_PERIOD_IN_HOURS, 36 37 // The number of points to decay per period. 38 DECAY_POINTS, 39 40 // The proportion [0-1] which the current engagement value is multiplied by 41 // at each decay period, before subtracting DECAY_POINTS. 42 DECAY_PROPORTION, 43 44 // A score will be erased from the engagement system if it's less than this 45 // value. 46 SCORE_CLEANUP_THRESHOLD, 47 48 // The number of points given for navigations. 49 NAVIGATION_POINTS, 50 51 // The number of points given for user input. 52 USER_INPUT_POINTS, 53 54 // The number of points given for media playing. Initially calibrated such 55 // that at least 30 minutes of foreground media would be required to allow a 56 // site to reach the daily engagement maximum. 57 VISIBLE_MEDIA_POINTS, 58 HIDDEN_MEDIA_POINTS, 59 60 // The number of points added to engagement when a site is launched from 61 // homescreen or added as a bookmark app. This bonus will apply for ten days 62 // following a launch; each new launch resets the ten days. 63 WEB_APP_INSTALLED_POINTS, 64 65 // The number of points given for the first engagement event of the day for 66 // each site. 67 FIRST_DAILY_ENGAGEMENT, 68 69 // The number of points that the engagement service must accumulate to be 70 // considered 'useful'. 71 BOOTSTRAP_POINTS, 72 73 // The boundaries between low/medium and medium/high engagement as returned 74 // by GetEngagementLevel(). 75 MEDIUM_ENGAGEMENT_BOUNDARY, 76 HIGH_ENGAGEMENT_BOUNDARY, 77 78 // The maximum number of decays that a SiteEngagementScore can incur before 79 // entering a grace period. MAX_DECAYS_PER_SCORE * DECAY_PERIOD_IN_DAYS is 80 // the max decay period, i.e. the maximum duration permitted for 81 // (clock_->Now() - score.last_engagement_time()). 82 MAX_DECAYS_PER_SCORE, 83 84 // If a SiteEngagamentScore has not been accessed or updated for a period 85 // longer than the max decay period + LAST_ENGAGEMENT_GRACE_PERIOD_IN_HOURS 86 // (see above), its last engagement time will be reset to be max decay 87 // period prior to clock_->Now(). 88 LAST_ENGAGEMENT_GRACE_PERIOD_IN_HOURS, 89 90 // The number of points given for interacting with a displayed notification. 91 NOTIFICATION_INTERACTION_POINTS, 92 93 MAX_VARIATION 94 }; 95 96 // The maximum number of points that are allowed. 97 static const double kMaxPoints; 98 99 static double GetMaxPointsPerDay(); 100 static double GetDecayPeriodInHours(); 101 static double GetDecayPoints(); 102 static double GetDecayProportion(); 103 static double GetScoreCleanupThreshold(); 104 static double GetNavigationPoints(); 105 static double GetUserInputPoints(); 106 static double GetVisibleMediaPoints(); 107 static double GetHiddenMediaPoints(); 108 static double GetWebAppInstalledPoints(); 109 static double GetFirstDailyEngagementPoints(); 110 static double GetBootstrapPoints(); 111 static double GetMediumEngagementBoundary(); 112 static double GetHighEngagementBoundary(); 113 static double GetMaxDecaysPerScore(); 114 static double GetLastEngagementGracePeriodInHours(); 115 static double GetNotificationInteractionPoints(); 116 117 // Sets fixed parameter values for testing site engagement. Ensure that any 118 // newly added parameters receive a fixed value here. 119 static void SetParamValuesForTesting(); 120 121 // Update the default engagement settings via variations. 122 static void UpdateFromVariations(const char* param_name); 123 124 // The SiteEngagementScore does not take ownership of |clock|. It is the 125 // responsibility of the caller to make sure |clock| outlives this 126 // SiteEngagementScore. 127 SiteEngagementScore(base::Clock* clock, 128 const GURL& origin, 129 HostContentSettingsMap* settings); 130 SiteEngagementScore(SiteEngagementScore&& other); 131 ~SiteEngagementScore(); 132 133 SiteEngagementScore& operator=(SiteEngagementScore&& other); 134 135 // Adds |points| to this score, respecting daily limits and the maximum 136 // possible score. Decays the score if it has not been updated recently 137 // enough. 138 void AddPoints(double points); 139 140 // Returns the total score, taking into account the base, bonus and maximum 141 // values. 142 double GetTotalScore() const; 143 144 // Returns a structure containing the origin URL and score, and details 145 // of the base and bonus scores. Note that the |score| is limited to 146 // kMaxPoints, while the detailed scores are returned raw. 147 mojom::SiteEngagementDetails GetDetails() const; 148 149 // Writes the values in this score into |settings_map_|. 150 void Commit(); 151 152 // Returns the discrete engagement level for this score. 153 blink::mojom::EngagementLevel GetEngagementLevel() const; 154 155 // Returns true if the maximum number of points today has been added. 156 bool MaxPointsPerDayAdded() const; 157 158 // Resets the score to |points| and resets the daily point limit. If 159 // |updated_time| is non-null, sets the last engagement time to that value. 160 void Reset(double points, const base::Time updated_time); 161 162 // Get/set the last time this origin was launched from an installed shortcut. last_shortcut_launch_time()163 base::Time last_shortcut_launch_time() const { 164 return last_shortcut_launch_time_; 165 } set_last_shortcut_launch_time(const base::Time & time)166 void set_last_shortcut_launch_time(const base::Time& time) { 167 last_shortcut_launch_time_ = time; 168 } 169 170 // Get/set the last time this origin recorded an engagement change. last_engagement_time()171 base::Time last_engagement_time() const { 172 return last_engagement_time_; 173 } set_last_engagement_time(const base::Time & time)174 void set_last_engagement_time(const base::Time& time) { 175 last_engagement_time_ = time; 176 } 177 178 private: 179 FRIEND_TEST_ALL_PREFIXES(SiteEngagementScoreTest, FirstDailyEngagementBonus); 180 FRIEND_TEST_ALL_PREFIXES(SiteEngagementScoreTest, PartiallyEmptyDictionary); 181 FRIEND_TEST_ALL_PREFIXES(SiteEngagementScoreTest, PopulatedDictionary); 182 FRIEND_TEST_ALL_PREFIXES(SiteEngagementScoreTest, Reset); 183 friend class SiteEngagementScoreTest; 184 friend class SiteEngagementServiceTest; 185 186 using ParamValues = std::array<std::pair<std::string, double>, MAX_VARIATION>; 187 188 // Array holding the values corresponding to each item in Variation array. 189 static ParamValues& GetParamValues(); 190 191 // Keys used in the content settings dictionary. 192 static const char kRawScoreKey[]; 193 static const char kPointsAddedTodayKey[]; 194 static const char kLastEngagementTimeKey[]; 195 static const char kLastShortcutLaunchTimeKey[]; 196 197 // This version of the constructor is used in unit tests. 198 SiteEngagementScore(base::Clock* clock, 199 const GURL& origin, 200 std::unique_ptr<base::DictionaryValue> score_dict); 201 202 // Determine the score, accounting for any decay. 203 double DecayedScore() const; 204 205 // Determine bonus from being installed, and having been launched recently.. 206 double BonusIfShortcutLaunched() const; 207 208 // Updates the content settings dictionary |score_dict| with the current score 209 // fields. Returns true if |score_dict| changed, otherwise return false. 210 bool UpdateScoreDict(base::DictionaryValue* score_dict); 211 212 // The clock used to vend times. Enables time travelling in tests. Owned by 213 // the SiteEngagementService. 214 base::Clock* clock_; 215 216 // |raw_score_| is the score before any decay is applied. 217 double raw_score_; 218 219 // The points added 'today' are tracked to avoid adding more than 220 // kMaxPointsPerDay on any one day. 'Today' is defined in local time. 221 double points_added_today_; 222 223 // The last time the score was updated for engagement. Used in conjunction 224 // with |points_added_today_| to avoid adding more than kMaxPointsPerDay on 225 // any one day. 226 base::Time last_engagement_time_; 227 228 // The last time the site with this score was launched from an installed 229 // shortcut. 230 base::Time last_shortcut_launch_time_; 231 232 // The dictionary that represents this engagement score. 233 std::unique_ptr<base::DictionaryValue> score_dict_; 234 235 // The origin this score represents. 236 GURL origin_; 237 238 // The settings to write this score to when Commit() is called. 239 HostContentSettingsMap* settings_map_; 240 241 DISALLOW_COPY_AND_ASSIGN(SiteEngagementScore); 242 }; 243 244 #endif // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_SCORE_H_ 245