910e62b5创建于 1月15日历史提交
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_TABS_ORGANIZATION_TRIGGER_POLICIES_H_
#define CHROME_BROWSER_UI_TABS_ORGANIZATION_TRIGGER_POLICIES_H_

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
#include "chrome/browser/ui/tabs/organization/trigger.h"

namespace content {
class BrowserContext;
}
class PrefService;

// We want to parameterize trigger policies with things like target triggering
// frequencies. That begs the question - how should we define those frequencies?
// Once per week? Once every 8 hours of active chrome usage? Once per 200 tabs
// opened?

// A simple way to model this is with clocks that measure time differently. Some
// clock options, in increasing complexity order:

// 1. Wall time. Simple and predictable, but could spam users who don't often
// use Chrome and under-serve users who use Chrome frequently.

// 2. Chrome foreground time, implemented below. Still fairly simple, and maps
// better to actual usage, but fails for cases like streaming a movie or leaving
// a computer on overnight.

// 3. Number of browser actions of some kind, e.g. page loads. Weirder to think
// about, some risk of degenerate behavior for unusual usage patterns, but can
// map very directly to e.g. tabstrip usage.

// 4. The above, but deduplicate events performed in quick succession. This
// effectively amounts to defining our own notion of 'active tabstrip time'.
// Fixes the issues with 3 but might just be overkill.

// A clock that runs only while Chrome is in the foreground. See also
// chrome/browser/resource_coordinator/usage_clock.h which implements the same
// concept in a resource_coordinator specific way.
class UsageTickClock final : public base::TickClock,
                             metrics::DesktopSessionDurationTracker::Observer {
 public:
  explicit UsageTickClock(const base::TickClock* base_clock);

  UsageTickClock(const UsageTickClock&) = delete;
  UsageTickClock& operator=(const UsageTickClock&) = delete;

  ~UsageTickClock() override;

  base::TimeTicks NowTicks() const override;

 private:
  void OnSessionStarted(base::TimeTicks session_start) override;
  void OnSessionEnded(base::TimeDelta session_length,
                      base::TimeTicks session_end) override;

  const raw_ptr<const base::TickClock> base_clock_;
  const base::TimeTicks start_time_;

  base::TimeDelta usage_time_in_completed_sessions_ = base::TimeDelta();
  std::optional<base::TimeTicks> current_usage_session_start_time_ =
      std::nullopt;
};

class BackoffLevelProvider {
 public:
  virtual ~BackoffLevelProvider() = default;
  virtual unsigned int Get() const = 0;
  virtual void Increment() = 0;
  virtual void Decrement() = 0;
};

class ProfilePrefBackoffLevelProvider final : public BackoffLevelProvider {
 public:
  explicit ProfilePrefBackoffLevelProvider(content::BrowserContext* context);
  ~ProfilePrefBackoffLevelProvider() override;

  // BackoffLevelProvider:
  unsigned int Get() const override;
  void Increment() override;
  void Decrement() override;

 private:
  raw_ptr<PrefService> prefs_;
};

// A policy which triggers up to once per period, based on the classic solution
// to the secretary problem. Has an observation phase and a trigger phase.
// During the observation phase, it keeps track of the best score seen so far.
// During the trigger phase, it triggers the first time the best score from the
// observation phase is beaten.
//
// For any given period, it has a 1/e chance to not trigger at all, but the rest
// of the time it will likely trigger on a very good moment, relative to the
// other moments in this period.
class TargetFrequencyTriggerPolicy final : public TriggerPolicy {
 public:
  TargetFrequencyTriggerPolicy(std::unique_ptr<base::TickClock> clock,
                               base::TimeDelta base_period,
                               float backoff_base,
                               BackoffLevelProvider* backoff_level_provider);
  ~TargetFrequencyTriggerPolicy() override;
  bool ShouldTrigger(float score) override;
  void OnTriggerSucceeded();
  void OnTriggerFailed();

 private:
  const std::unique_ptr<base::TickClock> clock_;
  const base::TimeDelta base_period_;
  const float backoff_base_;
  const raw_ptr<BackoffLevelProvider> backoff_level_provider_;

  base::TimeTicks cycle_start_time_;
  std::optional<float> best_score = std::nullopt;
  bool has_triggered_ = false;
};

// Never trigger. Useful for disabling the trigger under certain conditions.
class NeverTriggerPolicy final : public TriggerPolicy {
 public:
  bool ShouldTrigger(float score) override;
};

// Trigger every time. Very spammy, but suitable for testing or demoing.
class DemoTriggerPolicy final : public TriggerPolicy {
 public:
  bool ShouldTrigger(float score) override;
};

#endif  // CHROME_BROWSER_UI_TABS_ORGANIZATION_TRIGGER_POLICIES_H_