Index: components/offline_pages/background/request_coordinator.cc |
diff --git a/components/offline_pages/background/request_coordinator.cc b/components/offline_pages/background/request_coordinator.cc |
deleted file mode 100644 |
index 8dcc4d94def87f3cd13e2d2d56eefe69908e5153..0000000000000000000000000000000000000000 |
--- a/components/offline_pages/background/request_coordinator.cc |
+++ /dev/null |
@@ -1,965 +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 "components/offline_pages/background/request_coordinator.h" |
- |
-#include <limits> |
-#include <utility> |
- |
-#include "base/bind.h" |
-#include "base/callback.h" |
-#include "base/logging.h" |
-#include "base/metrics/histogram_macros.h" |
-#include "base/rand_util.h" |
-#include "base/sys_info.h" |
-#include "base/time/time.h" |
-#include "components/offline_pages/background/offliner_factory.h" |
-#include "components/offline_pages/background/offliner_policy.h" |
-#include "components/offline_pages/background/save_page_request.h" |
-#include "components/offline_pages/client_policy_controller.h" |
-#include "components/offline_pages/offline_page_feature.h" |
-#include "components/offline_pages/offline_page_item.h" |
-#include "components/offline_pages/offline_page_model.h" |
- |
-namespace offline_pages { |
- |
-namespace { |
-const bool kUserRequest = true; |
-const bool kStartOfProcessing = true; |
-const int kMinDurationSeconds = 1; |
-const int kMaxDurationSeconds = 7 * 24 * 60 * 60; // 7 days |
-const int kDurationBuckets = 50; |
-const int kDisabledTaskRecheckSeconds = 5; |
- |
-// TODO(dougarnett): Move to util location and share with model impl. |
-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; |
-} |
- |
-// Records the final request status UMA for an offlining request. This should |
-// only be called once per Offliner::LoadAndSave request. |
-void RecordOfflinerResultUMA(const ClientId& client_id, |
- const base::Time& request_creation_time, |
- Offliner::RequestStatus request_status) { |
- // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION |
- // macro adapted to allow for a dynamically suffixed histogram name. |
- // Note: The factory creates and owns the histogram. |
- base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( |
- AddHistogramSuffix(client_id, |
- "OfflinePages.Background.OfflinerRequestStatus"), |
- 1, static_cast<int>(Offliner::RequestStatus::STATUS_COUNT), |
- static_cast<int>(Offliner::RequestStatus::STATUS_COUNT) + 1, |
- base::HistogramBase::kUmaTargetedHistogramFlag); |
- histogram->Add(static_cast<int>(request_status)); |
- |
- // For successful requests also record time from request to save. |
- if (request_status == Offliner::RequestStatus::SAVED) { |
- // Using regular histogram (with dynamic suffix) rather than time-oriented |
- // one to record samples in seconds rather than milliseconds. |
- base::HistogramBase* histogram = base::Histogram::FactoryGet( |
- AddHistogramSuffix(client_id, "OfflinePages.Background.TimeToSaved"), |
- kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets, |
- base::HistogramBase::kUmaTargetedHistogramFlag); |
- base::TimeDelta duration = base::Time::Now() - request_creation_time; |
- histogram->Add(duration.InSeconds()); |
- } |
-} |
- |
-void RecordStartTimeUMA(const SavePageRequest& request) { |
- std::string histogram_name("OfflinePages.Background.TimeToStart"); |
- if (base::SysInfo::IsLowEndDevice()) { |
- histogram_name += ".Svelte"; |
- } |
- |
- // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_TIMES |
- // macro adapted to allow for a dynamically suffixed histogram name. |
- // Note: The factory creates and owns the histogram. |
- base::HistogramBase* histogram = base::Histogram::FactoryTimeGet( |
- AddHistogramSuffix(request.client_id(), histogram_name.c_str()), |
- base::TimeDelta::FromMilliseconds(100), base::TimeDelta::FromDays(7), 50, |
- base::HistogramBase::kUmaTargetedHistogramFlag); |
- base::TimeDelta duration = base::Time::Now() - request.creation_time(); |
- histogram->AddTime(duration); |
-} |
- |
-void RecordCancelTimeUMA(const SavePageRequest& canceled_request) { |
- // Using regular histogram (with dynamic suffix) rather than time-oriented |
- // one to record samples in seconds rather than milliseconds. |
- base::HistogramBase* histogram = base::Histogram::FactoryGet( |
- AddHistogramSuffix(canceled_request.client_id(), |
- "OfflinePages.Background.TimeToCanceled"), |
- kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets, |
- base::HistogramBase::kUmaTargetedHistogramFlag); |
- base::TimeDelta duration = |
- base::Time::Now() - canceled_request.creation_time(); |
- histogram->Add(duration.InSeconds()); |
-} |
- |
-// Records the number of started attempts for completed requests (whether |
-// successful or not). |
-void RecordAttemptCount(const SavePageRequest& request, |
- RequestNotifier::BackgroundSavePageResult status) { |
- if (status == RequestNotifier::BackgroundSavePageResult::SUCCESS) { |
- // TODO(dougarnett): Also record UMA for completed attempts here. |
- UMA_HISTOGRAM_CUSTOM_COUNTS( |
- "OfflinePages.Background.RequestSuccess.StartedAttemptCount", |
- request.started_attempt_count(), 1, 10, 11); |
- } else { |
- UMA_HISTOGRAM_CUSTOM_COUNTS( |
- "OfflinePages.Background.RequestFailure.StartedAttemptCount", |
- request.started_attempt_count(), 1, 10, 11); |
- } |
-} |
- |
-// Record the network quality at request creation time per namespace. |
-void RecordSavePageLaterNetworkQuality( |
- const ClientId& client_id, |
- const net::EffectiveConnectionType effective_connection) { |
- // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION |
- // macro adapted to allow for a dynamically suffixed histogram name. |
- // Note: The factory creates and owns the histogram. |
- base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( |
- AddHistogramSuffix( |
- client_id, |
- "OfflinePages.Background.EffectiveConnectionType.SavePageLater"), |
- 1, net::EFFECTIVE_CONNECTION_TYPE_LAST - 1, |
- net::EFFECTIVE_CONNECTION_TYPE_LAST, |
- base::HistogramBase::kUmaTargetedHistogramFlag); |
- histogram->Add(effective_connection); |
-} |
- |
-// This should use the same algorithm as we use for OfflinePageItem, so the IDs |
-// are similar. |
-int64_t GenerateOfflineId() { |
- return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1; |
-} |
- |
-// In case we start processing from SavePageLater, we need a callback, but there |
-// is nothing for it to do. |
-void EmptySchedulerCallback(bool started) {} |
- |
-// Returns whether |result| is a successful result for a single request. |
-bool IsSingleSuccessResult(const UpdateRequestsResult* result) { |
- return result->store_state == StoreState::LOADED && |
- result->item_statuses.size() == 1 && |
- result->item_statuses.at(0).second == ItemActionStatus::SUCCESS; |
-} |
- |
-} // namespace |
- |
-RequestCoordinator::RequestCoordinator( |
- std::unique_ptr<OfflinerPolicy> policy, |
- std::unique_ptr<OfflinerFactory> factory, |
- std::unique_ptr<RequestQueue> queue, |
- std::unique_ptr<Scheduler> scheduler, |
- net::NetworkQualityEstimator::NetworkQualityProvider* |
- network_quality_estimator) |
- : is_low_end_device_(base::SysInfo::IsLowEndDevice()), |
- is_busy_(false), |
- is_starting_(false), |
- processing_state_(ProcessingWindowState::STOPPED), |
- use_test_connection_type_(false), |
- test_connection_type_(), |
- offliner_(nullptr), |
- policy_(std::move(policy)), |
- factory_(std::move(factory)), |
- queue_(std::move(queue)), |
- scheduler_(std::move(scheduler)), |
- policy_controller_(new ClientPolicyController()), |
- network_quality_estimator_(network_quality_estimator), |
- active_request_(nullptr), |
- last_offlining_status_(Offliner::RequestStatus::UNKNOWN), |
- scheduler_callback_(base::Bind(&EmptySchedulerCallback)), |
- immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)), |
- weak_ptr_factory_(this) { |
- DCHECK(policy_ != nullptr); |
- std::unique_ptr<PickRequestTaskFactory> picker_factory( |
- new PickRequestTaskFactory(policy_.get(), this, &event_logger_)); |
- queue_->SetPickerFactory(std::move(picker_factory)); |
-} |
- |
-RequestCoordinator::~RequestCoordinator() {} |
- |
-int64_t RequestCoordinator::SavePageLater(const GURL& url, |
- const ClientId& client_id, |
- bool user_requested, |
- RequestAvailability availability) { |
- DVLOG(2) << "URL is " << url << " " << __func__; |
- |
- if (!OfflinePageModel::CanSaveURL(url)) { |
- DVLOG(1) << "Not able to save page for requested url: " << url; |
- return 0L; |
- } |
- |
- int64_t id = GenerateOfflineId(); |
- |
- // Build a SavePageRequest. |
- offline_pages::SavePageRequest request(id, url, client_id, base::Time::Now(), |
- user_requested); |
- |
- // If the download manager is not done with the request, put it on the |
- // disabled list. |
- if (availability == RequestAvailability::DISABLED_FOR_OFFLINER) |
- disabled_requests_.insert(id); |
- |
- // Put the request on the request queue. |
- queue_->AddRequest(request, |
- base::Bind(&RequestCoordinator::AddRequestResultCallback, |
- weak_ptr_factory_.GetWeakPtr(), availability)); |
- |
- // Record the network quality when this request is made. |
- if (network_quality_estimator_) { |
- RecordSavePageLaterNetworkQuality( |
- client_id, network_quality_estimator_->GetEffectiveConnectionType()); |
- } |
- |
- return id; |
-} |
-void RequestCoordinator::GetAllRequests(const GetRequestsCallback& callback) { |
- // Get all matching requests from the request queue, send them to our |
- // callback. We bind the namespace and callback to the front of the callback |
- // param set. |
- queue_->GetRequests(base::Bind(&RequestCoordinator::GetQueuedRequestsCallback, |
- weak_ptr_factory_.GetWeakPtr(), callback)); |
-} |
- |
-void RequestCoordinator::GetQueuedRequestsCallback( |
- const GetRequestsCallback& callback, |
- GetRequestsResult result, |
- std::vector<std::unique_ptr<SavePageRequest>> requests) { |
- callback.Run(std::move(requests)); |
-} |
- |
-void RequestCoordinator::StopPrerendering(Offliner::RequestStatus stop_status) { |
- if (offliner_ && is_busy_) { |
- DCHECK(active_request_.get()); |
- offliner_->Cancel(); |
- |
- if (stop_status == Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT) { |
- // Consider watchdog timeout a completed attempt. |
- SavePageRequest request(*active_request_.get()); |
- UpdateRequestForCompletedAttempt(request, |
- Offliner::REQUEST_COORDINATOR_TIMED_OUT); |
- } else { |
- // Otherwise consider this stop an aborted attempt. |
- UpdateRequestForAbortedAttempt(*active_request_.get()); |
- } |
- } |
- |
- // Stopping offliner means it will not call callback so set last status. |
- last_offlining_status_ = stop_status; |
- |
- if (active_request_) { |
- event_logger_.RecordOfflinerResult(active_request_->client_id().name_space, |
- last_offlining_status_, |
- active_request_->request_id()); |
- RecordOfflinerResultUMA(active_request_->client_id(), |
- active_request_->creation_time(), |
- last_offlining_status_); |
- is_busy_ = false; |
- active_request_.reset(); |
- } |
-} |
- |
-void RequestCoordinator::GetRequestsForSchedulingCallback( |
- GetRequestsResult result, |
- std::vector<std::unique_ptr<SavePageRequest>> requests) { |
- bool user_requested = false; |
- |
- // Examine all requests, if we find a user requested one, we will use the less |
- // restrictive conditions for user_requested requests. Otherwise we will use |
- // the more restrictive non-user-requested conditions. |
- for (const auto& request : requests) { |
- if (request->user_requested()) { |
- user_requested = true; |
- break; |
- } |
- } |
- |
- // In the get callback, determine the least restrictive, and call |
- // GetTriggerConditions based on that. |
- scheduler_->Schedule(GetTriggerConditions(user_requested)); |
-} |
- |
-bool RequestCoordinator::CancelActiveRequestIfItMatches( |
- const std::vector<int64_t>& request_ids) { |
- // If we have a request in progress and need to cancel it, call the |
- // pre-renderer to cancel. TODO Make sure we remove any page created by the |
- // prerenderer if it doesn't get the cancel in time. |
- if (active_request_ != nullptr) { |
- if (request_ids.end() != std::find(request_ids.begin(), request_ids.end(), |
- active_request_->request_id())) { |
- StopPrerendering(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED); |
- active_request_.reset(nullptr); |
- return true; |
- } |
- } |
- |
- return false; |
-} |
- |
-void RequestCoordinator::UpdateRequestForAbortedAttempt( |
- const SavePageRequest& request) { |
- if (request.started_attempt_count() >= policy_->GetMaxStartedTries()) { |
- const RequestNotifier::BackgroundSavePageResult result( |
- RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED); |
- event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space, |
- result, request.request_id()); |
- RemoveAttemptedRequest(request, result); |
- } else { |
- MarkAttemptAborted(request.request_id(), request.client_id().name_space); |
- } |
-} |
- |
-void RequestCoordinator::RemoveAttemptedRequest( |
- const SavePageRequest& request, |
- RequestNotifier::BackgroundSavePageResult result) { |
- std::vector<int64_t> remove_requests; |
- remove_requests.push_back(request.request_id()); |
- queue_->RemoveRequests(remove_requests, |
- base::Bind(&RequestCoordinator::HandleRemovedRequests, |
- weak_ptr_factory_.GetWeakPtr(), result)); |
- RecordAttemptCount(request, result); |
-} |
- |
-void RequestCoordinator::MarkAttemptAborted(int64_t request_id, |
- const std::string& name_space) { |
- queue_->MarkAttemptAborted( |
- request_id, |
- base::Bind(&RequestCoordinator::MarkAttemptDone, |
- weak_ptr_factory_.GetWeakPtr(), request_id, name_space)); |
-} |
- |
-void RequestCoordinator::MarkAttemptDone( |
- int64_t request_id, |
- const std::string& name_space, |
- std::unique_ptr<UpdateRequestsResult> result) { |
- // If the request succeeded, notify observer. If it failed, we can't really |
- // do much, so just log it. |
- if (IsSingleSuccessResult(result.get())) { |
- NotifyChanged(result->updated_items.at(0)); |
- } else { |
- DVLOG(1) << "Failed to mark attempt: " << request_id; |
- UpdateRequestResult request_result = |
- result->store_state != StoreState::LOADED |
- ? UpdateRequestResult::STORE_FAILURE |
- : UpdateRequestResult::REQUEST_DOES_NOT_EXIST; |
- event_logger_.RecordUpdateRequestFailed(name_space, request_result); |
- } |
-} |
- |
-void RequestCoordinator::RemoveRequests( |
- const std::vector<int64_t>& request_ids, |
- const RemoveRequestsCallback& callback) { |
- bool canceled = CancelActiveRequestIfItMatches(request_ids); |
- queue_->RemoveRequests( |
- request_ids, |
- base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback, |
- weak_ptr_factory_.GetWeakPtr(), callback, |
- RequestNotifier::BackgroundSavePageResult::REMOVED)); |
- |
- // Record the network quality when this request is made. |
- if (network_quality_estimator_) { |
- UMA_HISTOGRAM_ENUMERATION( |
- "OfflinePages.Background.EffectiveConnectionType.RemoveRequests", |
- network_quality_estimator_->GetEffectiveConnectionType(), |
- net::EFFECTIVE_CONNECTION_TYPE_LAST); |
- } |
- |
- if (canceled) |
- TryNextRequest(!kStartOfProcessing); |
-} |
- |
-void RequestCoordinator::PauseRequests( |
- const std::vector<int64_t>& request_ids) { |
- bool canceled = CancelActiveRequestIfItMatches(request_ids); |
- queue_->ChangeRequestsState( |
- request_ids, SavePageRequest::RequestState::PAUSED, |
- base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback, |
- weak_ptr_factory_.GetWeakPtr())); |
- |
- // Record the network quality when this request is made. |
- if (network_quality_estimator_) { |
- UMA_HISTOGRAM_ENUMERATION( |
- "OfflinePages.Background.EffectiveConnectionType.PauseRequests", |
- network_quality_estimator_->GetEffectiveConnectionType(), |
- net::EFFECTIVE_CONNECTION_TYPE_LAST); |
- } |
- |
- if (canceled) |
- TryNextRequest(!kStartOfProcessing); |
-} |
- |
-void RequestCoordinator::ResumeRequests( |
- const std::vector<int64_t>& request_ids) { |
- queue_->ChangeRequestsState( |
- request_ids, SavePageRequest::RequestState::AVAILABLE, |
- base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback, |
- weak_ptr_factory_.GetWeakPtr())); |
- |
- // Record the network quality when this request is made. |
- if (network_quality_estimator_) { |
- UMA_HISTOGRAM_ENUMERATION( |
- "OfflinePages.Background.EffectiveConnectionType.ResumeRequests", |
- network_quality_estimator_->GetEffectiveConnectionType(), |
- net::EFFECTIVE_CONNECTION_TYPE_LAST); |
- } |
- |
- // Schedule a task, in case there is not one scheduled. |
- ScheduleAsNeeded(); |
-} |
- |
-net::NetworkChangeNotifier::ConnectionType |
-RequestCoordinator::GetConnectionType() { |
- // If we have a connection type set for test, use that. |
- if (use_test_connection_type_) |
- return test_connection_type_; |
- |
- return net::NetworkChangeNotifier::GetConnectionType(); |
-} |
- |
-void RequestCoordinator::AddRequestResultCallback( |
- RequestAvailability availability, |
- AddRequestResult result, |
- const SavePageRequest& request) { |
- NotifyAdded(request); |
- // Inform the scheduler that we have an outstanding task. |
- scheduler_->Schedule(GetTriggerConditions(kUserRequest)); |
- |
- if (availability == RequestAvailability::DISABLED_FOR_OFFLINER) { |
- // Mark attempt started (presuming it is disabled for background offliner |
- // because foreground offlining is happening). |
- queue_->MarkAttemptStarted( |
- request.request_id(), |
- base::Bind(&RequestCoordinator::MarkAttemptDone, |
- weak_ptr_factory_.GetWeakPtr(), request.request_id(), |
- request.client_id().name_space)); |
- } else if (request.user_requested()) { |
- StartImmediatelyIfConnected(); |
- } |
-} |
- |
-void RequestCoordinator::UpdateMultipleRequestsCallback( |
- std::unique_ptr<UpdateRequestsResult> result) { |
- for (const auto& request : result->updated_items) |
- NotifyChanged(request); |
- |
- bool available_user_request = false; |
- for (const auto& request : result->updated_items) { |
- if (!available_user_request && request.user_requested() && |
- request.request_state() == SavePageRequest::RequestState::AVAILABLE) { |
- available_user_request = true; |
- } |
- } |
- |
- if (available_user_request) |
- StartImmediatelyIfConnected(); |
-} |
- |
-void RequestCoordinator::HandleRemovedRequestsAndCallback( |
- const RemoveRequestsCallback& callback, |
- RequestNotifier::BackgroundSavePageResult status, |
- std::unique_ptr<UpdateRequestsResult> result) { |
- // TODO(dougarnett): Define status code for user/api cancel and use here |
- // to determine whether to record cancel time UMA. |
- for (const auto& request : result->updated_items) |
- RecordCancelTimeUMA(request); |
- callback.Run(result->item_statuses); |
- HandleRemovedRequests(status, std::move(result)); |
-} |
- |
-void RequestCoordinator::HandleRemovedRequests( |
- RequestNotifier::BackgroundSavePageResult status, |
- std::unique_ptr<UpdateRequestsResult> result) { |
- for (const auto& request : result->updated_items) |
- NotifyCompleted(request, status); |
-} |
- |
-void RequestCoordinator::ScheduleAsNeeded() { |
- // Get all requests from queue (there is no filtering mechanism). |
- queue_->GetRequests( |
- base::Bind(&RequestCoordinator::GetRequestsForSchedulingCallback, |
- weak_ptr_factory_.GetWeakPtr())); |
-} |
- |
-void RequestCoordinator::StopProcessing( |
- Offliner::RequestStatus stop_status) { |
- processing_state_ = ProcessingWindowState::STOPPED; |
- StopPrerendering(stop_status); |
- |
- // Let the scheduler know we are done processing. |
- scheduler_callback_.Run(true); |
-} |
- |
-void RequestCoordinator::HandleWatchdogTimeout() { |
- Offliner::RequestStatus watchdog_status = |
- Offliner::REQUEST_COORDINATOR_TIMED_OUT; |
- StopPrerendering(watchdog_status); |
- TryNextRequest(!kStartOfProcessing); |
-} |
- |
-// Returns true if the caller should expect a callback, false otherwise. For |
-// instance, this would return false if a request is already in progress. |
-bool RequestCoordinator::StartProcessing( |
- const DeviceConditions& device_conditions, |
- const base::Callback<void(bool)>& callback) { |
- DVLOG(2) << "Scheduled " << __func__; |
- return StartProcessingInternal(ProcessingWindowState::SCHEDULED_WINDOW, |
- device_conditions, callback); |
-} |
- |
-bool RequestCoordinator::StartProcessingInternal( |
- const ProcessingWindowState processing_state, |
- const DeviceConditions& device_conditions, |
- const base::Callback<void(bool)>& callback) { |
- current_conditions_.reset(new DeviceConditions(device_conditions)); |
- if (is_starting_ || is_busy_) |
- return false; |
- processing_state_ = processing_state; |
- scheduler_callback_ = callback; |
- |
- // Mark the time at which we started processing so we can check our time |
- // budget. |
- operation_start_time_ = base::Time::Now(); |
- |
- TryNextRequest(kStartOfProcessing); |
- |
- return true; |
-} |
- |
-void RequestCoordinator::StartImmediatelyIfConnected() { |
- OfflinerImmediateStartStatus immediate_start_status = TryImmediateStart(); |
- UMA_HISTOGRAM_ENUMERATION( |
- "OfflinePages.Background.ImmediateStartStatus", immediate_start_status, |
- RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT); |
-} |
- |
-RequestCoordinator::OfflinerImmediateStartStatus |
-RequestCoordinator::TryImmediateStart() { |
- DVLOG(2) << "Immediate " << __func__; |
- // Make sure not already busy processing. |
- if (is_busy_) |
- return OfflinerImmediateStartStatus::BUSY; |
- |
- // Make sure we are not on svelte device to start immediately. |
- if (is_low_end_device_ && |
- !offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled()) { |
- DVLOG(2) << "low end device, returning"; |
- // Let the scheduler know we are done processing and failed due to svelte. |
- immediate_schedule_callback_.Run(false); |
- return OfflinerImmediateStartStatus::NOT_STARTED_ON_SVELTE; |
- } |
- |
- if (GetConnectionType() == |
- net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE) |
- return OfflinerImmediateStartStatus::NO_CONNECTION; |
- |
- // Start processing with manufactured conservative battery conditions |
- // (i.e., assume no battery). |
- // TODO(dougarnett): Obtain actual battery conditions (from Android/Java). |
- |
- DeviceConditions device_conditions(false, 0, GetConnectionType()); |
- if (StartProcessingInternal(ProcessingWindowState::IMMEDIATE_WINDOW, |
- device_conditions, immediate_schedule_callback_)) |
- return OfflinerImmediateStartStatus::STARTED; |
- else |
- return OfflinerImmediateStartStatus::NOT_ACCEPTED; |
-} |
- |
-void RequestCoordinator::TryNextRequest(bool is_start_of_processing) { |
- is_starting_ = true; |
- base::TimeDelta processing_time_budget; |
- if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) { |
- processing_time_budget = base::TimeDelta::FromSeconds( |
- policy_->GetProcessingTimeBudgetWhenBackgroundScheduledInSeconds()); |
- } else { |
- DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW); |
- processing_time_budget = base::TimeDelta::FromSeconds( |
- policy_->GetProcessingTimeBudgetForImmediateLoadInSeconds()); |
- } |
- |
- // Determine connection type. If just starting processing, the best source is |
- // from the current device conditions (they are fresh and motivated starting |
- // processing whereas NetworkChangeNotifier may lag reality). |
- net::NetworkChangeNotifier::ConnectionType connection_type; |
- if (is_start_of_processing) |
- connection_type = current_conditions_->GetNetConnectionType(); |
- else |
- connection_type = GetConnectionType(); |
- |
- // If there is no network or no time left in the budget, return to the |
- // scheduler. We do not remove the pending scheduler task that was set |
- // up earlier in case we run out of time, so the background scheduler |
- // will return to us at the next opportunity to run background tasks. |
- if (connection_type == |
- net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE || |
- (base::Time::Now() - operation_start_time_) > processing_time_budget) { |
- is_starting_ = false; |
- |
- // Let the scheduler know we are done processing. |
- // TODO: Make sure the scheduler callback is valid before running it. |
- scheduler_callback_.Run(true); |
- DVLOG(2) << " out of time, giving up. " << __func__; |
- |
- return; |
- } |
- |
- // Ask request queue to make a new PickRequestTask object, then put it on the |
- // task queue. |
- queue_->PickNextRequest( |
- base::Bind(&RequestCoordinator::RequestPicked, |
- weak_ptr_factory_.GetWeakPtr()), |
- base::Bind(&RequestCoordinator::RequestNotPicked, |
- weak_ptr_factory_.GetWeakPtr()), |
- base::Bind(&RequestCoordinator::RequestCounts, |
- weak_ptr_factory_.GetWeakPtr(), is_start_of_processing), |
- *current_conditions_.get(), disabled_requests_); |
- // TODO(petewil): Verify current_conditions has a good value on all calling |
- // paths. It is really more of a "last known conditions" than "current |
- // conditions". Consider having a call to Java to check the current |
- // conditions. |
-} |
- |
-// Called by the request picker when a request has been picked. |
-void RequestCoordinator::RequestPicked(const SavePageRequest& request) { |
- DVLOG(2) << request.url() << " " << __func__; |
- is_starting_ = false; |
- |
- // Make sure we were not stopped while picking. |
- if (processing_state_ != ProcessingWindowState::STOPPED) { |
- // Send the request on to the offliner. |
- SendRequestToOffliner(request); |
- } |
-} |
- |
-void RequestCoordinator::RequestNotPicked( |
- bool non_user_requested_tasks_remaining) { |
- DVLOG(2) << __func__; |
- is_starting_ = false; |
- |
- // Clear the outstanding "safety" task in the scheduler. |
- scheduler_->Unschedule(); |
- |
- // If disabled tasks remain, post a new safety task for 5 sec from now. |
- if (disabled_requests_.size() > 0) { |
- scheduler_->BackupSchedule(GetTriggerConditions(kUserRequest), |
- kDisabledTaskRecheckSeconds); |
- } else if (non_user_requested_tasks_remaining) { |
- // If we don't have any of those, check for non-user-requested tasks. |
- scheduler_->Schedule(GetTriggerConditions(!kUserRequest)); |
- } |
- |
- // Let the scheduler know we are done processing. |
- scheduler_callback_.Run(true); |
-} |
- |
-void RequestCoordinator::RequestCounts(bool is_start_of_processing, |
- size_t total_requests, |
- size_t available_requests) { |
- // Only capture request counts for the start of processing (not for |
- // continued processing in the same window). |
- if (!is_start_of_processing) |
- return; |
- |
- if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) { |
- if (is_low_end_device_) { |
- UMA_HISTOGRAM_COUNTS_1000( |
- "OfflinePages.Background.ScheduledStart.AvailableRequestCount." |
- "Svelte", |
- available_requests); |
- UMA_HISTOGRAM_COUNTS_1000( |
- "OfflinePages.Background.ScheduledStart.UnavailableRequestCount." |
- "Svelte", |
- total_requests - available_requests); |
- } else { |
- UMA_HISTOGRAM_COUNTS_1000( |
- "OfflinePages.Background.ScheduledStart.AvailableRequestCount", |
- available_requests); |
- UMA_HISTOGRAM_COUNTS_1000( |
- "OfflinePages.Background.ScheduledStart.UnavailableRequestCount", |
- total_requests - available_requests); |
- } |
- } else if (processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW) { |
- if (is_low_end_device_) { |
- UMA_HISTOGRAM_COUNTS_1000( |
- "OfflinePages.Background.ImmediateStart.AvailableRequestCount." |
- "Svelte", |
- available_requests); |
- UMA_HISTOGRAM_COUNTS_1000( |
- "OfflinePages.Background.ImmediateStart.UnavailableRequestCount." |
- "Svelte", |
- total_requests - available_requests); |
- } else { |
- UMA_HISTOGRAM_COUNTS_1000( |
- "OfflinePages.Background.ImmediateStart.AvailableRequestCount", |
- available_requests); |
- UMA_HISTOGRAM_COUNTS_1000( |
- "OfflinePages.Background.ImmediateStart.UnavailableRequestCount", |
- total_requests - available_requests); |
- } |
- } |
-} |
- |
-void RequestCoordinator::SendRequestToOffliner(const SavePageRequest& request) { |
- // Check that offlining didn't get cancelled while performing some async |
- // steps. |
- if (processing_state_ == ProcessingWindowState::STOPPED) |
- return; |
- |
- GetOffliner(); |
- if (!offliner_) { |
- DVLOG(0) << "Unable to create Offliner. " |
- << "Cannot background offline page."; |
- return; |
- } |
- |
- DCHECK(!is_busy_); |
- is_busy_ = true; |
- |
- // Record start time if this is first attempt. |
- if (request.started_attempt_count() == 0) { |
- RecordStartTimeUMA(request); |
- } |
- |
- // Mark attempt started in the database and start offliner when completed. |
- queue_->MarkAttemptStarted( |
- request.request_id(), |
- base::Bind(&RequestCoordinator::StartOffliner, |
- weak_ptr_factory_.GetWeakPtr(), request.request_id(), |
- request.client_id().name_space)); |
-} |
- |
-void RequestCoordinator::StartOffliner( |
- int64_t request_id, |
- const std::string& client_namespace, |
- std::unique_ptr<UpdateRequestsResult> update_result) { |
- if (update_result->store_state != StoreState::LOADED || |
- update_result->item_statuses.size() != 1 || |
- update_result->item_statuses.at(0).first != request_id || |
- update_result->item_statuses.at(0).second != ItemActionStatus::SUCCESS) { |
- is_busy_ = false; |
- // TODO(fgorski): what is the best result? Do we create a new status? |
- StopProcessing(Offliner::PRERENDERING_NOT_STARTED); |
- DVLOG(1) << "Failed to mark attempt started: " << request_id; |
- UpdateRequestResult request_result = |
- update_result->store_state != StoreState::LOADED |
- ? UpdateRequestResult::STORE_FAILURE |
- : UpdateRequestResult::REQUEST_DOES_NOT_EXIST; |
- event_logger_.RecordUpdateRequestFailed(client_namespace, request_result); |
- return; |
- } |
- |
- // TODO(fgorski): Switch to request_id only, so that this value is not written |
- // back to the store. |
- active_request_.reset( |
- new SavePageRequest(update_result->updated_items.at(0))); |
- |
- // Start the load and save process in the offliner (Async). |
- if (offliner_->LoadAndSave( |
- update_result->updated_items.at(0), |
- base::Bind(&RequestCoordinator::OfflinerDoneCallback, |
- weak_ptr_factory_.GetWeakPtr()))) { |
- base::TimeDelta timeout; |
- if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) { |
- timeout = base::TimeDelta::FromSeconds( |
- policy_->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds()); |
- } else { |
- DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW); |
- timeout = base::TimeDelta::FromSeconds( |
- policy_->GetSinglePageTimeLimitForImmediateLoadInSeconds()); |
- } |
- |
- // Inform observer of active request. |
- NotifyChanged(*active_request_.get()); |
- |
- // Start a watchdog timer to catch pre-renders running too long |
- watchdog_timer_.Start(FROM_HERE, timeout, this, |
- &RequestCoordinator::HandleWatchdogTimeout); |
- } else { |
- is_busy_ = false; |
- DVLOG(0) << "Unable to start LoadAndSave"; |
- StopProcessing(Offliner::PRERENDERING_NOT_STARTED); |
- |
- // We need to undo the MarkAttemptStarted that brought us to this |
- // method since we didn't success in starting after all. |
- MarkAttemptAborted(request_id, client_namespace); |
- } |
-} |
- |
-void RequestCoordinator::OfflinerDoneCallback(const SavePageRequest& request, |
- Offliner::RequestStatus status) { |
- DVLOG(2) << "offliner finished, saved: " |
- << (status == Offliner::RequestStatus::SAVED) |
- << ", status: " << static_cast<int>(status) << ", " << __func__; |
- DCHECK_NE(status, Offliner::RequestStatus::UNKNOWN); |
- DCHECK_NE(status, Offliner::RequestStatus::LOADED); |
- event_logger_.RecordOfflinerResult(request.client_id().name_space, status, |
- request.request_id()); |
- last_offlining_status_ = status; |
- RecordOfflinerResultUMA(request.client_id(), request.creation_time(), |
- last_offlining_status_); |
- watchdog_timer_.Stop(); |
- is_busy_ = false; |
- active_request_.reset(nullptr); |
- |
- UpdateRequestForCompletedAttempt(request, status); |
- if (ShouldTryNextRequest(status)) |
- TryNextRequest(!kStartOfProcessing); |
- else |
- scheduler_callback_.Run(true); |
-} |
- |
-void RequestCoordinator::UpdateRequestForCompletedAttempt( |
- const SavePageRequest& request, |
- Offliner::RequestStatus status) { |
- if (status == Offliner::RequestStatus::FOREGROUND_CANCELED || |
- status == Offliner::RequestStatus::PRERENDERING_CANCELED) { |
- // Update the request for the canceled attempt. |
- // TODO(dougarnett): See if we can conclusively identify other attempt |
- // aborted cases to treat this way (eg, for Render Process Killed). |
- UpdateRequestForAbortedAttempt(request); |
- } else if (status == Offliner::RequestStatus::SAVED) { |
- // Remove the request from the queue if it succeeded. |
- RemoveAttemptedRequest(request, |
- RequestNotifier::BackgroundSavePageResult::SUCCESS); |
- } else if (status == Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY) { |
- RemoveAttemptedRequest( |
- request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE); |
- } else if (request.completed_attempt_count() + 1 >= |
- policy_->GetMaxCompletedTries()) { |
- // Remove from the request queue if we exceeded max retries. The +1 |
- // represents the request that just completed. Since we call |
- // MarkAttemptCompleted within the if branches, the completed_attempt_count |
- // has not yet been updated when we are checking the if condition. |
- const RequestNotifier::BackgroundSavePageResult result( |
- RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED); |
- event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space, |
- result, request.request_id()); |
- RemoveAttemptedRequest(request, result); |
- } else { |
- // If we failed, but are not over the limit, update the request in the |
- // queue. |
- queue_->MarkAttemptCompleted( |
- request.request_id(), |
- base::Bind(&RequestCoordinator::MarkAttemptDone, |
- weak_ptr_factory_.GetWeakPtr(), request.request_id(), |
- request.client_id().name_space)); |
- } |
-} |
- |
-bool RequestCoordinator::ShouldTryNextRequest( |
- Offliner::RequestStatus previous_request_status) { |
- switch (previous_request_status) { |
- case Offliner::RequestStatus::SAVED: |
- case Offliner::RequestStatus::SAVE_FAILED: |
- case Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED: |
- case Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT: |
- case Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY: |
- return true; |
- case Offliner::RequestStatus::FOREGROUND_CANCELED: |
- case Offliner::RequestStatus::PRERENDERING_CANCELED: |
- case Offliner::RequestStatus::PRERENDERING_FAILED: |
- // No further processing in this service window. |
- return false; |
- default: |
- // Make explicit choice about new status codes that actually reach here. |
- // Their default is no further processing in this service window. |
- NOTREACHED(); |
- return false; |
- } |
-} |
- |
-void RequestCoordinator::EnableForOffliner(int64_t request_id, |
- const ClientId& client_id) { |
- // Since the recent tab helper might call multiple times, ignore subsequent |
- // calls for a particular request_id. |
- if (disabled_requests_.find(request_id) == disabled_requests_.end()) |
- return; |
- |
- // Clear from disabled list. |
- disabled_requests_.erase(request_id); |
- |
- // Mark the request as now in available state. |
- MarkAttemptAborted(request_id, client_id.name_space); |
- |
- // If we are not busy, start processing right away. |
- StartImmediatelyIfConnected(); |
-} |
- |
-void RequestCoordinator::MarkRequestCompleted(int64_t request_id) { |
- // Since the recent tab helper might call multiple times, ignore subsequent |
- // calls for a particular request_id. |
- if (disabled_requests_.find(request_id) == disabled_requests_.end()) |
- return; |
- |
- // Clear from disabled list. |
- disabled_requests_.erase(request_id); |
- |
- // Remove the request, but send out SUCCEEDED instead of removed. |
- // Note: since it had been disabled, it will not have been active in a |
- // background offliner, so it is not appropriate to TryNextRequest here. |
- std::vector<int64_t> request_ids { request_id }; |
- queue_->RemoveRequests( |
- request_ids, |
- base::Bind(&RequestCoordinator::HandleRemovedRequests, |
- weak_ptr_factory_.GetWeakPtr(), |
- RequestNotifier::BackgroundSavePageResult::SUCCESS)); |
-} |
- |
-const Scheduler::TriggerConditions RequestCoordinator::GetTriggerConditions( |
- const bool user_requested) { |
- return Scheduler::TriggerConditions( |
- policy_->PowerRequired(user_requested), |
- policy_->BatteryPercentageRequired(user_requested), |
- policy_->UnmeteredNetworkRequired(user_requested)); |
-} |
- |
-void RequestCoordinator::AddObserver(Observer* observer) { |
- DCHECK(observer); |
- observers_.AddObserver(observer); |
-} |
- |
-void RequestCoordinator::RemoveObserver(Observer* observer) { |
- observers_.RemoveObserver(observer); |
-} |
- |
-void RequestCoordinator::NotifyAdded(const SavePageRequest& request) { |
- for (Observer& observer : observers_) |
- observer.OnAdded(request); |
-} |
- |
-void RequestCoordinator::NotifyCompleted( |
- const SavePageRequest& request, |
- RequestNotifier::BackgroundSavePageResult status) { |
- for (Observer& observer : observers_) |
- observer.OnCompleted(request, status); |
-} |
- |
-void RequestCoordinator::NotifyChanged(const SavePageRequest& request) { |
- for (Observer& observer : observers_) |
- observer.OnChanged(request); |
-} |
- |
-void RequestCoordinator::GetOffliner() { |
- if (!offliner_) { |
- offliner_ = factory_->GetOffliner(policy_.get()); |
- } |
-} |
- |
-ClientPolicyController* RequestCoordinator::GetPolicyController() { |
- return policy_controller_.get(); |
-} |
- |
-void RequestCoordinator::Shutdown() { |
- network_quality_estimator_ = nullptr; |
-} |
- |
-} // namespace offline_pages |