#ifndef EXTENSIONS_BROWSER_API_ALARMS_ALARM_MANAGER_H_
#define EXTENSIONS_BROWSER_API_ALARMS_ALARM_MANAGER_H_
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/containers/queue.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/wall_clock_timer.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/api/alarms.h"
#include "extensions/common/extension_id.h"
namespace base {
class Clock;
}
namespace content {
class BrowserContext;
}
namespace extensions {
class ExtensionAlarmsSchedulingTest;
struct Alarm {
Alarm();
Alarm(const std::string& name,
const api::alarms::AlarmCreateInfo& create_info,
base::TimeDelta min_granularity,
base::Time now);
Alarm(Alarm&&) noexcept;
Alarm& operator=(Alarm&&) noexcept;
Alarm(const Alarm&) = delete;
Alarm& operator=(const Alarm&) = delete;
~Alarm();
std::optional<api::alarms::Alarm> js_alarm;
base::TimeDelta granularity;
base::TimeDelta minimum_granularity;
};
class AlarmManager : public BrowserContextKeyedAPI,
public ExtensionRegistryObserver {
public:
using AlarmList = std::vector<Alarm>;
static constexpr int kMaxAlarmsPerExtension = 500;
class Delegate {
public:
virtual ~Delegate() {}
virtual void OnAlarm(const ExtensionId& extension_id,
const Alarm& alarm) = 0;
};
explicit AlarmManager(content::BrowserContext* context);
AlarmManager(const AlarmManager&) = delete;
AlarmManager& operator=(const AlarmManager&) = delete;
~AlarmManager() override;
void set_delegate(std::unique_ptr<Delegate> delegate) {
delegate_ = std::move(delegate);
}
int GetCountForExtension(const ExtensionId& extension_id) const;
using AddAlarmCallback = base::OnceClosure;
void AddAlarm(const ExtensionId& extension_id,
Alarm alarm,
AddAlarmCallback callback);
using GetAlarmCallback = base::OnceCallback<void(Alarm*)>;
void GetAlarm(const ExtensionId& extension_id,
const std::string& name,
GetAlarmCallback callback);
using GetAllAlarmsCallback = base::OnceCallback<void(const AlarmList*)>;
void GetAllAlarms(const ExtensionId& extension_id,
GetAllAlarmsCallback callback);
using RemoveAlarmCallback = base::OnceCallback<void(bool)>;
void RemoveAlarm(const ExtensionId& extension_id,
const std::string& name,
RemoveAlarmCallback callback);
using RemoveAllAlarmsCallback = base::OnceClosure;
void RemoveAllAlarms(const ExtensionId& extension_id,
RemoveAllAlarmsCallback callback);
void SetClockForTesting(base::Clock* clock);
static BrowserContextKeyedAPIFactory<AlarmManager>* GetFactoryInstance();
static AlarmManager* Get(content::BrowserContext* browser_context);
private:
friend void RunScheduleNextPoll(AlarmManager*);
friend class ExtensionAlarmsSchedulingTest;
FRIEND_TEST_ALL_PREFIXES(ExtensionAlarmsSchedulingTest, PollScheduling);
FRIEND_TEST_ALL_PREFIXES(ExtensionAlarmsSchedulingTest,
ReleasedExtensionPollsInfrequently);
FRIEND_TEST_ALL_PREFIXES(ExtensionAlarmsSchedulingTest, TimerRunning);
FRIEND_TEST_ALL_PREFIXES(ExtensionAlarmsSchedulingTest, MinimumGranularity);
FRIEND_TEST_ALL_PREFIXES(ExtensionAlarmsSchedulingTest,
DifferentMinimumGranularities);
FRIEND_TEST_ALL_PREFIXES(ExtensionAlarmsSchedulingTest,
RepeatingAlarmsScheduledPredictably);
FRIEND_TEST_ALL_PREFIXES(ExtensionAlarmsSchedulingTest,
PollFrequencyFromStoredAlarm);
friend class BrowserContextKeyedAPIFactory<AlarmManager>;
using AlarmMap = std::map<ExtensionId, AlarmList>;
using ReadyAction = base::OnceCallback<void(const std::string&)>;
using ReadyQueue = base::queue<ReadyAction>;
using ReadyMap = std::map<ExtensionId, ReadyQueue>;
typedef std::pair<AlarmMap::iterator, AlarmList::iterator> AlarmIterator;
void AddAlarmWhenReady(Alarm alarm,
AddAlarmCallback callback,
const ExtensionId& extension_id);
void GetAlarmWhenReady(const std::string& name,
GetAlarmCallback callback,
const ExtensionId& extension_id);
void GetAllAlarmsWhenReady(GetAllAlarmsCallback callback,
const ExtensionId& extension_id);
void RemoveAlarmWhenReady(const std::string& name,
RemoveAlarmCallback callback,
const ExtensionId& extension_id);
void RemoveAllAlarmsWhenReady(RemoveAllAlarmsCallback callback,
const ExtensionId& extension_id);
AlarmIterator GetAlarmIterator(const ExtensionId& extension_id,
const std::string& name);
void RemoveAlarmIterator(const AlarmIterator& iter);
void OnAlarm(AlarmIterator iter);
void AddAlarmImpl(const ExtensionId& extension_id, Alarm alarm);
void WriteToStorage(const ExtensionId& extension_id);
void ReadFromStorage(const ExtensionId& extension_id,
base::TimeDelta min_delay,
std::optional<base::Value> value);
void SetNextPollTime(const base::Time& time);
void ScheduleNextPoll();
void PollAlarms();
void RunWhenReady(const ExtensionId& extension_id, ReadyAction action);
void OnExtensionLoaded(content::BrowserContext* browser_context,
const Extension* extension) override;
void OnExtensionUninstalled(content::BrowserContext* browser_context,
const Extension* extension,
extensions::UninstallReason reason) override;
static const char* service_name() { return "AlarmManager"; }
static const bool kServiceHasOwnInstanceInIncognito = true;
const raw_ptr<content::BrowserContext> browser_context_;
raw_ptr<base::Clock> clock_;
std::unique_ptr<Delegate> delegate_;
base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver>
extension_registry_observation_{this};
base::WallClockTimer timer_;
AlarmMap alarms_;
ReadyMap ready_actions_;
base::Time last_poll_time_;
base::Time next_poll_time_;
base::WeakPtrFactory<AlarmManager> weak_ptr_factory_{this};
};
}
#endif