1 // Copyright 2016 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 #include "chrome/browser/offline_pages/background_loader_offliner.h"
6 
7 #include <string>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/bind.h"
12 #include "base/json/json_writer.h"
13 #include "base/metrics/histogram_functions.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/system/sys_info.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/offline_pages/offline_page_mhtml_archiver.h"
20 #include "chrome/browser/offline_pages/offliner_helper.h"
21 #include "chrome/browser/offline_pages/offliner_user_data.h"
22 #include "chrome/browser/previews/previews_ui_tab_helper.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ssl/security_state_tab_helper.h"
25 #include "chrome/common/chrome_isolated_world_ids.h"
26 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
27 #include "components/offline_pages/content/renovations/render_frame_script_injector.h"
28 #include "components/offline_pages/core/background/offliner_policy.h"
29 #include "components/offline_pages/core/background/save_page_request.h"
30 #include "components/offline_pages/core/client_namespace_constants.h"
31 #include "components/offline_pages/core/offline_page_client_policy.h"
32 #include "components/offline_pages/core/offline_page_feature.h"
33 #include "components/offline_pages/core/offline_page_model.h"
34 #include "components/offline_pages/core/renovations/page_renovation_loader.h"
35 #include "components/offline_pages/core/renovations/page_renovator.h"
36 #include "components/previews/content/previews_user_data.h"
37 #include "components/security_state/core/security_state.h"
38 #include "content/public/browser/browser_context.h"
39 #include "content/public/browser/mhtml_extra_parts.h"
40 #include "content/public/browser/navigation_entry.h"
41 #include "content/public/browser/navigation_handle.h"
42 #include "content/public/browser/render_frame_host.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/browser/web_contents_user_data.h"
45 #include "net/cert/cert_status_flags.h"
46 #include "net/http/http_response_headers.h"
47 #include "third_party/blink/public/common/loader/previews_state.h"
48 
49 namespace offline_pages {
50 
51 namespace {
52 const char kContentType[] = "text/plain";
53 const char kContentTransferEncodingBinary[] =
54     "Content-Transfer-Encoding: binary";
55 const char kXHeaderForSignals[] = "X-Chrome-Loading-Metrics-Data: 1";
56 
AddHistogramSuffix(const ClientId & client_id,const char * histogram_name)57 std::string AddHistogramSuffix(const ClientId& client_id,
58                                const char* histogram_name) {
59   if (client_id.name_space.empty()) {
60     NOTREACHED();
61     return histogram_name;
62   }
63   std::string adjusted_histogram_name(histogram_name);
64   adjusted_histogram_name += "." + client_id.name_space;
65   return adjusted_histogram_name;
66 }
67 
RecordErrorCauseUMA(const ClientId & client_id,int error_code)68 void RecordErrorCauseUMA(const ClientId& client_id, int error_code) {
69   base::UmaHistogramSparse(
70       AddHistogramSuffix(client_id,
71                          "OfflinePages.Background.LoadingErrorStatusCode"),
72       error_code);
73 }
74 
RecordOffliningPreviewsUMA(const ClientId & client_id,blink::PreviewsState previews_state)75 void RecordOffliningPreviewsUMA(const ClientId& client_id,
76                                 blink::PreviewsState previews_state) {
77   bool is_previews_enabled =
78       (previews_state != blink::PreviewsTypes::PREVIEWS_OFF &&
79        previews_state != blink::PreviewsTypes::PREVIEWS_NO_TRANSFORM);
80 
81   base::UmaHistogramBoolean(
82       AddHistogramSuffix(client_id,
83                          "OfflinePages.Background.OffliningPreviewStatus"),
84       is_previews_enabled);
85 }
86 
HandleLoadTerminationCancel(Offliner::CompletionCallback completion_callback,const SavePageRequest & canceled_request)87 void HandleLoadTerminationCancel(
88     Offliner::CompletionCallback completion_callback,
89     const SavePageRequest& canceled_request) {
90   std::move(completion_callback)
91       .Run(canceled_request, Offliner::RequestStatus::FOREGROUND_CANCELED);
92 }
93 
94 }  // namespace
95 
BackgroundLoaderOffliner(content::BrowserContext * browser_context,const OfflinerPolicy * policy,OfflinePageModel * offline_page_model,std::unique_ptr<LoadTerminationListener> load_termination_listener)96 BackgroundLoaderOffliner::BackgroundLoaderOffliner(
97     content::BrowserContext* browser_context,
98     const OfflinerPolicy* policy,
99     OfflinePageModel* offline_page_model,
100     std::unique_ptr<LoadTerminationListener> load_termination_listener)
101     : browser_context_(browser_context),
102       offline_page_model_(offline_page_model),
103       policy_(policy),
104       load_termination_listener_(std::move(load_termination_listener)),
105       save_state_(NONE),
106       page_load_state_(SUCCESS),
107       network_bytes_(0LL),
108       is_low_bar_met_(false),
109       did_snapshot_on_last_retry_(false) {
110   DCHECK(offline_page_model_);
111   DCHECK(browser_context_);
112   // When the offliner is created for test harness runs, the
113   // |load_termination_listener_| will be set to nullptr, in order to prevent
114   // crashing, adding a check here.
115   if (load_termination_listener_)
116     load_termination_listener_->set_offliner(this);
117 
118   for (int i = 0; i < ResourceDataType::RESOURCE_DATA_TYPE_COUNT; ++i) {
119     stats_[i].requested = 0;
120     stats_[i].completed = 0;
121   }
122 }
123 
~BackgroundLoaderOffliner()124 BackgroundLoaderOffliner::~BackgroundLoaderOffliner() {}
125 
126 // static
FromWebContents(content::WebContents * contents)127 BackgroundLoaderOffliner* BackgroundLoaderOffliner::FromWebContents(
128     content::WebContents* contents) {
129   Offliner* offliner = OfflinerUserData::OfflinerFromWebContents(contents);
130   // Today we only have one kind of offliner that uses OfflinerUserData.  If we
131   // add other types, revisit this cast.
132   if (offliner)
133     return static_cast<BackgroundLoaderOffliner*>(offliner);
134   return nullptr;
135 }
136 
LoadAndSave(const SavePageRequest & request,CompletionCallback completion_callback,const ProgressCallback & progress_callback)137 bool BackgroundLoaderOffliner::LoadAndSave(
138     const SavePageRequest& request,
139     CompletionCallback completion_callback,
140     const ProgressCallback& progress_callback) {
141   DCHECK(completion_callback);
142   DCHECK(progress_callback);
143   DCHECK(offline_page_model_);
144 
145   if (pending_request_) {
146     DVLOG(1) << "Already have pending request";
147     return false;
148   }
149 
150   if (GetPolicy(request.client_id().name_space)
151           .requires_specific_user_settings &&
152       (AreThirdPartyCookiesBlocked(browser_context_) ||
153        IsNetworkPredictionDisabled(browser_context_))) {
154     DVLOG(1) << "WARNING: Unable to load when 3rd party cookies blocked or "
155              << "prediction disabled";
156     return false;
157   }
158 
159   if (!OfflinePageModel::CanSaveURL(request.url())) {
160     DVLOG(1) << "Not able to save page for requested url: " << request.url();
161     return false;
162   }
163 
164   ResetLoader();
165   AttachObservers();
166 
167   MarkLoadStartTime();
168 
169   // Track copy of pending request.
170   pending_request_.reset(new SavePageRequest(request));
171   completion_callback_ = std::move(completion_callback);
172   progress_callback_ = progress_callback;
173 
174   if (IsOfflinePagesRenovationsEnabled()) {
175     // Lazily create PageRenovationLoader
176     if (!page_renovation_loader_)
177       page_renovation_loader_ = std::make_unique<PageRenovationLoader>();
178 
179     // Set up PageRenovator for this offlining instance.
180     auto script_injector = std::make_unique<RenderFrameScriptInjector>(
181         loader_->web_contents()->GetMainFrame(),
182         ISOLATED_WORLD_ID_CHROME_INTERNAL);
183     page_renovator_ = std::make_unique<PageRenovator>(
184         page_renovation_loader_.get(), std::move(script_injector),
185         request.url());
186   }
187 
188   // Load page attempt.
189   loader_.get()->LoadPage(request.url());
190 
191   snapshot_controller_ = std::make_unique<BackgroundSnapshotController>(
192       base::ThreadTaskRunnerHandle::Get(), this,
193       static_cast<bool>(page_renovator_));
194 
195   return true;
196 }
197 
Cancel(CancelCallback callback)198 bool BackgroundLoaderOffliner::Cancel(CancelCallback callback) {
199   DCHECK(pending_request_);
200   // We ignore the case where pending_request_ is not set, but given the checks
201   // in RequestCoordinator this should not happen.
202   if (!pending_request_)
203     return false;
204 
205   // TODO(chili): We are not able to cancel a pending
206   // OfflinePageModel::SaveSnapshot() operation. We will notify caller that
207   // cancel completed once the SavePage operation returns.
208   if (save_state_ != NONE) {
209     save_state_ = DELETE_AFTER_SAVE;
210     cancel_callback_ = std::move(callback);
211     return true;
212   }
213 
214   // Post the cancel callback right after this call concludes.
215   base::ThreadTaskRunnerHandle::Get()->PostTask(
216       FROM_HERE, base::BindOnce(std::move(callback), *pending_request_.get()));
217   ResetState();
218   return true;
219 }
220 
TerminateLoadIfInProgress()221 void BackgroundLoaderOffliner::TerminateLoadIfInProgress() {
222   if (!pending_request_)
223     return;
224 
225   Cancel(base::BindOnce(HandleLoadTerminationCancel,
226                         std::move(completion_callback_)));
227 }
228 
HandleTimeout(int64_t request_id)229 bool BackgroundLoaderOffliner::HandleTimeout(int64_t request_id) {
230   if (pending_request_) {
231     DCHECK(request_id == pending_request_->request_id());
232     if (is_low_bar_met_ && (pending_request_->started_attempt_count() + 1 >=
233                                 policy_->GetMaxStartedTries() ||
234                             pending_request_->completed_attempt_count() + 1 >=
235                                 policy_->GetMaxCompletedTries())) {
236       // If we are already in the middle of a save operation, let it finish
237       // but do not return SAVED_ON_LAST_RETRY
238       if (save_state_ == NONE) {
239         did_snapshot_on_last_retry_ = true;
240         StartSnapshot();
241       }
242       return true;
243     }
244   }
245   return false;
246 }
247 
CanDownload(base::OnceCallback<void (bool)> callback)248 void BackgroundLoaderOffliner::CanDownload(
249     base::OnceCallback<void(bool)> callback) {
250   if (!pending_request_.get()) {
251     std::move(callback).Run(false);  // Shouldn't happen though...
252     return;
253   }
254 
255   bool should_allow_downloads = false;
256   Offliner::RequestStatus final_status =
257       Offliner::RequestStatus::LOADING_FAILED_DOWNLOAD;
258   // Check whether we should allow file downloads for this save page request.
259   // If we want to proceed with the file download, fail with
260   // DOWNLOAD_THROTTLED. If we don't want to proceed with the file download,
261   // fail with LOADING_FAILED_DOWNLOAD.
262   if (GetPolicy(pending_request_.get()->client_id().name_space)
263           .allows_conversion_to_background_file_download) {
264     should_allow_downloads = true;
265     final_status = Offliner::RequestStatus::DOWNLOAD_THROTTLED;
266   }
267 
268   std::move(callback).Run(should_allow_downloads);
269   SavePageRequest request(*pending_request_.get());
270   std::move(completion_callback_).Run(request, final_status);
271   base::ThreadTaskRunnerHandle::Get()->PostTask(
272       FROM_HERE, base::BindOnce(&BackgroundLoaderOffliner::ResetState,
273                                 weak_ptr_factory_.GetWeakPtr()));
274 }
275 
MarkLoadStartTime()276 void BackgroundLoaderOffliner::MarkLoadStartTime() {
277   load_start_time_ = base::TimeTicks::Now();
278 }
279 
DocumentAvailableInMainFrame()280 void BackgroundLoaderOffliner::DocumentAvailableInMainFrame() {
281   is_low_bar_met_ = true;
282 
283   // Add this signal to signal_data_.
284   AddLoadingSignal("DocumentAvailableInMainFrame");
285 }
286 
DocumentOnLoadCompletedInMainFrame()287 void BackgroundLoaderOffliner::DocumentOnLoadCompletedInMainFrame() {
288   if (!pending_request_.get()) {
289     DVLOG(1) << "DidStopLoading called even though no pending request.";
290     return;
291   }
292 
293   // Add this signal to signal_data_.
294   AddLoadingSignal("DocumentOnLoadCompletedInMainFrame");
295 
296   snapshot_controller_->DocumentOnLoadCompletedInMainFrame();
297 }
298 
RenderProcessGone(base::TerminationStatus status)299 void BackgroundLoaderOffliner::RenderProcessGone(
300     base::TerminationStatus status) {
301   if (pending_request_) {
302     SavePageRequest request(*pending_request_.get());
303     switch (status) {
304       case base::TERMINATION_STATUS_OOM:
305       case base::TERMINATION_STATUS_PROCESS_CRASHED:
306       case base::TERMINATION_STATUS_STILL_RUNNING:
307         std::move(completion_callback_)
308             .Run(request, Offliner::RequestStatus::LOADING_FAILED_NO_NEXT);
309         break;
310       case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
311       default:
312         std::move(completion_callback_)
313             .Run(request, Offliner::RequestStatus::LOADING_FAILED);
314     }
315     ResetState();
316   }
317 }
318 
WebContentsDestroyed()319 void BackgroundLoaderOffliner::WebContentsDestroyed() {
320   if (pending_request_) {
321     SavePageRequest request(*pending_request_.get());
322     std::move(completion_callback_)
323         .Run(request, Offliner::RequestStatus::LOADING_FAILED);
324     ResetState();
325   }
326 }
327 
DidFinishNavigation(content::NavigationHandle * navigation_handle)328 void BackgroundLoaderOffliner::DidFinishNavigation(
329     content::NavigationHandle* navigation_handle) {
330   if (!navigation_handle->IsInMainFrame())
331     return;
332   // If there was an error of any kind (certificate, client, DNS, etc),
333   // Mark as error page. Resetting here causes RecordNavigationMetrics to crash.
334   if (navigation_handle->IsErrorPage()) {
335     RecordErrorCauseUMA(pending_request_->client_id(),
336                         static_cast<int>(navigation_handle->GetNetErrorCode()));
337     page_load_state_ = RETRIABLE_NET_ERROR;
338   } else {
339     int status_code = 200;  // Default to OK.
340     // No response header can imply intermediate navigation state.
341     if (navigation_handle->GetResponseHeaders())
342       status_code = navigation_handle->GetResponseHeaders()->response_code();
343     // 2XX and 3XX are ok because they indicate success or redirection.
344     // We track 301 because it's MOVED_PERMANENTLY and usually accompanies an
345     // error page with new address.
346     // 400+ codes are client and server errors.
347     // We skip 418 because it's a teapot.
348     if (status_code == 301 || (status_code >= 400 && status_code != 418)) {
349       RecordErrorCauseUMA(pending_request_->client_id(), status_code);
350       page_load_state_ = RETRIABLE_HTTP_ERROR;
351     }
352   }
353 
354   PreviewsUITabHelper* previews_tab_helper =
355       PreviewsUITabHelper::FromWebContents(navigation_handle->GetWebContents());
356   blink::PreviewsState previews_state = blink::PreviewsTypes::PREVIEWS_OFF;
357   if (previews_tab_helper) {
358     previews::PreviewsUserData* previews_user_data =
359         previews_tab_helper->GetPreviewsUserData(navigation_handle);
360     if (previews_user_data)
361       previews_state = previews_user_data->CommittedPreviewsState();
362   }
363 
364   RecordOffliningPreviewsUMA(pending_request_->client_id(), previews_state);
365 }
366 
SetBackgroundSnapshotControllerForTest(std::unique_ptr<BackgroundSnapshotController> controller)367 void BackgroundLoaderOffliner::SetBackgroundSnapshotControllerForTest(
368     std::unique_ptr<BackgroundSnapshotController> controller) {
369   snapshot_controller_ = std::move(controller);
370 }
371 
ObserveResourceLoading(ResourceLoadingObserver::ResourceDataType type,bool started)372 void BackgroundLoaderOffliner::ObserveResourceLoading(
373     ResourceLoadingObserver::ResourceDataType type,
374     bool started) {
375   // Add the signal to extra data, and use for tracking.
376 
377   RequestStats& found_stats = stats_[type];
378   if (started)
379     ++found_stats.requested;
380   else
381     ++found_stats.completed;
382 }
383 
OnNetworkBytesChanged(int64_t bytes)384 void BackgroundLoaderOffliner::OnNetworkBytesChanged(int64_t bytes) {
385   if (pending_request_ && save_state_ != SAVING) {
386     network_bytes_ += bytes;
387     progress_callback_.Run(*pending_request_, network_bytes_);
388   }
389 }
390 
StartSnapshot()391 void BackgroundLoaderOffliner::StartSnapshot() {
392   if (!pending_request_.get()) {
393     DVLOG(1) << "Pending request was cleared during delay.";
394     return;
395   }
396   DCHECK(is_low_bar_met_)
397       << "Minimum quality must have been reached before saving a snapshot";
398 
399   // Add this signal to signal_data_.
400   AddLoadingSignal("Snapshotting");
401 
402   SavePageRequest request(*pending_request_.get());
403   // If there was an error navigating to page, return loading failed.
404   if (page_load_state_ != SUCCESS) {
405     Offliner::RequestStatus status;
406     switch (page_load_state_) {
407       case RETRIABLE_NET_ERROR:
408         status = Offliner::RequestStatus::LOADING_FAILED_NET_ERROR;
409         break;
410       case RETRIABLE_HTTP_ERROR:
411         status = Offliner::RequestStatus::LOADING_FAILED_HTTP_ERROR;
412         break;
413       case NONRETRIABLE:
414         status = Offliner::RequestStatus::LOADING_FAILED_NO_RETRY;
415         break;
416       default:
417         // We should've already checked for Success before entering here.
418         NOTREACHED();
419         status = Offliner::RequestStatus::LOADING_FAILED;
420     }
421 
422     std::move(completion_callback_).Run(request, status);
423     ResetState();
424     return;
425   }
426 
427   content::WebContents* web_contents(
428       content::WebContentsObserver::web_contents());
429 
430   Offliner::RequestStatus loaded_page_error =
431       CanSavePageInBackground(web_contents);
432   if (loaded_page_error != Offliner::RequestStatus::UNKNOWN) {
433     std::move(completion_callback_).Run(request, loaded_page_error);
434     ResetState();
435     return;
436   }
437 
438   save_state_ = SAVING;
439 
440   // Capture loading signals to UMA.
441   RequestStats& image_stats = stats_[ResourceDataType::IMAGE];
442   RequestStats& css_stats = stats_[ResourceDataType::TEXT_CSS];
443   RequestStats& xhr_stats = stats_[ResourceDataType::XHR];
444 
445   // Add loading signal into the MHTML that will be generated if the command
446   // line flag is set for it.
447   if (IsOfflinePagesLoadSignalCollectingEnabled()) {
448     // Write resource percentage signal data into extra data before emitting it
449     // to the MHTML.
450     signal_data_.SetDouble("StartedImages", image_stats.requested);
451     signal_data_.SetDouble("CompletedImages", image_stats.completed);
452     signal_data_.SetDouble("StartedCSS", css_stats.requested);
453     signal_data_.SetDouble("CompletedCSS", css_stats.completed);
454     signal_data_.SetDouble("StartedXHR", xhr_stats.requested);
455     signal_data_.SetDouble("CompletedXHR", xhr_stats.completed);
456 
457     // Stash loading signals for writing when we write out the MHTML.
458     std::string headers = base::StringPrintf(
459         "%s\r\n%s\r\n\r\n", kContentTransferEncodingBinary, kXHeaderForSignals);
460     std::string body;
461     base::JSONWriter::Write(signal_data_, &body);
462     std::string content_type = kContentType;
463     std::string content_location = base::StringPrintf(
464         "cid:signal-data-%" PRId64 "@mhtml.blink", request.request_id());
465 
466     content::MHTMLExtraParts* extra_parts =
467         content::MHTMLExtraParts::FromWebContents(web_contents);
468     DCHECK(extra_parts);
469     if (extra_parts != nullptr) {
470       extra_parts->AddExtraMHTMLPart(content_type, content_location, headers,
471                                      body);
472     }
473   }
474 
475   std::unique_ptr<OfflinePageArchiver> archiver(new OfflinePageMHTMLArchiver());
476 
477   OfflinePageModel::SavePageParams params;
478   params.url = web_contents->GetLastCommittedURL();
479   params.client_id = request.client_id();
480   params.proposed_offline_id = request.request_id();
481   params.is_background = true;
482   params.use_page_problem_detectors = true;
483   params.request_origin = request.request_origin();
484 
485   // Pass in the original URL if it's different from last committed
486   // when redirects occur.
487   if (!request.original_url().is_empty())
488     params.original_url = request.original_url();
489   else if (params.url != request.url())
490     params.original_url = request.url();
491 
492   offline_page_model_->SavePage(
493       params, std::move(archiver), web_contents,
494       base::BindOnce(&BackgroundLoaderOffliner::OnPageSaved,
495                      weak_ptr_factory_.GetWeakPtr()));
496 }
497 
RunRenovations()498 void BackgroundLoaderOffliner::RunRenovations() {
499   if (page_renovator_) {
500     page_renovator_->RunRenovations(
501         base::BindOnce(&BackgroundLoaderOffliner::RenovationsCompleted,
502                        weak_ptr_factory_.GetWeakPtr()));
503   }
504 }
505 
OnPageSaved(SavePageResult save_result,int64_t offline_id)506 void BackgroundLoaderOffliner::OnPageSaved(SavePageResult save_result,
507                                            int64_t offline_id) {
508   if (!pending_request_)
509     return;
510 
511   SavePageRequest request(*pending_request_.get());
512   bool did_snapshot_on_last_retry = did_snapshot_on_last_retry_;
513   ResetState();
514 
515   if (save_state_ == DELETE_AFTER_SAVE) {
516     // Delete the saved page off disk and from the OPM.
517     PageCriteria criteria;
518     criteria.offline_ids = std::vector<int64_t>{offline_id};
519     offline_page_model_->DeletePagesWithCriteria(
520         criteria,
521         base::BindOnce(&BackgroundLoaderOffliner::DeleteOfflinePageCallback,
522                        weak_ptr_factory_.GetWeakPtr(), request));
523     save_state_ = NONE;
524     return;
525   }
526 
527   save_state_ = NONE;
528 
529   Offliner::RequestStatus save_status;
530   if (save_result == SavePageResult::ALREADY_EXISTS) {
531     save_status = RequestStatus::SAVED;
532   } else if (save_result == SavePageResult::SUCCESS) {
533     if (did_snapshot_on_last_retry)
534       save_status = RequestStatus::SAVED_ON_LAST_RETRY;
535     else
536       save_status = RequestStatus::SAVED;
537   } else {
538     save_status = RequestStatus::SAVE_FAILED;
539   }
540 
541   std::move(completion_callback_).Run(request, save_status);
542 }
543 
DeleteOfflinePageCallback(const SavePageRequest & request,DeletePageResult result)544 void BackgroundLoaderOffliner::DeleteOfflinePageCallback(
545     const SavePageRequest& request,
546     DeletePageResult result) {
547   std::move(cancel_callback_).Run(request);
548 }
549 
ResetState()550 void BackgroundLoaderOffliner::ResetState() {
551   pending_request_.reset();
552   // Stop snapshot controller from triggering any more events.
553   snapshot_controller_->Stop();
554   // Delete the snapshot controller after stack unwinds, so we don't
555   // corrupt stack in some edge cases. Deleting it soon should be safe because
556   // we check against pending_request_ with every action, and snapshot
557   // controller is configured to only call StartSnapshot once for BGL.
558   base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
559       FROM_HERE, snapshot_controller_.release());
560   page_load_state_ = SUCCESS;
561   network_bytes_ = 0LL;
562   is_low_bar_met_ = false;
563   did_snapshot_on_last_retry_ = false;
564   content::WebContentsObserver::Observe(nullptr);
565   loader_.reset();
566 
567   for (int i = 0; i < ResourceDataType::RESOURCE_DATA_TYPE_COUNT; ++i) {
568     stats_[i].requested = 0;
569     stats_[i].completed = 0;
570   }
571 }
572 
ResetLoader()573 void BackgroundLoaderOffliner::ResetLoader() {
574   loader_.reset(
575       new background_loader::BackgroundLoaderContents(browser_context_));
576   loader_->SetDelegate(this);
577 }
578 
AttachObservers()579 void BackgroundLoaderOffliner::AttachObservers() {
580   content::WebContents* contents = loader_->web_contents();
581   content::WebContentsObserver::Observe(contents);
582   OfflinerUserData::AddToWebContents(contents, this);
583 }
584 
AddLoadingSignal(const char * signal_name)585 void BackgroundLoaderOffliner::AddLoadingSignal(const char* signal_name) {
586   base::TimeTicks current_time = base::TimeTicks::Now();
587   base::TimeDelta delay_so_far = current_time - load_start_time_;
588   // We would prefer to use int64_t here, but JSON does not support that type.
589   // Given the choice between int and double, we choose to implicitly convert to
590   // a double since it maintains more precision (we can get a longer time in
591   // milliseconds than we can with a 2 bit int, 53 bits vs 32).
592   signal_data_.SetDouble(signal_name, delay_so_far.InMillisecondsF());
593 }
594 
RenovationsCompleted()595 void BackgroundLoaderOffliner::RenovationsCompleted() {
596   snapshot_controller_->RenovationsCompleted();
597 }
598 
CanSavePageInBackground(content::WebContents * web_contents)599 Offliner::RequestStatus BackgroundLoaderOffliner::CanSavePageInBackground(
600     content::WebContents* web_contents) {
601   DCHECK(is_low_bar_met_)
602       << "Minimum quality must have been reached before checking loaded page";
603   std::unique_ptr<security_state::VisibleSecurityState> visible_security_state =
604       GetVisibleSecurityState(web_contents);
605   // Checks for HTTPS certificate errors (HTTP connections are not affected).
606   if (security_state::HasMajorCertificateError(*visible_security_state))
607     return Offliner::RequestStatus::LOADED_PAGE_HAS_CERTIFICATE_ERROR;
608 
609   // Checks if the page is blocked by SafeBrowsing.
610   if (visible_security_state->malicious_content_status !=
611       security_state::MaliciousContentStatus::MALICIOUS_CONTENT_STATUS_NONE) {
612     return Offliner::RequestStatus::LOADED_PAGE_IS_BLOCKED;
613   }
614 
615   // Don't save Chrome error or interstitial pages.
616   if (GetPageType(web_contents) != content::PageType::PAGE_TYPE_NORMAL)
617     return Offliner::RequestStatus::LOADED_PAGE_IS_CHROME_INTERNAL;
618 
619   return Offliner::RequestStatus::UNKNOWN;
620 }
621 
622 std::unique_ptr<security_state::VisibleSecurityState>
GetVisibleSecurityState(content::WebContents * web_contents)623 BackgroundLoaderOffliner::GetVisibleSecurityState(
624     content::WebContents* web_contents) {
625   // Note: this tab helper needs to be created here as in the background it is
626   // not created by default.
627   SecurityStateTabHelper::CreateForWebContents(web_contents);
628   SecurityStateTabHelper* helper =
629       SecurityStateTabHelper::FromWebContents(web_contents);
630   DCHECK(helper);
631   return helper->GetVisibleSecurityState();
632 }
633 
GetPageType(content::WebContents * web_contents)634 content::PageType BackgroundLoaderOffliner::GetPageType(
635     content::WebContents* web_contents) {
636   DCHECK(web_contents->GetController().GetVisibleEntry())
637       << "An entry must have committed at this WebContents";
638   return web_contents->GetController().GetVisibleEntry()->GetPageType();
639 }
640 
641 }  // namespace offline_pages
642