blob: 3a2d029768d5ca173ba0d8c558ffd9d323977d09 [file] [log] [blame]
// Copyright 2021 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_SHARE_SHARE_RANKING_H_
#define CHROME_BROWSER_SHARE_SHARE_RANKING_H_
#include <optional>
#include "base/callback_list.h"
#include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h"
#include "base/supports_user_data.h"
#include "build/build_config.h"
#include "chrome/browser/share/proto/share_ranking_message.pb.h"
#include "chrome/browser/share/share_history.h"
#include "components/leveldb_proto/public/proto_database.h"
class Profile;
namespace sharing {
class ShareHistory;
class ShareRanking : public base::SupportsUserData::Data {
public:
using Ranking = std::vector<std::string>;
using BackingDb = leveldb_proto::ProtoDatabase<proto::ShareRanking>;
using GetRankingCallback =
base::OnceCallback<void(std::optional<Ranking> result)>;
static ShareRanking* Get(Profile* profile);
explicit ShareRanking(Profile* profile,
std::unique_ptr<BackingDb> backing_db = nullptr);
~ShareRanking() override;
void UpdateRanking(const std::string& type, Ranking ranking);
void GetRanking(const std::string& type, GetRankingCallback callback);
// This method:
// 1. Fetches the existing rankings and histories for |type| from the provided
// databases
// 2. Runs the ranking algorithm (see below).
// 3. If |persist_update|, writes the new ranking back to |ranking|
// 4. Returns the display ranking to use for this specific share
void Rank(ShareHistory* history,
const std::string& type,
const std::vector<std::string>& available_on_system,
size_t fold,
size_t length,
bool persist_update,
GetRankingCallback callback);
void Clear(const base::Time& start = base::Time(),
const base::Time& end = base::Time());
// The core of the ranking algorithm, exposed as a pure function for ease of
// testing and reasoning about the code. This function takes the existing
// share history and ranking for this type, a set of all targets available on
// the current system, and a length, and computes the new display ranking and
// the new persistent ranking.
//
// TODO(ellyjones): Document (publicly) how this works and why.
static void ComputeRanking(
const std::map<std::string, int>& all_share_history,
const std::map<std::string, int>& recent_share_history,
const Ranking& old_ranking,
const std::vector<std::string>& available_on_system,
size_t fold,
size_t length,
Ranking* display_ranking,
Ranking* persisted_ranking);
static const char* const kMoreTarget;
void set_initial_ranking_for_test(Ranking ranking) {
initial_ranking_for_test_ = ranking;
}
private:
// A PendingRankCall represents a call to Rank() that is in progress; an
// object of this type is threaded through the async calls used by Rank() to
// pass state from one step to the next.
struct PendingRankCall {
PendingRankCall();
~PendingRankCall();
// These members mirror the parameters passed in to Rank().
std::string type;
std::vector<std::string> available_on_system;
size_t fold;
size_t length;
bool persist_update;
GetRankingCallback callback;
// This is the ShareHistory passed into Rank(), held as a WeakPtr in case
// the Rank() call is pending across profile teardown.
base::WeakPtr<ShareHistory> history_db;
// These are intermediate results generated by async steps of Rank(),
// accumulated into the PendingRankCall for later use.
std::vector<ShareHistory::Target> all_history;
std::vector<ShareHistory::Target> recent_history;
};
void Init();
void OnInitDone(leveldb_proto::Enums::InitStatus status);
void OnBackingGetDone(std::string key,
GetRankingCallback callback,
bool ok,
std::unique_ptr<proto::ShareRanking> ranking);
void FlushToBackingDb(const std::string& key);
// Steps of the state machine for Rank() - in order, that method:
// 1. Fetches all history for the given type
// 2. Fetches recent history for the given type
// 3. Fetches the existing ranking for the given type
// Each of these steps is asynchronous and the On*Done methods are used for
// their completion callbacks.
void OnRankGetAllDone(std::unique_ptr<PendingRankCall> pending,
std::vector<ShareHistory::Target> history);
void OnRankGetRecentDone(std::unique_ptr<PendingRankCall> pending,
std::vector<ShareHistory::Target> history);
void OnRankGetOldRankingDone(std::unique_ptr<PendingRankCall> pending,
std::optional<Ranking> ranking);
// Return the default initial ranking, which depends on the current locale.
Ranking GetDefaultInitialRankingForType(const std::string& type);
bool init_finished_ = false;
leveldb_proto::Enums::InitStatus db_init_status_;
base::OnceClosureList post_init_callbacks_;
std::unique_ptr<BackingDb> db_;
// Cached ranking values, which are used when populated; otherwise they are
// fetched from the backing database. Updates to this are written back to the
// backing database by FlushToBackingDb().
base::flat_map<std::string, Ranking> ranking_;
std::optional<Ranking> initial_ranking_for_test_;
base::WeakPtrFactory<ShareRanking> weak_factory_{this};
};
} // namespace sharing
#endif // CHROME_BROWSER_SHARE_SHARE_RANKING_H_