Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(887)

Unified Diff: chrome/browser/engagement/site_engagement_helper.cc

Issue 1338603002: Implement a site engagement score based on time-on-site. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressing reviewer feedback Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/engagement/site_engagement_helper.cc
diff --git a/chrome/browser/engagement/site_engagement_helper.cc b/chrome/browser/engagement/site_engagement_helper.cc
index b107edf030c458bb1d9803395d91ab0dbc7652c6..a5022c1aa9697d5210f9b17a49847b9fc13db06b 100644
--- a/chrome/browser/engagement/site_engagement_helper.cc
+++ b/chrome/browser/engagement/site_engagement_helper.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/engagement/site_engagement_helper.h"
+#include "base/time/time.h"
#include "chrome/browser/engagement/site_engagement_service.h"
#include "chrome/browser/engagement/site_engagement_service_factory.h"
#include "chrome/browser/prerender/prerender_contents.h"
@@ -11,36 +12,195 @@
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
+namespace {
+
+double g_seconds_between_user_input_check = 10;
+bool g_disable_callback_registration_for_testing = false;
+
+} // anonymous namespace
+
DEFINE_WEB_CONTENTS_USER_DATA_KEY(SiteEngagementHelper);
+SiteEngagementHelper::InputTracker::InputTracker(SiteEngagementHelper* helper)
+ : helper_(helper),
+ pause_timer_(new base::Timer(true, false)),
+ callbacks_added_(false) {
+ key_press_event_callback_ =
+ base::Bind(&SiteEngagementHelper::InputTracker::HandleKeyPressEvent,
+ base::Unretained(this));
+ mouse_event_callback_ =
+ base::Bind(&SiteEngagementHelper::InputTracker::HandleMouseEvent,
+ base::Unretained(this));
+}
+
+SiteEngagementHelper::InputTracker::~InputTracker() { }
+
+// Record that there was some user input, and defer handling of the input event.
+// web_contents() will return nullptr if the observed contents have been
+// deleted; if the contents exist, record engagement for the site. Once the
+// timer finishes running, the callbacks detecting user input will be registered
+// again.
+bool SiteEngagementHelper::InputTracker::HandleKeyPressEvent(
+ const content::NativeWebKeyboardEvent& event) {
+ // Only respond to raw key down to avoid multiple triggering on a single input
+ // (e.g. keypress is a key down then key up).
+ if (event.type == blink::WebInputEvent::RawKeyDown) {
+ PauseTracking(helper_->web_contents()->GetRenderViewHost());
+ helper_->RecordUserInput();
+ }
+ return false;
+}
+
+bool SiteEngagementHelper::InputTracker::HandleMouseEvent(
+ const blink::WebMouseEvent& event) {
+ // Only respond to mouse down with a button or mouse move events (e.g. a click
+ // is a mouse down and mouse up) to avoid cases where multiple events come in
+ // before we can pause tracking.
+ if ((event.button != blink::WebMouseEvent::ButtonNone &&
+ event.type == blink::WebInputEvent::MouseDown) ||
+ event.type == blink::WebInputEvent::MouseMove ||
jdduke (slow) 2015/09/24 15:13:25 I'm not sure MouseMove's without an active button
dominickn 2015/09/25 00:08:56 Done, though I think it's also important to listen
+ event.type == blink::WebInputEvent::MouseWheel) {
+ PauseTracking(helper_->web_contents()->GetRenderViewHost());
+ helper_->RecordUserInput();
+ }
+ return false;
+}
+
+void SiteEngagementHelper::InputTracker::StartTracking(
+ content::RenderViewHost* host) {
+ if (!callbacks_added_ && !g_disable_callback_registration_for_testing) {
+ host->AddKeyPressEventCallback(key_press_event_callback_);
+ host->AddMouseEventCallback(mouse_event_callback_);
+ callbacks_added_ = true;
+ }
+}
+
+void SiteEngagementHelper::InputTracker::PauseTracking(
+ content::RenderViewHost* host) {
+ StopTracking(host);
+ pause_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(
+ g_seconds_between_user_input_check),
+ base::Bind(&SiteEngagementHelper::InputTracker::ResumeTracking,
+ base::Unretained(this)));
+}
+
+void SiteEngagementHelper::InputTracker::ResumeTracking() {
+ content::WebContents* contents = helper_->web_contents();
+ if (contents)
+ StartTracking(contents->GetRenderViewHost());
+}
+
+void SiteEngagementHelper::InputTracker::StopTracking(
+ content::RenderViewHost* host) {
+ pause_timer_->Stop();
+ if (callbacks_added_) {
+ host->RemoveKeyPressEventCallback(key_press_event_callback_);
+ host->RemoveMouseEventCallback(mouse_event_callback_);
+ callbacks_added_ = false;
+ }
+}
+
+void SiteEngagementHelper::InputTracker::SetTimerForTesting(
+ scoped_ptr<base::Timer> timer) {
+ pause_timer_ = timer.Pass();
+}
+
SiteEngagementHelper::~SiteEngagementHelper() {
+ content::WebContents* contents = web_contents();
+ if (contents)
+ input_tracker_->StopTracking(contents->GetRenderViewHost());
}
-SiteEngagementHelper::SiteEngagementHelper(content::WebContents* web_contents)
- : content::WebContentsObserver(web_contents) {
+void SiteEngagementHelper::AddObserverForTesting(Observer* observer) {
+ observer_list_.AddObserver(observer);
}
-void SiteEngagementHelper::DidStartNavigationToPendingEntry(
- const GURL& url,
- content::NavigationController::ReloadType reload_type) {
- prerender::PrerenderContents* prerender_contents =
- prerender::PrerenderContents::FromWebContents(web_contents());
+void SiteEngagementHelper::RemoveObserverForTesting(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
- // Ignore pre-render loads.
- if (prerender_contents != NULL)
- return;
+SiteEngagementHelper::SiteEngagementHelper(content::WebContents* contents)
+ : content::WebContentsObserver(contents),
+ input_tracker_(new InputTracker(this)),
+ record_engagement_(false) { }
+
+void SiteEngagementHelper::RecordUserInput() {
+ content::WebContents* contents = web_contents();
+ if (contents) {
+ Profile* profile =
+ Profile::FromBrowserContext(contents->GetBrowserContext());
+ SiteEngagementService* service =
+ SiteEngagementServiceFactory::GetForProfile(profile);
+
+ // Service is null in incognito.
+ if (service) {
+ service->HandleUserInput(contents->GetVisibleURL());
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnInputRecorded(this));
jdduke (slow) 2015/09/24 15:17:35 Any idea what the potential costs are here? Is it
dominickn 2015/09/25 00:08:56 Do you mean the observer or the cost of recording
+ }
+ }
+}
+
+void SiteEngagementHelper::DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) {
+ content::WebContents* contents = web_contents();
+ input_tracker_->StopTracking(contents->GetRenderViewHost());
// Ignore all schemes except HTTP and HTTPS.
- if (!url.SchemeIsHTTPOrHTTPS())
+ record_engagement_ = params.url.SchemeIsHTTPOrHTTPS();
+
+ if (!record_engagement_)
return;
Profile* profile =
- Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+ Profile::FromBrowserContext(contents->GetBrowserContext());
SiteEngagementService* service =
SiteEngagementServiceFactory::GetForProfile(profile);
- // Service is null in incognito.
- if (!service)
- return;
- service->HandleNavigation(url);
+ if (service)
+ service->HandleNavigation(params.url, params.transition);
+
+ input_tracker_->StartTracking(contents->GetRenderViewHost());
+}
+
+void SiteEngagementHelper::RenderViewHostChanged(
+ content::RenderViewHost* old_host,
+ content::RenderViewHost* new_host) {
+ // On changing the render view host, we need to re-register the callbacks
+ // listening for user input.
+ if (record_engagement_) {
+ if (old_host)
+ input_tracker_->StopTracking(old_host);
+ input_tracker_->StartTracking(new_host);
+ }
+}
+
+void SiteEngagementHelper::WasShown() {
+ // Ensure that the input callbacks are registered when we come into view.
+ if (record_engagement_)
+ input_tracker_->StartTracking(web_contents()->GetRenderViewHost());
+}
+
+void SiteEngagementHelper::WasHidden() {
+ // Ensure that the input callbacks are not registered when hidden.
+ if (record_engagement_) {
+ content::WebContents* contents = web_contents();
+ if (contents)
+ input_tracker_->StopTracking(contents->GetRenderViewHost());
+ }
+}
+
+// static
+void SiteEngagementHelper::SetSecondsBetweenUserInputCheck(double seconds) {
+ g_seconds_between_user_input_check = seconds;
+}
+
+// static
+void SiteEngagementHelper::EnableCallbackRegistrationForTesting() {
+ g_disable_callback_registration_for_testing = false;
+}
+
+// static
+void SiteEngagementHelper::DisableCallbackRegistrationForTesting() {
+ g_disable_callback_registration_for_testing = true;
}
« no previous file with comments | « chrome/browser/engagement/site_engagement_helper.h ('k') | chrome/browser/engagement/site_engagement_service.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698