| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/engagement/site_engagement_helper.h" | 5 #include "chrome/browser/engagement/site_engagement_helper.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/time/time.h" | 9 #include "base/time/time.h" |
| 10 #include "base/trace_event/trace_event.h" | 10 #include "base/trace_event/trace_event.h" |
| 11 #include "chrome/browser/engagement/site_engagement_service.h" | 11 #include "chrome/browser/engagement/site_engagement_service.h" |
| 12 #include "chrome/browser/engagement/site_engagement_service_factory.h" | |
| 13 #include "chrome/browser/prerender/prerender_contents.h" | 12 #include "chrome/browser/prerender/prerender_contents.h" |
| 14 #include "chrome/browser/profiles/profile.h" | 13 #include "chrome/browser/profiles/profile.h" |
| 15 #include "content/public/browser/navigation_handle.h" | 14 #include "content/public/browser/navigation_handle.h" |
| 16 #include "content/public/browser/web_contents.h" | 15 #include "content/public/browser/web_contents.h" |
| 17 | 16 |
| 18 namespace { | 17 namespace { |
| 19 | 18 |
| 20 int g_seconds_to_pause_engagement_detection = 10; | 19 int g_seconds_to_pause_engagement_detection = 10; |
| 21 int g_seconds_delay_after_navigation = 10; | 20 int g_seconds_delay_after_navigation = 10; |
| 22 int g_seconds_delay_after_media_starts = 10; | 21 int g_seconds_delay_after_media_starts = 10; |
| 23 int g_seconds_delay_after_show = 5; | 22 int g_seconds_delay_after_show = 5; |
| 24 | 23 |
| 25 } // anonymous namespace | 24 } // anonymous namespace |
| 26 | 25 |
| 27 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SiteEngagementHelper); | 26 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SiteEngagementService::Helper); |
| 28 | 27 |
| 29 SiteEngagementHelper::PeriodicTracker::PeriodicTracker( | 28 SiteEngagementService::Helper::PeriodicTracker::PeriodicTracker( |
| 30 SiteEngagementHelper* helper) | 29 SiteEngagementService::Helper* helper) |
| 31 : helper_(helper), pause_timer_(new base::Timer(true, false)) {} | 30 : helper_(helper), pause_timer_(new base::Timer(true, false)) {} |
| 32 | 31 |
| 33 SiteEngagementHelper::PeriodicTracker::~PeriodicTracker() {} | 32 SiteEngagementService::Helper::PeriodicTracker::~PeriodicTracker() {} |
| 34 | 33 |
| 35 void SiteEngagementHelper::PeriodicTracker::Start( | 34 void SiteEngagementService::Helper::PeriodicTracker::Start( |
| 36 base::TimeDelta initial_delay) { | 35 base::TimeDelta initial_delay) { |
| 37 StartTimer(initial_delay); | 36 StartTimer(initial_delay); |
| 38 } | 37 } |
| 39 | 38 |
| 40 void SiteEngagementHelper::PeriodicTracker::Pause() { | 39 void SiteEngagementService::Helper::PeriodicTracker::Pause() { |
| 41 TrackingStopped(); | 40 TrackingStopped(); |
| 42 StartTimer( | 41 StartTimer( |
| 43 base::TimeDelta::FromSeconds(g_seconds_to_pause_engagement_detection)); | 42 base::TimeDelta::FromSeconds(g_seconds_to_pause_engagement_detection)); |
| 44 } | 43 } |
| 45 | 44 |
| 46 void SiteEngagementHelper::PeriodicTracker::Stop() { | 45 void SiteEngagementService::Helper::PeriodicTracker::Stop() { |
| 47 TrackingStopped(); | 46 TrackingStopped(); |
| 48 pause_timer_->Stop(); | 47 pause_timer_->Stop(); |
| 49 } | 48 } |
| 50 | 49 |
| 51 bool SiteEngagementHelper::PeriodicTracker::IsTimerRunning() { | 50 bool SiteEngagementService::Helper::PeriodicTracker::IsTimerRunning() { |
| 52 return pause_timer_->IsRunning(); | 51 return pause_timer_->IsRunning(); |
| 53 } | 52 } |
| 54 | 53 |
| 55 void SiteEngagementHelper::PeriodicTracker::SetPauseTimerForTesting( | 54 void SiteEngagementService::Helper::PeriodicTracker::SetPauseTimerForTesting( |
| 56 std::unique_ptr<base::Timer> timer) { | 55 std::unique_ptr<base::Timer> timer) { |
| 57 pause_timer_ = std::move(timer); | 56 pause_timer_ = std::move(timer); |
| 58 } | 57 } |
| 59 | 58 |
| 60 void SiteEngagementHelper::PeriodicTracker::StartTimer( | 59 void SiteEngagementService::Helper::PeriodicTracker::StartTimer( |
| 61 base::TimeDelta delay) { | 60 base::TimeDelta delay) { |
| 62 pause_timer_->Start( | 61 pause_timer_->Start( |
| 63 FROM_HERE, delay, | 62 FROM_HERE, delay, |
| 64 base::Bind(&SiteEngagementHelper::PeriodicTracker::TrackingStarted, | 63 base::Bind( |
| 65 base::Unretained(this))); | 64 &SiteEngagementService::Helper::PeriodicTracker::TrackingStarted, |
| 65 base::Unretained(this))); |
| 66 } | 66 } |
| 67 | 67 |
| 68 SiteEngagementHelper::InputTracker::InputTracker( | 68 SiteEngagementService::Helper::InputTracker::InputTracker( |
| 69 SiteEngagementHelper* helper, | 69 SiteEngagementService::Helper* helper, |
| 70 content::WebContents* web_contents) | 70 content::WebContents* web_contents) |
| 71 : PeriodicTracker(helper), | 71 : PeriodicTracker(helper), |
| 72 content::WebContentsObserver(web_contents), | 72 content::WebContentsObserver(web_contents), |
| 73 is_tracking_(false) {} | 73 is_tracking_(false) {} |
| 74 | 74 |
| 75 void SiteEngagementHelper::InputTracker::TrackingStarted() { | 75 void SiteEngagementService::Helper::InputTracker::TrackingStarted() { |
| 76 is_tracking_ = true; | 76 is_tracking_ = true; |
| 77 } | 77 } |
| 78 | 78 |
| 79 void SiteEngagementHelper::InputTracker::TrackingStopped() { | 79 void SiteEngagementService::Helper::InputTracker::TrackingStopped() { |
| 80 is_tracking_ = false; | 80 is_tracking_ = false; |
| 81 } | 81 } |
| 82 | 82 |
| 83 // Record that there was some user input, and defer handling of the input event. | 83 // Record that there was some user input, and defer handling of the input event. |
| 84 // Once the timer finishes running, the callbacks detecting user input will be | 84 // Once the timer finishes running, the callbacks detecting user input will be |
| 85 // registered again. | 85 // registered again. |
| 86 void SiteEngagementHelper::InputTracker::DidGetUserInteraction( | 86 void SiteEngagementService::Helper::InputTracker::DidGetUserInteraction( |
| 87 const blink::WebInputEvent::Type type) { | 87 const blink::WebInputEvent::Type type) { |
| 88 // Only respond to raw key down to avoid multiple triggering on a single input | 88 // Only respond to raw key down to avoid multiple triggering on a single input |
| 89 // (e.g. keypress is a key down then key up). | 89 // (e.g. keypress is a key down then key up). |
| 90 if (!is_tracking_) | 90 if (!is_tracking_) |
| 91 return; | 91 return; |
| 92 | 92 |
| 93 // This switch has a default NOTREACHED case because it will not test all | 93 // This switch has a default NOTREACHED case because it will not test all |
| 94 // of the values of the WebInputEvent::Type enum (hence it won't require the | 94 // of the values of the WebInputEvent::Type enum (hence it won't require the |
| 95 // compiler verifying that all cases are covered). | 95 // compiler verifying that all cases are covered). |
| 96 switch (type) { | 96 switch (type) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 109 break; | 109 break; |
| 110 case blink::WebInputEvent::Undefined: | 110 case blink::WebInputEvent::Undefined: |
| 111 // Explicitly ignore browser-initiated navigation input. | 111 // Explicitly ignore browser-initiated navigation input. |
| 112 break; | 112 break; |
| 113 default: | 113 default: |
| 114 NOTREACHED(); | 114 NOTREACHED(); |
| 115 } | 115 } |
| 116 Pause(); | 116 Pause(); |
| 117 } | 117 } |
| 118 | 118 |
| 119 SiteEngagementHelper::MediaTracker::MediaTracker( | 119 SiteEngagementService::Helper::MediaTracker::MediaTracker( |
| 120 SiteEngagementHelper* helper, | 120 SiteEngagementService::Helper* helper, |
| 121 content::WebContents* web_contents) | 121 content::WebContents* web_contents) |
| 122 : PeriodicTracker(helper), | 122 : PeriodicTracker(helper), |
| 123 content::WebContentsObserver(web_contents), | 123 content::WebContentsObserver(web_contents), |
| 124 is_hidden_(false) {} | 124 is_hidden_(false) {} |
| 125 | 125 |
| 126 SiteEngagementHelper::MediaTracker::~MediaTracker() {} | 126 SiteEngagementService::Helper::MediaTracker::~MediaTracker() {} |
| 127 | 127 |
| 128 void SiteEngagementHelper::MediaTracker::TrackingStarted() { | 128 void SiteEngagementService::Helper::MediaTracker::TrackingStarted() { |
| 129 if (!active_media_players_.empty()) | 129 if (!active_media_players_.empty()) |
| 130 helper()->RecordMediaPlaying(is_hidden_); | 130 helper()->RecordMediaPlaying(is_hidden_); |
| 131 | 131 |
| 132 Pause(); | 132 Pause(); |
| 133 } | 133 } |
| 134 | 134 |
| 135 void SiteEngagementHelper::MediaTracker::MediaStartedPlaying( | 135 void SiteEngagementService::Helper::MediaTracker::MediaStartedPlaying( |
| 136 const MediaPlayerId& id) { | 136 const MediaPlayerId& id) { |
| 137 // Only begin engagement detection when media actually starts playing. | 137 // Only begin engagement detection when media actually starts playing. |
| 138 active_media_players_.push_back(id); | 138 active_media_players_.push_back(id); |
| 139 if (!IsTimerRunning()) | 139 if (!IsTimerRunning()) |
| 140 Start(base::TimeDelta::FromSeconds(g_seconds_delay_after_media_starts)); | 140 Start(base::TimeDelta::FromSeconds(g_seconds_delay_after_media_starts)); |
| 141 } | 141 } |
| 142 | 142 |
| 143 void SiteEngagementHelper::MediaTracker::MediaStoppedPlaying( | 143 void SiteEngagementService::Helper::MediaTracker::MediaStoppedPlaying( |
| 144 const MediaPlayerId& id) { | 144 const MediaPlayerId& id) { |
| 145 active_media_players_.erase(std::remove(active_media_players_.begin(), | 145 active_media_players_.erase(std::remove(active_media_players_.begin(), |
| 146 active_media_players_.end(), id), | 146 active_media_players_.end(), id), |
| 147 active_media_players_.end()); | 147 active_media_players_.end()); |
| 148 } | 148 } |
| 149 | 149 |
| 150 void SiteEngagementHelper::MediaTracker::WasShown() { | 150 void SiteEngagementService::Helper::MediaTracker::WasShown() { |
| 151 is_hidden_ = false; | 151 is_hidden_ = false; |
| 152 } | 152 } |
| 153 | 153 |
| 154 void SiteEngagementHelper::MediaTracker::WasHidden() { | 154 void SiteEngagementService::Helper::MediaTracker::WasHidden() { |
| 155 is_hidden_ = true; | 155 is_hidden_ = true; |
| 156 } | 156 } |
| 157 | 157 |
| 158 SiteEngagementHelper::~SiteEngagementHelper() { | 158 SiteEngagementService::Helper::~Helper() { |
| 159 if (web_contents()) { | 159 if (web_contents()) { |
| 160 input_tracker_.Stop(); | 160 input_tracker_.Stop(); |
| 161 media_tracker_.Stop(); | 161 media_tracker_.Stop(); |
| 162 } | 162 } |
| 163 } | 163 } |
| 164 | 164 |
| 165 SiteEngagementHelper::SiteEngagementHelper(content::WebContents* web_contents) | 165 SiteEngagementService::Helper::Helper(content::WebContents* web_contents) |
| 166 : content::WebContentsObserver(web_contents), | 166 : content::WebContentsObserver(web_contents), |
| 167 input_tracker_(this, web_contents), | 167 input_tracker_(this, web_contents), |
| 168 media_tracker_(this, web_contents), | 168 media_tracker_(this, web_contents), |
| 169 record_engagement_(false) {} | 169 record_engagement_(false) {} |
| 170 | 170 |
| 171 void SiteEngagementHelper::RecordUserInput( | 171 void SiteEngagementService::Helper::RecordUserInput( |
| 172 SiteEngagementMetrics::EngagementType type) { | 172 SiteEngagementMetrics::EngagementType type) { |
| 173 TRACE_EVENT0("SiteEngagement", "RecordUserInput"); | 173 TRACE_EVENT0("SiteEngagement", "RecordUserInput"); |
| 174 content::WebContents* contents = web_contents(); | 174 content::WebContents* contents = web_contents(); |
| 175 if (contents) { | 175 if (contents) { |
| 176 Profile* profile = | 176 Profile* profile = |
| 177 Profile::FromBrowserContext(contents->GetBrowserContext()); | 177 Profile::FromBrowserContext(contents->GetBrowserContext()); |
| 178 SiteEngagementService* service = | 178 SiteEngagementService* service = SiteEngagementService::Get(profile); |
| 179 SiteEngagementServiceFactory::GetForProfile(profile); | |
| 180 | 179 |
| 181 // Service is null in incognito. | 180 // Service is null in incognito. |
| 182 if (service) | 181 if (service) |
| 183 service->HandleUserInput(contents->GetVisibleURL(), type); | 182 service->HandleUserInput(contents->GetVisibleURL(), type); |
| 184 } | 183 } |
| 185 } | 184 } |
| 186 | 185 |
| 187 void SiteEngagementHelper::RecordMediaPlaying(bool is_hidden) { | 186 void SiteEngagementService::Helper::RecordMediaPlaying(bool is_hidden) { |
| 188 content::WebContents* contents = web_contents(); | 187 content::WebContents* contents = web_contents(); |
| 189 if (contents) { | 188 if (contents) { |
| 190 Profile* profile = | 189 Profile* profile = |
| 191 Profile::FromBrowserContext(contents->GetBrowserContext()); | 190 Profile::FromBrowserContext(contents->GetBrowserContext()); |
| 192 SiteEngagementService* service = | 191 SiteEngagementService* service = SiteEngagementService::Get(profile); |
| 193 SiteEngagementServiceFactory::GetForProfile(profile); | |
| 194 | 192 |
| 195 if (service) | 193 if (service) |
| 196 service->HandleMediaPlaying(contents->GetVisibleURL(), is_hidden); | 194 service->HandleMediaPlaying(contents->GetVisibleURL(), is_hidden); |
| 197 } | 195 } |
| 198 } | 196 } |
| 199 | 197 |
| 200 void SiteEngagementHelper::DidFinishNavigation( | 198 void SiteEngagementService::Helper::DidFinishNavigation( |
| 201 content::NavigationHandle* handle) { | 199 content::NavigationHandle* handle) { |
| 202 input_tracker_.Stop(); | 200 input_tracker_.Stop(); |
| 203 media_tracker_.Stop(); | 201 media_tracker_.Stop(); |
| 204 | 202 |
| 205 // Ignore all schemes except HTTP and HTTPS, as well as uncommitted, non | 203 // Ignore all schemes except HTTP and HTTPS, as well as uncommitted, non |
| 206 // main-frame, same page, or error page navigations. | 204 // main-frame, same page, or error page navigations. |
| 207 record_engagement_ = handle->GetURL().SchemeIsHTTPOrHTTPS(); | 205 record_engagement_ = handle->GetURL().SchemeIsHTTPOrHTTPS(); |
| 208 if (!handle->HasCommitted() || !handle->IsInMainFrame() || | 206 if (!handle->HasCommitted() || !handle->IsInMainFrame() || |
| 209 handle->IsSamePage() || handle->IsErrorPage() || !record_engagement_) { | 207 handle->IsSamePage() || handle->IsErrorPage() || !record_engagement_) { |
| 210 return; | 208 return; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 221 // always assigned the LINK transition, which is ignored for navigation | 219 // always assigned the LINK transition, which is ignored for navigation |
| 222 // engagement. | 220 // engagement. |
| 223 // | 221 // |
| 224 // Prerenders trigger WasShown() when they are swapped in, so input engagement | 222 // Prerenders trigger WasShown() when they are swapped in, so input engagement |
| 225 // will activate even if navigation engagement is not scored. | 223 // will activate even if navigation engagement is not scored. |
| 226 if (prerender::PrerenderContents::FromWebContents(web_contents()) != nullptr) | 224 if (prerender::PrerenderContents::FromWebContents(web_contents()) != nullptr) |
| 227 return; | 225 return; |
| 228 | 226 |
| 229 Profile* profile = | 227 Profile* profile = |
| 230 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); | 228 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); |
| 231 SiteEngagementService* service = | 229 SiteEngagementService* service = SiteEngagementService::Get(profile); |
| 232 SiteEngagementServiceFactory::GetForProfile(profile); | |
| 233 | 230 |
| 234 if (service) | 231 if (service) |
| 235 service->HandleNavigation(handle->GetURL(), handle->GetPageTransition()); | 232 service->HandleNavigation(handle->GetURL(), handle->GetPageTransition()); |
| 236 | 233 |
| 237 input_tracker_.Start( | 234 input_tracker_.Start( |
| 238 base::TimeDelta::FromSeconds(g_seconds_delay_after_navigation)); | 235 base::TimeDelta::FromSeconds(g_seconds_delay_after_navigation)); |
| 239 } | 236 } |
| 240 | 237 |
| 241 void SiteEngagementHelper::WasShown() { | 238 void SiteEngagementService::Helper::WasShown() { |
| 242 // Ensure that the input callbacks are registered when we come into view. This | 239 // Ensure that the input callbacks are registered when we come into view. |
| 243 // occurs when switching tabs, or when prerendered pages are swapped in. | |
| 244 if (record_engagement_) { | 240 if (record_engagement_) { |
| 245 input_tracker_.Start( | 241 input_tracker_.Start( |
| 246 base::TimeDelta::FromSeconds(g_seconds_delay_after_show)); | 242 base::TimeDelta::FromSeconds(g_seconds_delay_after_show)); |
| 247 } | 243 } |
| 248 } | 244 } |
| 249 | 245 |
| 250 void SiteEngagementHelper::WasHidden() { | 246 void SiteEngagementService::Helper::WasHidden() { |
| 251 // Ensure that the input callbacks are not registered when hidden. | 247 // Ensure that the input callbacks are not registered when hidden. |
| 252 input_tracker_.Stop(); | 248 input_tracker_.Stop(); |
| 253 } | 249 } |
| 254 | 250 |
| 255 // static | 251 // static |
| 256 void SiteEngagementHelper::SetSecondsBetweenUserInputCheck(int seconds) { | 252 void SiteEngagementService::Helper::SetSecondsBetweenUserInputCheck( |
| 253 int seconds) { |
| 257 g_seconds_to_pause_engagement_detection = seconds; | 254 g_seconds_to_pause_engagement_detection = seconds; |
| 258 } | 255 } |
| 259 | 256 |
| 260 // static | 257 // static |
| 261 void SiteEngagementHelper::SetSecondsTrackingDelayAfterNavigation(int seconds) { | 258 void SiteEngagementService::Helper::SetSecondsTrackingDelayAfterNavigation( |
| 259 int seconds) { |
| 262 g_seconds_delay_after_navigation = seconds; | 260 g_seconds_delay_after_navigation = seconds; |
| 263 } | 261 } |
| 264 | 262 |
| 265 // static | 263 // static |
| 266 void SiteEngagementHelper::SetSecondsTrackingDelayAfterShow(int seconds) { | 264 void SiteEngagementService::Helper::SetSecondsTrackingDelayAfterShow( |
| 265 int seconds) { |
| 267 g_seconds_delay_after_show = seconds; | 266 g_seconds_delay_after_show = seconds; |
| 268 } | 267 } |
| OLD | NEW |