| // Copyright 2020 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/feed/core/v2/tasks/load_stream_task.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/callback_helpers.h" |
| #include "base/check.h" |
| #include "base/time/time.h" |
| #include "components/feed/core/proto/v2/wire/capability.pb.h" |
| #include "components/feed/core/proto/v2/wire/client_info.pb.h" |
| #include "components/feed/core/proto/v2/wire/feed_request.pb.h" |
| #include "components/feed/core/proto/v2/wire/request.pb.h" |
| #include "components/feed/core/v2/feed_network.h" |
| #include "components/feed/core/v2/feed_stream.h" |
| #include "components/feed/core/v2/metrics_reporter.h" |
| #include "components/feed/core/v2/proto_util.h" |
| #include "components/feed/core/v2/protocol_translator.h" |
| #include "components/feed/core/v2/stream_model.h" |
| #include "components/feed/core/v2/tasks/upload_actions_task.h" |
| |
| namespace feed { |
| namespace { |
| using LoadType = LoadStreamTask::LoadType; |
| using Result = LoadStreamTask::Result; |
| |
| feedwire::FeedQuery::RequestReason GetRequestReason(LoadType load_type) { |
| switch (load_type) { |
| case LoadType::kInitialLoad: |
| return feedwire::FeedQuery::MANUAL_REFRESH; |
| case LoadType::kBackgroundRefresh: |
| return feedwire::FeedQuery::SCHEDULED_REFRESH; |
| } |
| } |
| |
| } // namespace |
| |
| Result::Result() = default; |
| Result::Result(LoadStreamStatus status) : final_status(status) {} |
| Result::~Result() = default; |
| Result::Result(Result&&) = default; |
| Result& Result::operator=(Result&&) = default; |
| |
| LoadStreamTask::LoadStreamTask(LoadType load_type, |
| FeedStream* stream, |
| base::OnceCallback<void(Result)> done_callback) |
| : load_type_(load_type), |
| stream_(stream), |
| done_callback_(std::move(done_callback)) { |
| latencies_ = std::make_unique<LoadLatencyTimes>(); |
| } |
| |
| LoadStreamTask::~LoadStreamTask() = default; |
| |
| void LoadStreamTask::Run() { |
| latencies_->StepComplete(LoadLatencyTimes::kTaskExecution); |
| // Phase 1: Try to load from persistent storage. |
| |
| // TODO(harringtond): We're checking ShouldAttemptLoad() here and before the |
| // task is added to the task queue. Maybe we can simplify this. |
| |
| // First, ensure we still should load the model. |
| LoadStreamStatus should_not_attempt_reason = stream_->ShouldAttemptLoad( |
| /*model_loading=*/true); |
| if (should_not_attempt_reason != LoadStreamStatus::kNoStatus) { |
| return Done(should_not_attempt_reason); |
| } |
| |
| // Use |kConsistencyTokenOnly| to short-circuit loading from store if we don't |
| // need the full stream state. |
| auto load_from_store_type = |
| (load_type_ == LoadType::kInitialLoad) |
| ? LoadStreamFromStoreTask::LoadType::kFullLoad |
| : LoadStreamFromStoreTask::LoadType::kPendingActionsOnly; |
| |
| load_from_store_task_ = std::make_unique<LoadStreamFromStoreTask>( |
| load_from_store_type, stream_->GetStore(), |
| base::BindOnce(&LoadStreamTask::LoadFromStoreComplete, GetWeakPtr())); |
| load_from_store_task_->Execute(base::DoNothing()); |
| } |
| |
| void LoadStreamTask::LoadFromStoreComplete( |
| LoadStreamFromStoreTask::Result result) { |
| load_from_store_status_ = result.status; |
| latencies_->StepComplete(LoadLatencyTimes::kLoadFromStore); |
| |
| // Phase 2. |
| // - If loading from store works, update the model. |
| // - Otherwise, try to load from the network. |
| |
| if (load_type_ == LoadType::kInitialLoad && |
| result.status == LoadStreamStatus::kLoadedFromStore) { |
| auto model = std::make_unique<StreamModel>(); |
| model->Update(std::move(result.update_request)); |
| stream_->LoadModel(std::move(model)); |
| Done(LoadStreamStatus::kLoadedFromStore); |
| return; |
| } |
| |
| LoadStreamStatus final_status = stream_->ShouldMakeFeedQueryRequest(); |
| if (final_status != LoadStreamStatus::kNoStatus) { |
| Done(final_status); |
| return; |
| } |
| |
| // If making a request, first try to upload pending actions. |
| upload_actions_task_ = std::make_unique<UploadActionsTask>( |
| std::move(result.pending_actions), stream_, |
| base::BindOnce(&LoadStreamTask::UploadActionsComplete, GetWeakPtr())); |
| upload_actions_task_->Execute(base::DoNothing()); |
| } |
| |
| void LoadStreamTask::UploadActionsComplete(UploadActionsTask::Result result) { |
| bool force_signed_out_request = |
| stream_->ShouldForceSignedOutFeedQueryRequest(); |
| upload_actions_result_ = |
| std::make_unique<UploadActionsTask::Result>(std::move(result)); |
| latencies_->StepComplete(LoadLatencyTimes::kUploadActions); |
| stream_->GetNetwork()->SendQueryRequest( |
| CreateFeedQueryRefreshRequest( |
| GetRequestReason(load_type_), |
| stream_->GetRequestMetadata(/*is_for_next_page=*/false), |
| stream_->GetMetadata()->GetConsistencyToken()), |
| force_signed_out_request, |
| base::BindOnce(&LoadStreamTask::QueryRequestComplete, GetWeakPtr())); |
| } |
| |
| void LoadStreamTask::QueryRequestComplete( |
| FeedNetwork::QueryRequestResult result) { |
| latencies_->StepComplete(LoadLatencyTimes::kQueryRequest); |
| |
| DCHECK(!stream_->GetModel()); |
| |
| network_response_info_ = result.response_info; |
| |
| if (result.response_info.status_code != 200) |
| return Done(LoadStreamStatus::kNetworkFetchFailed); |
| |
| if (!result.response_body) { |
| if (result.response_info.response_body_bytes > 0) |
| return Done(LoadStreamStatus::kCannotParseNetworkResponseBody); |
| else |
| return Done(LoadStreamStatus::kNoResponseBody); |
| } |
| |
| RefreshResponseData response_data = |
| stream_->GetWireResponseTranslator()->TranslateWireResponse( |
| *result.response_body, |
| StreamModelUpdateRequest::Source::kNetworkUpdate, |
| result.response_info.was_signed_in, base::Time::Now()); |
| if (!response_data.model_update_request) |
| return Done(LoadStreamStatus::kProtoTranslationFailed); |
| |
| loaded_new_content_from_network_ = true; |
| |
| stream_->GetStore()->OverwriteStream( |
| std::make_unique<StreamModelUpdateRequest>( |
| *response_data.model_update_request), |
| base::DoNothing()); |
| |
| bool isNoticeCardFulfilled = response_data.model_update_request->stream_data |
| .privacy_notice_fulfilled(); |
| stream_->SetLastStreamLoadHadNoticeCard(isNoticeCardFulfilled); |
| MetricsReporter::NoticeCardFulfilled(isNoticeCardFulfilled); |
| |
| stream_->GetMetadata()->MaybeUpdateSessionId(response_data.session_id); |
| |
| if (load_type_ != LoadType::kBackgroundRefresh) { |
| auto model = std::make_unique<StreamModel>(); |
| model->Update(std::move(response_data.model_update_request)); |
| stream_->LoadModel(std::move(model)); |
| } |
| |
| if (response_data.request_schedule) |
| stream_->SetRequestSchedule(*response_data.request_schedule); |
| |
| Done(LoadStreamStatus::kLoadedFromNetwork); |
| } |
| |
| void LoadStreamTask::Done(LoadStreamStatus status) { |
| Result result; |
| result.load_from_store_status = load_from_store_status_; |
| result.final_status = status; |
| result.load_type = load_type_; |
| result.network_response_info = network_response_info_; |
| result.loaded_new_content_from_network = loaded_new_content_from_network_; |
| result.latencies = std::move(latencies_); |
| result.upload_actions_result = std::move(upload_actions_result_); |
| std::move(done_callback_).Run(std::move(result)); |
| TaskComplete(); |
| } |
| |
| } // namespace feed |