| 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..a1f83736780589f22a5bc6319c84fc925f902727 100644
|
| --- a/chrome/browser/engagement/site_engagement_helper.cc
|
| +++ b/chrome/browser/engagement/site_engagement_helper.cc
|
| @@ -4,6 +4,8 @@
|
|
|
| #include "chrome/browser/engagement/site_engagement_helper.h"
|
|
|
| +#include "base/time/time.h"
|
| +#include "base/trace_event/trace_event.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 +13,196 @@
|
| #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::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() {
|
| + TRACE_EVENT0("SiteEngagement", "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));
|
| + }
|
| + }
|
| +}
|
| +
|
| +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;
|
| }
|
|
|