1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef COMPONENTS_FEED_CONTENT_FEED_OFFLINE_HOST_H_
6 #define COMPONENTS_FEED_CONTENT_FEED_OFFLINE_HOST_H_
7 
8 #include <string>
9 #include <vector>
10 
11 #include "base/callback.h"
12 #include "base/containers/flat_map.h"
13 #include "base/macros.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/optional.h"
16 #include "base/sequenced_task_runner.h"
17 #include "components/feed/core/content_metadata.h"
18 #include "components/offline_pages/core/offline_page_model.h"
19 #include "components/offline_pages/core/prefetch/suggestions_provider.h"
20 
21 class GURL;
22 
23 namespace offline_pages {
24 class PrefetchService;
25 }  // namespace offline_pages
26 
27 namespace feed {
28 
29 // Responsible for wiring up connections for Feed operations pertaining to
30 // articles that can be loaded from Offline Pages component. Most significantly
31 // this class connects Prefetch and the Feed, and tracks offlined articles the
32 // Feed may have badged for this user. This knowledge is later used when Feed
33 // articles are opened to populate load params.
34 class FeedOfflineHost : public offline_pages::SuggestionsProvider,
35                         public offline_pages::OfflinePageModel::Observer {
36  public:
37   using GetKnownContentCallback =
38       base::OnceCallback<void(std::vector<ContentMetadata>)>;
39   using NotifyStatusChangeCallback =
40       base::RepeatingCallback<void(const std::string&, bool)>;
41 
42   FeedOfflineHost(offline_pages::OfflinePageModel* offline_page_model,
43                   offline_pages::PrefetchService* prefetch_service,
44                   base::RepeatingClosure on_suggestion_consumed,
45                   base::RepeatingClosure on_suggestions_shown);
46   ~FeedOfflineHost() override;
47 
48   // Initialize with callbacks to call into bridge/Java side. Should only be
49   // called once, and done as soon as the bridge is ready. The FeedOfflineHost
50   // will not be fully ready to perform its function without these dependencies.
51   // Neither of these callbacks will be invoked until after this method exits.
52   void Initialize(const base::RepeatingClosure& trigger_get_known_content,
53                   const NotifyStatusChangeCallback& notify_status_change);
54 
55   // Called during initialization make ourselves known to |prefetch_service_|.
56   // This method is used to wrap PrefetchService::SetSuggestionProvider() to let
57   // our weak pointer guarantee everyone is still alive.
58   void SetSuggestionProvider();
59 
60   // Synchronously returns the offline id of the given page. The host will only
61   // have knowledge of the page if it had previously returned status about it
62   // through GetOfflineState() or as a notification. Otherwise the caller will
63   // receive a false negative. Additionally, since the host tracks pages by
64   // hashing, there's also a small chance that the host erroneously returns an
65   // id for a page that is not offlined.
66   base::Optional<int64_t> GetOfflineId(const std::string& url);
67 
68   // Asynchronously fetches offline status for the given URLs. Any pages that
69   // are currently offlined will be remembered by the FeedOfflineHost.
70   void GetOfflineStatus(
71       std::vector<std::string> urls,
72       base::OnceCallback<void(std::vector<std::string>)> callback);
73 
74   // Should be called from Feed any time the user manually removes articles or
75   // groupings of articles. Propagates the signal to Prefetch.
76   void OnContentRemoved(std::vector<std::string> urls);
77 
78   // Should be called from Feed any time new articles are fetched.
79   void OnNewContentReceived();
80 
81   // Should be called from Feed side any time there are no active surfaces
82   // displaying articles and listening to our notifications. This signal is used
83   // to clear local tracking of offlined items.
84   void OnNoListeners();
85 
86   // Should be called when async GetKnownContent is completed. Broadcasts to all
87   // waiting consumers in |pending_known_content_callbacks_|.
88   void OnGetKnownContentDone(std::vector<ContentMetadata> suggestions);
89 
90   // offline_pages::SuggestionsProvider:
91   void GetCurrentArticleSuggestions(
92       offline_pages::SuggestionsProvider::SuggestionCallback
93           suggestions_callback) override;
94   void ReportArticleListViewed() override;
95   void ReportArticleViewed(GURL article_url) override;
96 
97   // offline_pages::OfflinePageModel::Observer:
98   void OfflinePageModelLoaded(offline_pages::OfflinePageModel* model) override;
99   void OfflinePageAdded(
100       offline_pages::OfflinePageModel* model,
101       const offline_pages::OfflinePageItem& added_page) override;
102   void OfflinePageDeleted(
103       const offline_pages::OfflinePageItem& deleted_page) override;
104 
105  private:
106   // Stores the given record in |url_hash_to_id_|. If there's a conflict, the
107   // new id will overwrite the old value.
108   void CacheOfflinePageUrlAndId(const std::string& url, int64_t id);
109 
110   // Removes a previously cached |id| for the given |url| if there was one.
111   void EvictOfflinePageUrl(const std::string& url);
112 
113   // The following objects all outlive us, so it is safe to hold raw pointers to
114   // them. This is guaranteed by the FeedHostServiceFactory.
115   offline_pages::OfflinePageModel* offline_page_model_;
116   offline_pages::PrefetchService* prefetch_service_;
117 
118   base::RepeatingClosure on_suggestion_consumed_;
119   base::RepeatingClosure on_suggestions_shown_;
120 
121   // Only offlined pages that have passed through the host are stored. If there
122   // are ever no listeners to the offline host logic and OnNoListeners() is
123   // called this map is cleared. The key is the hash of the url, and the value
124   // is the offline id for the given page.
125   base::flat_map<size_t, int64_t> url_hash_to_id_;
126 
127   // Starts an the async request for ContentMetadata through KnownContentApi's
128   // GetKnownContent(). Will only be invoked when there isn't already an
129   // outstanding GetKnownContent().
130   base::RepeatingClosure trigger_get_known_content_;
131 
132   // Holds all consumers of GetKnownContent(). It is assumed that there's an
133   // outstanding GetKnownContent() if and only if this vector is not empty.
134   std::vector<SuggestionsProvider::SuggestionCallback>
135       pending_known_content_callbacks_;
136 
137   // Calls all OfflineStatusListeners with the updated status.
138   NotifyStatusChangeCallback notify_status_change_;
139 
140   SEQUENCE_CHECKER(sequence_checker_);
141 
142   base::WeakPtrFactory<FeedOfflineHost> weak_factory_{this};
143 
144   DISALLOW_COPY_AND_ASSIGN(FeedOfflineHost);
145 };
146 
147 }  // namespace feed
148 
149 #endif  // COMPONENTS_FEED_CONTENT_FEED_OFFLINE_HOST_H_
150