blob: a6f7cd51d2eee2ffc081964bba71993474ad1c27 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_HELPER_H_
#define COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_HELPER_H_
#include <memory>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/timer/timer.h"
#include "components/no_state_prefetch/browser/no_state_prefetch_manager.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "content/public/browser/media_player_id.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
namespace content {
class NavigationHandle;
}
namespace site_engagement {
enum class EngagementType;
// Per-WebContents class to handle updating the site engagement scores for
// origins.
class SiteEngagementService::Helper
: public content::WebContentsObserver,
public content::WebContentsUserData<SiteEngagementService::Helper> {
public:
static void SetSecondsBetweenUserInputCheck(int seconds);
static void SetSecondsTrackingDelayAfterNavigation(int seconds);
static void SetSecondsTrackingDelayAfterShow(int seconds);
~Helper() override;
private:
// Class to encapsulate the periodic detection of site engagement.
//
// Engagement detection begins at some constant time delta following
// navigation, tab activation, or media starting to play. Once engagement is
// recorded, detection is suspended for another constant time delta. For sites
// to continually record engagement, this overall design requires:
//
// 1. engagement at a non-trivial time after a site loads
// 2. continual engagement over a non-trivial duration of time
class PeriodicTracker {
public:
explicit PeriodicTracker(SiteEngagementService::Helper* helper);
virtual ~PeriodicTracker();
PeriodicTracker(const PeriodicTracker&) = delete;
PeriodicTracker& operator=(const PeriodicTracker&) = delete;
// Begin tracking after |initial_delay|.
void Start(base::TimeDelta initial_delay);
// Pause tracking and restart after a delay.
void Pause();
// Stop tracking.
void Stop();
// Returns true if the timer is currently running.
bool IsTimerRunning();
// Set the timer object for testing.
void SetPauseTimerForTesting(std::unique_ptr<base::OneShotTimer> timer);
SiteEngagementService::Helper* helper() { return helper_; }
protected:
friend class SiteEngagementHelperTest;
// Called when tracking is to be paused by |delay|. Used when tracking first
// starts or is paused.
void StartTimer(base::TimeDelta delay);
// Called when the timer expires and engagement tracking is activated.
virtual void TrackingStarted() {}
// Called when engagement tracking is paused or stopped.
virtual void TrackingStopped() {}
private:
raw_ptr<SiteEngagementService::Helper> helper_;
std::unique_ptr<base::OneShotTimer> pause_timer_;
};
// Class to encapsulate time-on-site engagement detection. Time-on-site is
// recorded by detecting user input on a focused WebContents (mouse click,
// mouse wheel, keypress, or touch gesture tap) over time.
//
// After an initial delay, the input tracker begins listening to
// DidGetUserInteraction. When user input is signaled, site engagement is
// recorded, and the tracker sleeps for a delay period.
class InputTracker : public PeriodicTracker,
public content::WebContentsObserver {
public:
InputTracker(SiteEngagementService::Helper* helper,
content::WebContents* web_contents);
InputTracker(const InputTracker&) = delete;
InputTracker& operator=(const InputTracker&) = delete;
bool is_tracking() const { return is_tracking_; }
private:
friend class SiteEngagementHelperTest;
void TrackingStarted() override;
void TrackingStopped() override;
// Returns whether the tracker will respond to user input via
// DidGetUserInteraction.
bool is_tracking_;
// content::WebContentsObserver overrides.
void DidGetUserInteraction(const blink::WebInputEvent& event) override;
};
// Class to encapsulate media detection. Any media playing in a WebContents
// (focused or not) will accumulate engagement points. Media in a hidden
// WebContents will accumulate engagement more slowly than in an active
// WebContents. Media which has been muted will also accumulate engagement
// more slowly.
//
// When media begins playing in the main frame of a tab, the tracker is
// triggered with an initial delay. It then wakes up every
// |g_seconds_to_pause_engagement_detection| and notes the visible/hidden
// state of the tab, as well as whether media is still playing.
class MediaTracker : public PeriodicTracker,
public content::WebContentsObserver {
public:
MediaTracker(SiteEngagementService::Helper* helper,
content::WebContents* web_contents);
~MediaTracker() override;
MediaTracker(const MediaTracker&) = delete;
MediaTracker& operator=(const MediaTracker&) = delete;
private:
friend class SiteEngagementHelperTest;
void TrackingStarted() override;
// content::WebContentsObserver overrides.
void PrimaryPageChanged(content::Page& page) override;
void MediaStartedPlaying(const MediaPlayerInfo& media_info,
const content::MediaPlayerId& id) override;
void MediaStoppedPlaying(
const MediaPlayerInfo& media_info,
const content::MediaPlayerId& id,
WebContentsObserver::MediaStoppedReason reason) override;
std::vector<content::MediaPlayerId> active_media_players_;
};
// Optionally include |NoStatePrefetchManager| if no state prefetches are
// possible in the embedder.
explicit Helper(
content::WebContents* web_contents,
prerender::NoStatePrefetchManager* prefetch_manager = nullptr);
Helper(const Helper&) = delete;
Helper& operator=(const Helper&) = delete;
friend class content::WebContentsUserData<SiteEngagementService::Helper>;
friend class SiteEngagementHelperTest;
friend class SiteEngagementHelperBrowserTest;
// Ask the SiteEngagementService to record engagement via user input at the
// current WebContents URL.
void RecordUserInput(EngagementType type);
// Ask the SiteEngagementService to record engagement via media playing at the
// current WebContents URL.
void RecordMediaPlaying(bool is_hidden);
// content::WebContentsObserver overrides.
void DidFinishNavigation(content::NavigationHandle* handle) override;
void OnVisibilityChanged(content::Visibility visibility) override;
InputTracker input_tracker_;
MediaTracker media_tracker_;
raw_ptr<SiteEngagementService> service_;
raw_ptr<prerender::NoStatePrefetchManager> prefetch_manager_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
} // namespace site_engagement
#endif // COMPONENTS_SITE_ENGAGEMENT_CONTENT_SITE_ENGAGEMENT_HELPER_H_