Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: chrome/browser/android/offline_pages/recent_tab_helper.cc

Issue 2602473004: Offline Page Cache uses user interaction triggers for saving pages. (Closed)
Patch Set: Fully implemented the new user interaction triggers. Updated tests. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/android/offline_pages/recent_tab_helper.h" 5 #include "chrome/browser/android/offline_pages/recent_tab_helper.h"
6 6
7 #include <queue> 7 #include <queue>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 } 70 }
71 71
72 void RecentTabHelper::SetDelegate( 72 void RecentTabHelper::SetDelegate(
73 std::unique_ptr<RecentTabHelper::Delegate> delegate) { 73 std::unique_ptr<RecentTabHelper::Delegate> delegate) {
74 DCHECK(delegate); 74 DCHECK(delegate);
75 delegate_ = std::move(delegate); 75 delegate_ = std::move(delegate);
76 } 76 }
77 77
78 void RecentTabHelper::ObserveAndDownloadCurrentPage( 78 void RecentTabHelper::ObserveAndDownloadCurrentPage(
79 const ClientId& client_id, int64_t request_id) { 79 const ClientId& client_id, int64_t request_id) {
80 EnsureInitialized(); 80 auto new_downloads_snapshot_info =
81 snapshot_info_ =
82 base::MakeUnique<SnapshotProgressInfo>(client_id, request_id); 81 base::MakeUnique<SnapshotProgressInfo>(client_id, request_id);
83 82
84 // If this tab helper is not enabled, immediately give the job back to 83 // If this tab helper is not enabled, immediately give the job back to
85 // RequestCoordinator. 84 // RequestCoordinator.
86 if (!snapshots_enabled_ || !page_model_) { 85 if (!EnsureInitialized()) {
87 ReportDownloadStatusToRequestCoordinator(); 86 ReportDownloadStatusToRequestCoordinator(new_downloads_snapshot_info.get());
88 weak_ptr_factory_.InvalidateWeakPtrs();
89 snapshot_info_.reset();
90 return; 87 return;
91 } 88 }
92 89
93 // No snapshots yet happened on the current page - return and wait for some. 90 // TODO(carlosk): This is a good moment check if a snapshot is currently being
94 if (!is_page_ready_for_snapshot_) 91 // generated. This would allow the early cancellation of this request (without
92 // incurring in scheduling a background download).
93
94 // Stores the new snapshot info.
95 latest_downloads_snapshot_info_ = std::move(new_downloads_snapshot_info);
96
97 // If the page is not yet ready for a snapshot return now as it will be
98 // started later, once page loading advances.
99 if (PageQuality::POOR == snapshot_controller_->current_page_quality())
95 return; 100 return;
96 101
97 // If snapshot already happened and we missed it, go ahead and snapshot now. 102 // Otherwise start saving the snapshot now.
98 OfflinePageModel::SavePageParams save_page_params; 103 SaveSnapshotForDownloads();
99 save_page_params.url = web_contents()->GetLastCommittedURL();
100 save_page_params.client_id = client_id;
101 save_page_params.proposed_offline_id = request_id;
102 page_model_->SavePage(
103 save_page_params,
104 delegate_->CreatePageArchiver(web_contents()),
105 base::Bind(&RecentTabHelper::SavePageCallback,
106 weak_ptr_factory_.GetWeakPtr()));
107 } 104 }
108 105
109 // Initialize lazily. It needs TabAndroid for initialization, which is also a 106 // Initialize lazily. It needs TabAndroid for initialization, which is also a
110 // TabHelper - so can't initialize in constructor because of uncertain order 107 // TabHelper - so can't initialize in constructor because of uncertain order
111 // of creation of TabHelpers. 108 // of creation of TabHelpers.
112 void RecentTabHelper::EnsureInitialized() { 109 bool RecentTabHelper::EnsureInitialized() {
113 if (snapshot_controller_) // Initialized already. 110 if (snapshot_controller_) // Initialized already.
114 return; 111 return snapshots_enabled_;
115 112
116 snapshot_controller_.reset( 113 snapshot_controller_.reset(
117 new SnapshotController(delegate_->GetTaskRunner(), this)); 114 new SnapshotController(delegate_->GetTaskRunner(), this));
118 snapshot_controller_->Stop(); // It is reset when navigation commits. 115 snapshot_controller_->Stop(); // It is reset when navigation commits.
119 116
120 int tab_id_number = 0; 117 int tab_id_number = 0;
121 tab_id_.clear(); 118 tab_id_.clear();
122 119
123 if (delegate_->GetTabId(web_contents(), &tab_id_number)) 120 if (delegate_->GetTabId(web_contents(), &tab_id_number))
124 tab_id_ = base::IntToString(tab_id_number); 121 tab_id_ = base::IntToString(tab_id_number);
125 122
126 // TODO(dimich): When we have BackgroundOffliner, avoid capturing prerenderer 123 // TODO(dimich): When we have BackgroundOffliner, avoid capturing prerenderer
127 // WebContents with its origin as well. 124 // WebContents with its origin as well.
128 snapshots_enabled_ = !tab_id_.empty() && 125 snapshots_enabled_ = !tab_id_.empty() &&
129 !web_contents()->GetBrowserContext()->IsOffTheRecord(); 126 !web_contents()->GetBrowserContext()->IsOffTheRecord();
130 127
131 if (!snapshots_enabled_) 128 if (snapshots_enabled_) {
132 return; 129 page_model_ = OfflinePageModelFactory::GetForBrowserContext(
130 web_contents()->GetBrowserContext());
131 }
133 132
134 page_model_ = OfflinePageModelFactory::GetForBrowserContext( 133 return snapshots_enabled_;
135 web_contents()->GetBrowserContext());
136 } 134 }
137 135
138 void RecentTabHelper::DidFinishNavigation( 136 void RecentTabHelper::DidFinishNavigation(
139 content::NavigationHandle* navigation_handle) { 137 content::NavigationHandle* navigation_handle) {
140 if (!navigation_handle->IsInMainFrame() || navigation_handle->IsSamePage() || 138 if (!navigation_handle->IsInMainFrame() || navigation_handle->IsSamePage() ||
141 !navigation_handle->HasCommitted()) { 139 !navigation_handle->HasCommitted()) {
142 return; 140 return;
143 } 141 }
144 142
145 EnsureInitialized(); 143 if (!EnsureInitialized())
146 if (!snapshots_enabled_)
147 return; 144 return;
148 145
149 // Cancel tasks in flight that relate to the previous page.
150 weak_ptr_factory_.InvalidateWeakPtrs();
151
152 // We navigated to a different page, lets report progress to Background 146 // We navigated to a different page, lets report progress to Background
153 // Offliner. 147 // Offliner.
154 ReportDownloadStatusToRequestCoordinator(); 148 if (latest_downloads_snapshot_info_)
149 ReportDownloadStatusToRequestCoordinator(
150 latest_downloads_snapshot_info_.get());
155 151
156 if (offline_pages::IsOffliningRecentPagesEnabled()) { 152 // Cancel any and all in flight snapshot tasks from the previous page.
157 // Note: at any point in time |snapshot_info_| might be replaced with one 153 weak_ptr_factory_.InvalidateWeakPtrs();
carlosk 2017/01/20 07:49:41 Note that now this is the only "normal" use case f
158 // created by ObserveAndDownloadCurrentPage for a download snapshot request. 154 latest_downloads_snapshot_info_.reset();
159 snapshot_info_ = 155 ongoing_last_n_snapshot_info_.reset();
160 base::MakeUnique<SnapshotProgressInfo>(GetRecentPagesClientId());
161 } else {
162 snapshot_info_.reset();
163 }
164
165 is_page_ready_for_snapshot_ = false;
166 156
167 // New navigation, new snapshot session. 157 // New navigation, new snapshot session.
168 snapshot_url_ = web_contents()->GetLastCommittedURL(); 158 snapshot_url_ = web_contents()->GetLastCommittedURL();
169 159
160 // Always reset so that posted tasks get canceled.
161 snapshot_controller_->Reset();
162
170 // Check for conditions that would cause us not to snapshot. 163 // Check for conditions that would cause us not to snapshot.
171 bool can_save = !navigation_handle->IsErrorPage() && 164 bool can_save = !navigation_handle->IsErrorPage() &&
172 OfflinePageModel::CanSaveURL(snapshot_url_) && 165 OfflinePageModel::CanSaveURL(snapshot_url_) &&
173 OfflinePageUtils::GetOfflinePageFromWebContents( 166 OfflinePageUtils::GetOfflinePageFromWebContents(
174 web_contents()) == nullptr; 167 web_contents()) == nullptr;
175 168
176 UMA_HISTOGRAM_BOOLEAN("OfflinePages.CanSaveRecentPage", can_save); 169 UMA_HISTOGRAM_BOOLEAN("OfflinePages.CanSaveRecentPage", can_save);
177 170
178 // Always reset so that posted tasks get canceled.
179 snapshot_controller_->Reset();
180
181 if (!can_save) 171 if (!can_save)
182 snapshot_controller_->Stop(); 172 snapshot_controller_->Stop();
173 last_n_listen_to_tab_hidden_ = can_save && IsOffliningRecentPagesEnabled();
174 last_n_latest_saved_quality_ = PageQuality::POOR;
183 } 175 }
184 176
185 void RecentTabHelper::DocumentAvailableInMainFrame() { 177 void RecentTabHelper::DocumentAvailableInMainFrame() {
186 EnsureInitialized(); 178 EnsureInitialized();
187 snapshot_controller_->DocumentAvailableInMainFrame(); 179 snapshot_controller_->DocumentAvailableInMainFrame();
188 } 180 }
189 181
190 void RecentTabHelper::DocumentOnLoadCompletedInMainFrame() { 182 void RecentTabHelper::DocumentOnLoadCompletedInMainFrame() {
191 EnsureInitialized(); 183 EnsureInitialized();
192 snapshot_controller_->DocumentOnLoadCompletedInMainFrame(); 184 snapshot_controller_->DocumentOnLoadCompletedInMainFrame();
193 } 185 }
194 186
195 void RecentTabHelper::WebContentsDestroyed() { 187 void RecentTabHelper::WebContentsDestroyed() {
196 // WebContents (and maybe Tab) is destroyed, report status to Offliner. 188 // WebContents (and maybe Tab) is destroyed, report status to Offliner.
197 ReportDownloadStatusToRequestCoordinator(); 189 if (latest_downloads_snapshot_info_)
190 ReportDownloadStatusToRequestCoordinator(
191 latest_downloads_snapshot_info_.get());
192 // And cancel any ongoing snapshots.
193 weak_ptr_factory_.InvalidateWeakPtrs();
198 } 194 }
199 195
196 // TODO(carlosk): this method is also called when the tab is being closed, when
197 // saving a snapshot is probably useless (low probability of the user undoing
198 // the close). We should detect that and avoid the saving.
199 void RecentTabHelper::WasHidden() {
200 if (!IsOffliningRecentPagesEnabled())
201 return;
200 202
201 // This starts a sequence of async operations chained through callbacks: 203 // Return immediately if last_n is not listening to tab hidden events or if a
202 // - compute the set of old 'last_n' pages that have to be purged 204 // last_n snapshot is currently being saved.
203 // - delete the pages found in the previous step 205 if (!last_n_listen_to_tab_hidden_ || ongoing_last_n_snapshot_info_)
204 // - snapshot the current web contents 206 return;
205 // Along the chain, the original URL is passed and compared, to detect
206 // possible navigation and cancel snapshot in that case.
207 void RecentTabHelper::StartSnapshot() {
208 is_page_ready_for_snapshot_ = true;
209 207
210 if (!snapshots_enabled_ || !page_model_ || !snapshot_info_) { 208 // Do not save if page quality is too low or if we already have a snapshot
211 ReportSnapshotCompleted(); 209 // with the current quality level.
210 // Note: we assume page quality for a page can only increase.
211 PageQuality current_quality = snapshot_controller_->current_page_quality();
212 if (current_quality == PageQuality::POOR ||
213 current_quality == last_n_latest_saved_quality_) {
Dmitry Titov 2017/01/20 06:25:36 This looks like we store the quality of the latest
carlosk 2017/01/20 07:49:41 Yes, this last statement is correct. With this imp
212 return; 214 return;
213 } 215 }
214 216
217 ongoing_last_n_snapshot_info_ =
218 base::MakeUnique<SnapshotProgressInfo>(GetRecentPagesClientId());
219 DCHECK(snapshots_enabled_);
215 // Remove previously captured pages for this tab. 220 // Remove previously captured pages for this tab.
216 page_model_->GetOfflineIdsForClientId( 221 page_model_->GetOfflineIdsForClientId(
217 GetRecentPagesClientId(), 222 GetRecentPagesClientId(),
218 base::Bind(&RecentTabHelper::ContinueSnapshotWithIdsToPurge, 223 base::Bind(&RecentTabHelper::ContinueSnapshotWithIdsToPurge,
219 weak_ptr_factory_.GetWeakPtr())); 224 weak_ptr_factory_.GetWeakPtr(),
225 ongoing_last_n_snapshot_info_.get()));
220 } 226 }
221 227
228 // TODO(carlosk): rename this to RequestSnapshot and make it return a bool
229 // representing the acceptance of the snapshot request.
230 void RecentTabHelper::StartSnapshot() {
231 DCHECK_NE(PageQuality::POOR, snapshot_controller_->current_page_quality());
232
233 // This is a navigation based snapshot request so check that snapshots are
234 // both enabled and there is a downloads request for one.
235 // TODO(carlosk): This is a good moment to add the check for an ongoing
236 // snapshot that could trigger the early cancellation of this request.
237 if (snapshots_enabled_ && latest_downloads_snapshot_info_)
238 SaveSnapshotForDownloads();
239 else
240 snapshot_controller_->PendingSnapshotCompleted();
241 }
242
243 // TODO(carlosk): There is still the possibility of overlapping snapshots with
244 // some combinations of calls to ObserveAndDownloadCurrentPage and
245 // StartSnapshot. It won't cause side effects beyond wasted resources and will
246 // be dealt with later.
247 void RecentTabHelper::SaveSnapshotForDownloads() {
248 DCHECK_NE(PageQuality::POOR, snapshot_controller_->current_page_quality());
249 DCHECK(latest_downloads_snapshot_info_);
250
251 // Requests the deletion of a potentially existing previous snapshot of this
252 // page.
253 std::vector<int64_t> ids{latest_downloads_snapshot_info_->request_id};
254 ContinueSnapshotWithIdsToPurge(latest_downloads_snapshot_info_.get(), ids);
255 }
256
257 // This is the 1st step of a sequence of async operations chained through
258 // callbacks, mostly shared between last_n and downloads:
259 // 1) Compute the set of old 'last_n' pages that have to be purged.
260 // 2) Delete the pages found in the previous step.
261 // 3) Snapshot the current web contents.
262 // 4) Notify requesters about the final result of the operation.
263 //
264 // For last_n requests the sequence is started in 1); for downloads it starts in
265 // 2). Step 4) might be called anytime during the chain for early termination in
266 // case of errors.
222 void RecentTabHelper::ContinueSnapshotWithIdsToPurge( 267 void RecentTabHelper::ContinueSnapshotWithIdsToPurge(
268 SnapshotProgressInfo* snapshot_info,
223 const std::vector<int64_t>& page_ids) { 269 const std::vector<int64_t>& page_ids) {
224 DCHECK(snapshot_info_); 270 DCHECK(snapshot_info);
225
226 // Also remove the download page if this is not a first snapshot.
227 std::vector<int64_t> ids(page_ids);
228 ids.push_back(snapshot_info_->request_id);
229 271
230 page_model_->DeletePagesByOfflineId( 272 page_model_->DeletePagesByOfflineId(
231 ids, base::Bind(&RecentTabHelper::ContinueSnapshotAfterPurge, 273 page_ids, base::Bind(&RecentTabHelper::ContinueSnapshotAfterPurge,
232 weak_ptr_factory_.GetWeakPtr())); 274 weak_ptr_factory_.GetWeakPtr(), snapshot_info));
233 } 275 }
234 276
235 void RecentTabHelper::ContinueSnapshotAfterPurge( 277 void RecentTabHelper::ContinueSnapshotAfterPurge(
278 SnapshotProgressInfo* snapshot_info,
236 OfflinePageModel::DeletePageResult result) { 279 OfflinePageModel::DeletePageResult result) {
237 DCHECK(snapshot_info_);
238 DCHECK_EQ(snapshot_url_, web_contents()->GetLastCommittedURL()); 280 DCHECK_EQ(snapshot_url_, web_contents()->GetLastCommittedURL());
239 if (result != OfflinePageModel::DeletePageResult::SUCCESS) { 281 if (result != OfflinePageModel::DeletePageResult::SUCCESS) {
240 ReportSnapshotCompleted(); 282 ReportSnapshotCompleted(snapshot_info);
241 return; 283 return;
242 } 284 }
243 285
286 snapshot_info->expected_page_quality =
287 snapshot_controller_->current_page_quality();
244 OfflinePageModel::SavePageParams save_page_params; 288 OfflinePageModel::SavePageParams save_page_params;
245 save_page_params.url = snapshot_url_; 289 save_page_params.url = snapshot_url_;
246 save_page_params.client_id = snapshot_info_->client_id; 290 save_page_params.client_id = snapshot_info->client_id;
247 save_page_params.proposed_offline_id = snapshot_info_->request_id; 291 save_page_params.proposed_offline_id = snapshot_info->request_id;
248 page_model_->SavePage(save_page_params, 292 page_model_->SavePage(
249 delegate_->CreatePageArchiver(web_contents()), 293 save_page_params, delegate_->CreatePageArchiver(web_contents()),
250 base::Bind(&RecentTabHelper::SavePageCallback, 294 base::Bind(&RecentTabHelper::SavePageCallback,
251 weak_ptr_factory_.GetWeakPtr())); 295 weak_ptr_factory_.GetWeakPtr(), snapshot_info));
252 } 296 }
253 297
254 void RecentTabHelper::SavePageCallback(OfflinePageModel::SavePageResult result, 298 void RecentTabHelper::SavePageCallback(SnapshotProgressInfo* snapshot_info,
299 OfflinePageModel::SavePageResult result,
255 int64_t offline_id) { 300 int64_t offline_id) {
256 DCHECK(snapshot_info_); 301 DCHECK(snapshot_info->IsForLastN() ||
257 snapshot_info_->page_snapshot_completed = (result == SavePageResult::SUCCESS); 302 snapshot_info->request_id == offline_id);
258 ReportSnapshotCompleted(); 303 snapshot_info->page_snapshot_completed = (result == SavePageResult::SUCCESS);
304 ReportSnapshotCompleted(snapshot_info);
259 } 305 }
260 306
261 void RecentTabHelper::ReportSnapshotCompleted() { 307 // Note: this is the final step in the chain of callbacks and it's where the
308 // behavior is different depending on this being a last_n or downloads snapshot.
309 void RecentTabHelper::ReportSnapshotCompleted(
310 SnapshotProgressInfo* snapshot_info) {
311 if (snapshot_info->IsForLastN()) {
312 DCHECK_EQ(snapshot_info, ongoing_last_n_snapshot_info_.get());
313 if (snapshot_info->page_snapshot_completed)
314 last_n_latest_saved_quality_ = snapshot_info->expected_page_quality;
315 ongoing_last_n_snapshot_info_.reset();
316 return;
317 }
318
262 snapshot_controller_->PendingSnapshotCompleted(); 319 snapshot_controller_->PendingSnapshotCompleted();
263 // Tell RequestCoordinator how the request should be processed further. 320 // Tell RequestCoordinator how the request should be processed further.
264 ReportDownloadStatusToRequestCoordinator(); 321 ReportDownloadStatusToRequestCoordinator(snapshot_info);
265 } 322 }
266 323
267 void RecentTabHelper::ReportDownloadStatusToRequestCoordinator() { 324 // Note: we cannot assume that snapshot_info == latest_downloads_snapshot_info_
268 if (!snapshot_info_ || snapshot_info_->IsForLastN()) 325 // because further calls made to ObserveAndDownloadCurrentPage will replace
269 return; 326 // latest_downloads_snapshot_info_ with a new instance.
327 void RecentTabHelper::ReportDownloadStatusToRequestCoordinator(
328 SnapshotProgressInfo* snapshot_info) {
329 DCHECK(snapshot_info);
330 DCHECK(!snapshot_info->IsForLastN());
270 331
271 RequestCoordinator* request_coordinator = 332 RequestCoordinator* request_coordinator =
272 RequestCoordinatorFactory::GetForBrowserContext( 333 RequestCoordinatorFactory::GetForBrowserContext(
273 web_contents()->GetBrowserContext()); 334 web_contents()->GetBrowserContext());
274 if (!request_coordinator) 335 if (!request_coordinator)
275 return; 336 return;
276 337
277 // It is OK to call these methods more then once, depending on 338 // It is OK to call these methods more then once, depending on
278 // number of snapshots attempted in this tab helper. If the request_id is not 339 // number of snapshots attempted in this tab helper. If the request_id is not
279 // in the list of RequestCoordinator, these calls have no effect. 340 // in the list of RequestCoordinator, these calls have no effect.
280 if (snapshot_info_->page_snapshot_completed) 341 if (snapshot_info->page_snapshot_completed)
281 request_coordinator->MarkRequestCompleted(snapshot_info_->request_id); 342 request_coordinator->MarkRequestCompleted(snapshot_info->request_id);
282 else 343 else
283 request_coordinator->EnableForOffliner(snapshot_info_->request_id, 344 request_coordinator->EnableForOffliner(snapshot_info->request_id,
284 snapshot_info_->client_id); 345 snapshot_info->client_id);
285 } 346 }
286 347
287 ClientId RecentTabHelper::GetRecentPagesClientId() const { 348 ClientId RecentTabHelper::GetRecentPagesClientId() const {
288 return ClientId(kLastNNamespace, tab_id_); 349 return ClientId(kLastNNamespace, tab_id_);
289 } 350 }
290 351
291 } // namespace offline_pages 352 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698