1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
// Copyright 2018 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_OFFLINE_PAGES_ANDROID_AUTO_FETCH_PAGE_LOAD_WATCHER_H_
#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_AUTO_FETCH_PAGE_LOAD_WATCHER_H_
#include <map>
#include <memory>
#include <set>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/offline_pages/core/auto_fetch.h"
#include "components/offline_pages/core/background/request_coordinator.h"
#include "components/offline_pages/core/client_id.h"
#include "url/gurl.h"
namespace content {
class WebContents;
class NavigationHandle;
} // namespace content
namespace offline_pages {
class SavePageRequest;
class RequestCoordinator;
// Manages showing the in-progress notification.
class AutoFetchNotifier {
public:
virtual ~AutoFetchNotifier() = default;
// Ensures that the in-progress notification is showing with the appropriate
// request count.
virtual void NotifyInProgress(int in_flight_count) {}
// Update the request count if the in-progress notification is already
// showing. This won't trigger showing the notification if it's not already
// shown. If |in_flight_count| is 0, the notification will be hidden.
virtual void InProgressCountChanged(int in_flight_count) {}
};
// Types and functions internal to AutoFetchPageLoadWatcher. Included in the
// header for testing.
namespace auto_fetch_internal {
// Information about an Android browser tab.
struct TabInfo {
int android_tab_id = 0;
GURL current_url;
};
// Interface to Android tabs used by |AutoFetchPageLoadWatcher|. This is the
// real implementation, methods are virtual for testing only.
class AndroidTabFinder {
public:
virtual ~AndroidTabFinder();
// Returns a mapping of Android tab ID to TabInfo.
virtual std::map<int, TabInfo> FindAndroidTabs(
std::vector<int> android_tab_ids);
virtual std::optional<TabInfo> FindNavigationTab(
content::WebContents* web_contents);
};
// Information about an auto-fetch |SavePageRequest|.
struct RequestInfo {
int64_t request_id;
GURL url;
SavePageRequest::AutoFetchNotificationState notification_state;
auto_fetch::ClientIdMetadata metadata;
};
std::optional<RequestInfo> MakeRequestInfo(const SavePageRequest& request);
// |AutoFetchPageLoadWatcher|'s more unit-testable internal implementation.
// This class was designed to have few dependencies to make testing more
// tractable. Events are communicated to |InternalImpl| through its public
// member functions, and functions are injected through |AutoFetchNotifier|,
// |Delegate|, and |AndroidTabFinder|.
class InternalImpl {
public:
// Injected functions, implemented by |AutoFetchPageLoadWatcher|.
// We need this because we can't call these functions directly.
class Delegate {
public:
virtual ~Delegate() = default;
// Sets the notification state of a request to
// |SavePageRequest::AutoFetchNotificationState::kShown|. Results in a call
// to |SetNotificationStateComplete|.
virtual void SetNotificationStateToShown(int64_t request_id) {}
// Removes all |SavePageRequest|s with the given IDs.
virtual void RemoveRequests(const std::vector<int64_t>& request_ids) {}
};
InternalImpl(AutoFetchNotifier* notifier,
Delegate* delegate,
std::unique_ptr<AndroidTabFinder> tab_finder);
~InternalImpl();
// Request state change methods.
void RequestListInitialized(std::vector<RequestInfo> requests);
void RequestAdded(RequestInfo info);
void RequestRemoved(RequestInfo info);
void SetNotificationStateComplete(int64_t request_id, bool success);
// Navigation methods.
void SuccessfulPageNavigation(const GURL& url);
void NavigationFrom(const GURL& previous_url,
content::WebContents* web_contents);
// Tab event methods.
void TabClosed(int android_tab_id);
void TabModelReady();
base::WeakPtr<InternalImpl> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
std::vector<GURL>& pages_loaded_before_observer_ready_for_testing() {
return pages_loaded_before_observer_ready_;
}
private:
void SetNotificationStateToShown(int64_t request_id);
void UpdateNotificationStateForAllRequests();
raw_ptr<AutoFetchNotifier> notifier_;
raw_ptr<Delegate> delegate_;
std::unique_ptr<AndroidTabFinder> tab_finder_;
std::vector<RequestInfo> requests_;
// Tracks whether |RequestListInitialized| has been called. If false,
// |RequestAdded| and |RequestRemoved| should be ignored, as per the
// documentation in |RequestCoordinator::Observer|.
bool requests_initialized_ = false;
bool tab_model_ready_ = false;
std::vector<GURL> pages_loaded_before_observer_ready_;
base::WeakPtrFactory<InternalImpl> weak_ptr_factory_{this};
};
} // namespace auto_fetch_internal
// Watches for page loads and |RequestCoordinator| requests.
// Given an active auto-fetch request for <tab, URL>:
// - If URL is loaded successfully on tab, cancel the auto-fetch request.
// - If a different URL is loaded successfully on tab, trigger the in-progress
// notification.
// - If tab is closed, trigger the in-progress notification.
// - If an auto-fetch request is removed, update the in-progress notification's
// displayed request count.
//
// Implementation note:
// This class simply observes events and passes them down to |InternalImpl|
// for processing. All code here is run on the UI thread.
class AutoFetchPageLoadWatcher
: public RequestCoordinator::Observer,
public auto_fetch_internal::InternalImpl::Delegate {
public:
using AndroidTabFinder = auto_fetch_internal::AndroidTabFinder;
static void CreateForWebContents(content::WebContents* web_contents);
AutoFetchPageLoadWatcher(AutoFetchNotifier* notifier,
RequestCoordinator* request_coordinator,
std::unique_ptr<AndroidTabFinder> tab_finder);
AutoFetchPageLoadWatcher(const AutoFetchPageLoadWatcher&) = delete;
AutoFetchPageLoadWatcher& operator=(const AutoFetchPageLoadWatcher&) = delete;
~AutoFetchPageLoadWatcher() override;
// Called when navigation completes, even on errors. This is only called
// once per navigation.
void HandleNavigation(content::NavigationHandle* navigation_handle);
std::vector<GURL>& loaded_pages_for_testing() {
return impl_.pages_loaded_before_observer_ready_for_testing();
}
private:
class NavigationObserver;
class TabWatcher;
base::WeakPtr<AutoFetchPageLoadWatcher> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void InitializeRequestList(
std::vector<std::unique_ptr<SavePageRequest>> requests);
// InternalImpl::Delegate.
void SetNotificationStateToShown(int64_t request_id) override;
void RemoveRequests(const std::vector<int64_t>& request_ids) override;
// RequestCoordinator::Observer.
void OnAdded(const SavePageRequest& request) override;
void OnCompleted(const SavePageRequest& request,
RequestNotifier::BackgroundSavePageResult status) override;
void OnChanged(const SavePageRequest& request) override {}
void OnNetworkProgress(const SavePageRequest& request,
int64_t received_bytes) override {}
raw_ptr<RequestCoordinator> request_coordinator_; // Not owned.
auto_fetch_internal::InternalImpl impl_;
std::unique_ptr<TabWatcher> tab_watcher_;
base::WeakPtrFactory<AutoFetchPageLoadWatcher> weak_ptr_factory_{this};
};
} // namespace offline_pages
#endif // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_AUTO_FETCH_PAGE_LOAD_WATCHER_H_
|