| // 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_ |