Index: chrome/browser/android/offline_pages/background_loader_offliner.cc |
diff --git a/chrome/browser/android/offline_pages/background_loader_offliner.cc b/chrome/browser/android/offline_pages/background_loader_offliner.cc |
deleted file mode 100644 |
index b2fc873f41f19a28d41c4e6e0346492aa7528c5c..0000000000000000000000000000000000000000 |
--- a/chrome/browser/android/offline_pages/background_loader_offliner.cc |
+++ /dev/null |
@@ -1,498 +0,0 @@ |
-// Copyright 2016 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "chrome/browser/android/offline_pages/background_loader_offliner.h" |
- |
-#include "base/bind.h" |
-#include "base/json/json_writer.h" |
-#include "base/metrics/histogram_macros.h" |
-#include "base/sys_info.h" |
-#include "base/threading/thread_task_runner_handle.h" |
-#include "base/time/time.h" |
-#include "chrome/browser/android/offline_pages/offline_page_mhtml_archiver.h" |
-#include "chrome/browser/android/offline_pages/offliner_helper.h" |
-#include "chrome/browser/profiles/profile.h" |
-#include "components/offline_pages/core/background/offliner_policy.h" |
-#include "components/offline_pages/core/background/save_page_request.h" |
-#include "components/offline_pages/core/client_namespace_constants.h" |
-#include "components/offline_pages/core/offline_page_feature.h" |
-#include "components/offline_pages/core/offline_page_model.h" |
-#include "content/public/browser/browser_context.h" |
-#include "content/public/browser/mhtml_extra_parts.h" |
-#include "content/public/browser/navigation_handle.h" |
-#include "content/public/browser/render_frame_host.h" |
-#include "content/public/browser/web_contents.h" |
-#include "content/public/browser/web_contents_user_data.h" |
- |
-namespace offline_pages { |
- |
-namespace { |
-const char kContentType[] = "text/plain"; |
-const char kContentTransferEncodingBinary[] = |
- "Content-Transfer-Encoding: binary"; |
-const char kXHeaderForSignals[] = "X-Chrome-Loading-Metrics-Data: 1"; |
- |
-class OfflinerData : public content::WebContentsUserData<OfflinerData> { |
- public: |
- static void AddToWebContents(content::WebContents* webcontents, |
- BackgroundLoaderOffliner* offliner) { |
- DCHECK(offliner); |
- webcontents->SetUserData(UserDataKey(), std::unique_ptr<OfflinerData>( |
- new OfflinerData(offliner))); |
- } |
- |
- explicit OfflinerData(BackgroundLoaderOffliner* offliner) { |
- offliner_ = offliner; |
- } |
- BackgroundLoaderOffliner* offliner() { return offliner_; } |
- |
- private: |
- // The offliner that the WebContents is attached to. The offliner owns the |
- // Delegate which owns the WebContents that this data is attached to. |
- // Therefore, its lifetime should exceed that of the WebContents, so this |
- // should always be non-null. |
- BackgroundLoaderOffliner* offliner_; |
-}; |
- |
-std::string AddHistogramSuffix(const ClientId& client_id, |
- const char* histogram_name) { |
- if (client_id.name_space.empty()) { |
- NOTREACHED(); |
- return histogram_name; |
- } |
- std::string adjusted_histogram_name(histogram_name); |
- adjusted_histogram_name += "." + client_id.name_space; |
- return adjusted_histogram_name; |
-} |
- |
-void RecordErrorCauseUMA(const ClientId& client_id, net::Error error_code) { |
- UMA_HISTOGRAM_SPARSE_SLOWLY( |
- AddHistogramSuffix(client_id, |
- "OfflinePages.Background.BackgroundLoadingFailedCode"), |
- std::abs(error_code)); |
-} |
- |
-void HandleApplicationStateChangeCancel( |
- const Offliner::CompletionCallback& completion_callback, |
- const SavePageRequest& canceled_request) { |
- completion_callback.Run(canceled_request, |
- Offliner::RequestStatus::FOREGROUND_CANCELED); |
-} |
- |
-} // namespace |
- |
-BackgroundLoaderOffliner::BackgroundLoaderOffliner( |
- content::BrowserContext* browser_context, |
- const OfflinerPolicy* policy, |
- OfflinePageModel* offline_page_model) |
- : browser_context_(browser_context), |
- offline_page_model_(offline_page_model), |
- policy_(policy), |
- is_low_end_device_(base::SysInfo::IsLowEndDevice()), |
- save_state_(NONE), |
- page_load_state_(SUCCESS), |
- network_bytes_(0LL), |
- is_low_bar_met_(false), |
- did_snapshot_on_last_retry_(false), |
- weak_ptr_factory_(this) { |
- DCHECK(offline_page_model_); |
- DCHECK(browser_context_); |
-} |
- |
-BackgroundLoaderOffliner::~BackgroundLoaderOffliner() {} |
- |
-// static |
-BackgroundLoaderOffliner* BackgroundLoaderOffliner::FromWebContents( |
- content::WebContents* contents) { |
- OfflinerData* data = OfflinerData::FromWebContents(contents); |
- if (data) |
- return data->offliner(); |
- return nullptr; |
-} |
- |
-bool BackgroundLoaderOffliner::LoadAndSave( |
- const SavePageRequest& request, |
- const CompletionCallback& completion_callback, |
- const ProgressCallback& progress_callback) { |
- DCHECK(completion_callback); |
- DCHECK(progress_callback); |
- DCHECK(offline_page_model_); |
- |
- if (pending_request_) { |
- DVLOG(1) << "Already have pending request"; |
- return false; |
- } |
- |
- ClientPolicyController* policy_controller = |
- offline_page_model_->GetPolicyController(); |
- if (policy_controller->IsDisabledWhenPrefetchDisabled( |
- request.client_id().name_space) && |
- (AreThirdPartyCookiesBlocked(browser_context_) || |
- IsNetworkPredictionDisabled(browser_context_))) { |
- DVLOG(1) << "WARNING: Unable to load when 3rd party cookies blocked or " |
- << "prediction disabled"; |
- // Record user metrics for third party cookies being disabled or network |
- // prediction being disabled. |
- if (AreThirdPartyCookiesBlocked(browser_context_)) { |
- UMA_HISTOGRAM_ENUMERATION( |
- "OfflinePages.Background.CctApiDisableStatus", |
- static_cast<int>(OfflinePagesCctApiPrerenderAllowedStatus:: |
- THIRD_PARTY_COOKIES_DISABLED), |
- static_cast<int>(OfflinePagesCctApiPrerenderAllowedStatus:: |
- NETWORK_PREDICTION_DISABLED) + |
- 1); |
- } |
- if (IsNetworkPredictionDisabled(browser_context_)) { |
- UMA_HISTOGRAM_ENUMERATION( |
- "OfflinePages.Background.CctApiDisableStatus", |
- static_cast<int>(OfflinePagesCctApiPrerenderAllowedStatus:: |
- NETWORK_PREDICTION_DISABLED), |
- static_cast<int>(OfflinePagesCctApiPrerenderAllowedStatus:: |
- NETWORK_PREDICTION_DISABLED) + |
- 1); |
- } |
- |
- return false; |
- } |
- |
- // Record UMA that the load was allowed to proceed. |
- if (request.client_id().name_space == kCCTNamespace) { |
- UMA_HISTOGRAM_ENUMERATION( |
- "OfflinePages.Background.CctApiDisableStatus", |
- static_cast<int>( |
- OfflinePagesCctApiPrerenderAllowedStatus::PRERENDER_ALLOWED), |
- static_cast<int>(OfflinePagesCctApiPrerenderAllowedStatus:: |
- NETWORK_PREDICTION_DISABLED) + |
- 1); |
- } |
- |
- if (!OfflinePageModel::CanSaveURL(request.url())) { |
- DVLOG(1) << "Not able to save page for requested url: " << request.url(); |
- return false; |
- } |
- |
- ResetLoader(); |
- AttachObservers(); |
- |
- MarkLoadStartTime(); |
- |
- // Track copy of pending request. |
- pending_request_.reset(new SavePageRequest(request)); |
- completion_callback_ = completion_callback; |
- progress_callback_ = progress_callback; |
- |
- // Listen for app foreground/background change. |
- app_listener_.reset(new base::android::ApplicationStatusListener( |
- base::Bind(&BackgroundLoaderOffliner::OnApplicationStateChange, |
- weak_ptr_factory_.GetWeakPtr()))); |
- |
- // Load page attempt. |
- loader_.get()->LoadPage(request.url()); |
- |
- snapshot_controller_ = SnapshotController::CreateForBackgroundOfflining( |
- base::ThreadTaskRunnerHandle::Get(), this); |
- |
- return true; |
-} |
- |
-bool BackgroundLoaderOffliner::Cancel(const CancelCallback& callback) { |
- DCHECK(pending_request_); |
- // We ignore the case where pending_request_ is not set, but given the checks |
- // in RequestCoordinator this should not happen. |
- if (!pending_request_) |
- return false; |
- |
- // TODO(chili): We are not able to cancel a pending |
- // OfflinePageModel::SaveSnapshot() operation. We will notify caller that |
- // cancel completed once the SavePage operation returns. |
- if (save_state_ != NONE) { |
- save_state_ = DELETE_AFTER_SAVE; |
- cancel_callback_ = callback; |
- return true; |
- } |
- |
- // Post the cancel callback right after this call concludes. |
- base::ThreadTaskRunnerHandle::Get()->PostTask( |
- FROM_HERE, base::Bind(callback, *pending_request_.get())); |
- ResetState(); |
- return true; |
-} |
- |
-bool BackgroundLoaderOffliner::HandleTimeout(int64_t request_id) { |
- if (pending_request_) { |
- DCHECK(request_id == pending_request_->request_id()); |
- if (is_low_bar_met_ && (pending_request_->started_attempt_count() + 1 >= |
- policy_->GetMaxStartedTries() || |
- pending_request_->completed_attempt_count() + 1 >= |
- policy_->GetMaxCompletedTries())) { |
- // If we are already in the middle of a save operation, let it finish |
- // but do not return SAVED_ON_LAST_RETRY |
- if (save_state_ == NONE) { |
- did_snapshot_on_last_retry_ = true; |
- StartSnapshot(); |
- } |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-void BackgroundLoaderOffliner::MarkLoadStartTime() { |
- load_start_time_ = base::TimeTicks::Now(); |
-} |
- |
-void BackgroundLoaderOffliner::DocumentAvailableInMainFrame() { |
- snapshot_controller_->DocumentAvailableInMainFrame(); |
- is_low_bar_met_ = true; |
- |
- // Add this signal to signal_data_. |
- AddLoadingSignal("DocumentAvailableInMainFrame"); |
-} |
- |
-void BackgroundLoaderOffliner::DocumentOnLoadCompletedInMainFrame() { |
- if (!pending_request_.get()) { |
- DVLOG(1) << "DidStopLoading called even though no pending request."; |
- return; |
- } |
- |
- // Add this signal to signal_data_. |
- AddLoadingSignal("DocumentOnLoadCompletedInMainFrame"); |
- |
- snapshot_controller_->DocumentOnLoadCompletedInMainFrame(); |
-} |
- |
-void BackgroundLoaderOffliner::RenderProcessGone( |
- base::TerminationStatus status) { |
- if (pending_request_) { |
- SavePageRequest request(*pending_request_.get()); |
- switch (status) { |
- case base::TERMINATION_STATUS_OOM: |
- case base::TERMINATION_STATUS_PROCESS_CRASHED: |
- case base::TERMINATION_STATUS_STILL_RUNNING: |
- completion_callback_.Run( |
- request, Offliner::RequestStatus::LOADING_FAILED_NO_NEXT); |
- break; |
- case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: |
- default: |
- completion_callback_.Run(request, |
- Offliner::RequestStatus::LOADING_FAILED); |
- } |
- ResetState(); |
- } |
-} |
- |
-void BackgroundLoaderOffliner::WebContentsDestroyed() { |
- if (pending_request_) { |
- SavePageRequest request(*pending_request_.get()); |
- completion_callback_.Run(request, Offliner::RequestStatus::LOADING_FAILED); |
- ResetState(); |
- } |
-} |
- |
-void BackgroundLoaderOffliner::DidFinishNavigation( |
- content::NavigationHandle* navigation_handle) { |
- if (!navigation_handle->IsInMainFrame()) |
- return; |
- // If there was an error of any kind (certificate, client, DNS, etc), |
- // Mark as error page. Resetting here causes RecordNavigationMetrics to crash. |
- if (navigation_handle->IsErrorPage()) { |
- RecordErrorCauseUMA(pending_request_->client_id(), |
- navigation_handle->GetNetErrorCode()); |
- switch (navigation_handle->GetNetErrorCode()) { |
- case net::ERR_INTERNET_DISCONNECTED: |
- page_load_state_ = DELAY_RETRY; |
- break; |
- default: |
- page_load_state_ = RETRIABLE; |
- } |
- } |
-} |
- |
-void BackgroundLoaderOffliner::SetSnapshotControllerForTest( |
- std::unique_ptr<SnapshotController> controller) { |
- snapshot_controller_ = std::move(controller); |
-} |
- |
-void BackgroundLoaderOffliner::OnNetworkBytesChanged(int64_t bytes) { |
- if (pending_request_ && save_state_ != SAVING) { |
- network_bytes_ += bytes; |
- progress_callback_.Run(*pending_request_, network_bytes_); |
- } |
-} |
- |
-void BackgroundLoaderOffliner::StartSnapshot() { |
- if (!pending_request_.get()) { |
- DVLOG(1) << "Pending request was cleared during delay."; |
- return; |
- } |
- |
- // Add this signal to signal_data_. |
- AddLoadingSignal("Snapshotting"); |
- |
- SavePageRequest request(*pending_request_.get()); |
- // If there was an error navigating to page, return loading failed. |
- if (page_load_state_ != SUCCESS) { |
- Offliner::RequestStatus status; |
- switch (page_load_state_) { |
- case RETRIABLE: |
- status = Offliner::RequestStatus::LOADING_FAILED; |
- break; |
- case NONRETRIABLE: |
- status = Offliner::RequestStatus::LOADING_FAILED_NO_RETRY; |
- break; |
- case DELAY_RETRY: |
- status = Offliner::RequestStatus::LOADING_FAILED_NO_NEXT; |
- break; |
- default: |
- // We should've already checked for Success before entering here. |
- NOTREACHED(); |
- status = Offliner::RequestStatus::LOADING_FAILED; |
- } |
- |
- completion_callback_.Run(request, status); |
- ResetState(); |
- return; |
- } |
- |
- save_state_ = SAVING; |
- content::WebContents* web_contents( |
- content::WebContentsObserver::web_contents()); |
- |
- // Add loading signal into the MHTML that will be generated if the command |
- // line flag is set for it. |
- if (IsOfflinePagesLoadSignalCollectingEnabled()) { |
- // Stash loading signals for writing when we write out the MHTML. |
- std::string headers = base::StringPrintf( |
- "%s\r\n%s\r\n\r\n", kContentTransferEncodingBinary, kXHeaderForSignals); |
- std::string body; |
- base::JSONWriter::Write(signal_data_, &body); |
- std::string content_type = kContentType; |
- std::string content_location = base::StringPrintf( |
- "cid:signal-data-%" PRId64 "@mhtml.blink", request.request_id()); |
- |
- content::MHTMLExtraParts* extra_parts = |
- content::MHTMLExtraParts::FromWebContents(web_contents); |
- DCHECK(extra_parts); |
- if (extra_parts != nullptr) { |
- extra_parts->AddExtraMHTMLPart(content_type, content_location, headers, |
- body); |
- } |
- } |
- |
- std::unique_ptr<OfflinePageArchiver> archiver( |
- new OfflinePageMHTMLArchiver(web_contents)); |
- |
- OfflinePageModel::SavePageParams params; |
- params.url = web_contents->GetLastCommittedURL(); |
- params.client_id = request.client_id(); |
- params.proposed_offline_id = request.request_id(); |
- params.is_background = true; |
- |
- // Pass in the original URL if it's different from last committed |
- // when redirects occur. |
- if (!request.original_url().is_empty()) |
- params.original_url = request.original_url(); |
- else if (params.url != request.url()) |
- params.original_url = request.url(); |
- |
- offline_page_model_->SavePage( |
- params, std::move(archiver), |
- base::Bind(&BackgroundLoaderOffliner::OnPageSaved, |
- weak_ptr_factory_.GetWeakPtr())); |
-} |
- |
-void BackgroundLoaderOffliner::OnPageSaved(SavePageResult save_result, |
- int64_t offline_id) { |
- if (!pending_request_) |
- return; |
- |
- SavePageRequest request(*pending_request_.get()); |
- bool did_snapshot_on_last_retry = did_snapshot_on_last_retry_; |
- ResetState(); |
- |
- if (save_state_ == DELETE_AFTER_SAVE) { |
- // Delete the saved page off disk and from the OPM. |
- std::vector<int64_t> offline_ids; |
- offline_ids.push_back(offline_id); |
- offline_page_model_->DeletePagesByOfflineId( |
- offline_ids, |
- base::Bind(&BackgroundLoaderOffliner::DeleteOfflinePageCallback, |
- weak_ptr_factory_.GetWeakPtr(), request)); |
- save_state_ = NONE; |
- return; |
- } |
- |
- save_state_ = NONE; |
- |
- Offliner::RequestStatus save_status; |
- if (save_result == SavePageResult::ALREADY_EXISTS) { |
- save_status = RequestStatus::SAVED; |
- } else if (save_result == SavePageResult::SUCCESS) { |
- if (did_snapshot_on_last_retry) |
- save_status = RequestStatus::SAVED_ON_LAST_RETRY; |
- else |
- save_status = RequestStatus::SAVED; |
- } else { |
- save_status = RequestStatus::SAVE_FAILED; |
- } |
- |
- completion_callback_.Run(request, save_status); |
-} |
- |
-void BackgroundLoaderOffliner::DeleteOfflinePageCallback( |
- const SavePageRequest& request, |
- DeletePageResult result) { |
- cancel_callback_.Run(request); |
-} |
- |
-void BackgroundLoaderOffliner::ResetState() { |
- pending_request_.reset(); |
- snapshot_controller_.reset(); |
- page_load_state_ = SUCCESS; |
- network_bytes_ = 0LL; |
- is_low_bar_met_ = false; |
- did_snapshot_on_last_retry_ = false; |
- content::WebContentsObserver::Observe(nullptr); |
- loader_.reset(); |
-} |
- |
-void BackgroundLoaderOffliner::ResetLoader() { |
- loader_.reset( |
- new background_loader::BackgroundLoaderContents(browser_context_)); |
-} |
- |
-void BackgroundLoaderOffliner::AttachObservers() { |
- content::WebContents* contents = loader_->web_contents(); |
- content::WebContentsObserver::Observe(contents); |
- OfflinerData::AddToWebContents(contents, this); |
-} |
- |
-void BackgroundLoaderOffliner::OnApplicationStateChange( |
- base::android::ApplicationState application_state) { |
- if (pending_request_ && is_low_end_device_ && |
- application_state == |
- base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES) { |
- DVLOG(1) << "App became active, canceling current offlining request"; |
- // No need to check the return value or complete early, as false would |
- // indicate that there was no request, in which case the state change is |
- // ignored. |
- Cancel( |
- base::Bind(HandleApplicationStateChangeCancel, completion_callback_)); |
- } |
-} |
- |
-void BackgroundLoaderOffliner::AddLoadingSignal(const char* signal_name) { |
- base::TimeTicks current_time = base::TimeTicks::Now(); |
- base::TimeDelta delay_so_far = current_time - load_start_time_; |
- // We would prefer to use int64_t here, but JSON does not support that type. |
- // Given the choice between int and double, we choose to implicitly convert to |
- // a double since it maintains more precision (we can get a longer time in |
- // milliseconds than we can with a 2 bit int, 53 bits vs 32). |
- double delay = delay_so_far.InMilliseconds(); |
- signal_data_.SetDouble(signal_name, delay); |
-} |
- |
-} // namespace offline_pages |
- |
-DEFINE_WEB_CONTENTS_USER_DATA_KEY(offline_pages::OfflinerData); |