| // Copyright 2018 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 "content/browser/background_fetch/storage/get_initialization_data_task.h" |
| |
| #include "base/barrier_closure.h" |
| #include "content/browser/background_fetch/background_fetch.pb.h" |
| #include "content/browser/background_fetch/storage/database_helpers.h" |
| #include "content/browser/service_worker/service_worker_context_wrapper.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| |
| namespace background_fetch { |
| |
| namespace { |
| |
| // Base class with all the common implementation for the SubTasks |
| // needed in this file. |
| class InitializationSubTask : public DatabaseTask { |
| public: |
| // Holds data used by all SubTasks. |
| struct SubTaskInit { |
| SubTaskInit() = delete; |
| ~SubTaskInit() = default; |
| |
| // Service Worker Database metadata. |
| int64_t service_worker_registration_id; |
| std::string unique_id; |
| |
| // The results to report. |
| BackgroundFetchInitializationData* initialization_data; |
| blink::mojom::BackgroundFetchError* error; |
| }; |
| |
| InitializationSubTask(DatabaseTaskHost* host, |
| const SubTaskInit& sub_task_init, |
| base::OnceClosure done_closure) |
| : DatabaseTask(host), |
| sub_task_init_(sub_task_init), |
| done_closure_(std::move(done_closure)) { |
| DCHECK(sub_task_init_.initialization_data); |
| DCHECK(sub_task_init_.error); |
| } |
| |
| ~InitializationSubTask() override = default; |
| |
| protected: |
| void FinishTask() { |
| std::move(done_closure_).Run(); |
| Finished(); // Destroys |this|. |
| } |
| |
| SubTaskInit& sub_task_init() { return sub_task_init_; } |
| |
| private: |
| SubTaskInit sub_task_init_; |
| base::OnceClosure done_closure_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InitializationSubTask); |
| }; |
| |
| // Fills the BackgroundFetchInitializationData with the number of completed |
| // requests. |
| class GetCompletedRequestsTask : public InitializationSubTask { |
| public: |
| GetCompletedRequestsTask(DatabaseTaskHost* host, |
| const SubTaskInit& sub_task_init, |
| base::OnceClosure done_closure) |
| : InitializationSubTask(host, sub_task_init, std::move(done_closure)), |
| weak_factory_(this) {} |
| |
| ~GetCompletedRequestsTask() override = default; |
| |
| void Start() override { |
| service_worker_context()->GetRegistrationUserDataByKeyPrefix( |
| sub_task_init().service_worker_registration_id, |
| {CompletedRequestKeyPrefix(sub_task_init().unique_id)}, |
| base::BindOnce(&GetCompletedRequestsTask::DidGetCompletedRequests, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| private: |
| void DidGetCompletedRequests(const std::vector<std::string>& data, |
| blink::ServiceWorkerStatusCode status) { |
| switch (ToDatabaseStatus(status)) { |
| case DatabaseStatus::kFailed: |
| *sub_task_init().error = |
| blink::mojom::BackgroundFetchError::STORAGE_ERROR; |
| FinishTask(); |
| return; |
| case DatabaseStatus::kNotFound: |
| case DatabaseStatus::kOk: |
| break; |
| } |
| |
| sub_task_init().initialization_data->num_completed_requests = data.size(); |
| FinishTask(); |
| } |
| |
| base::WeakPtrFactory<GetCompletedRequestsTask> |
| weak_factory_; // Keep as last. |
| }; |
| |
| // Fills the BackgroundFetchInitializationData with the guids of active |
| // (previously started) requests. |
| class GetActiveRequestsTask : public InitializationSubTask { |
| public: |
| GetActiveRequestsTask(DatabaseTaskHost* host, |
| const SubTaskInit& sub_task_init, |
| base::OnceClosure done_closure) |
| : InitializationSubTask(host, sub_task_init, std::move(done_closure)), |
| weak_factory_(this) {} |
| |
| ~GetActiveRequestsTask() override = default; |
| |
| void Start() override { |
| service_worker_context()->GetRegistrationUserDataByKeyPrefix( |
| sub_task_init().service_worker_registration_id, |
| {ActiveRequestKeyPrefix(sub_task_init().unique_id)}, |
| base::BindOnce(&GetActiveRequestsTask::DidGetActiveRequests, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| private: |
| void DidGetActiveRequests(const std::vector<std::string>& data, |
| blink::ServiceWorkerStatusCode status) { |
| switch (ToDatabaseStatus(status)) { |
| case DatabaseStatus::kFailed: |
| *sub_task_init().error = |
| blink::mojom::BackgroundFetchError::STORAGE_ERROR; |
| FinishTask(); |
| return; |
| case DatabaseStatus::kNotFound: |
| case DatabaseStatus::kOk: |
| break; |
| } |
| |
| for (const std::string& serialized_active_request : data) { |
| proto::BackgroundFetchActiveRequest active_request; |
| if (!active_request.ParseFromString(serialized_active_request)) { |
| *sub_task_init().error = |
| blink::mojom::BackgroundFetchError::STORAGE_ERROR; |
| continue; |
| } |
| DCHECK_EQ(sub_task_init().unique_id, active_request.unique_id()); |
| sub_task_init().initialization_data->active_fetch_guids.push_back( |
| active_request.download_guid()); |
| } |
| |
| FinishTask(); |
| } |
| |
| base::WeakPtrFactory<GetActiveRequestsTask> weak_factory_; // Keep as last. |
| |
| DISALLOW_COPY_AND_ASSIGN(GetActiveRequestsTask); |
| }; |
| |
| // Fills the BackgroundFetchInitializationData with all the relevant information |
| // stored in the BackgroundFetchMetadata proto. |
| class FillFromMetadataTask : public InitializationSubTask { |
| public: |
| FillFromMetadataTask(DatabaseTaskHost* host, |
| const SubTaskInit& sub_task_init, |
| base::OnceClosure done_closure) |
| : InitializationSubTask(host, sub_task_init, std::move(done_closure)), |
| weak_factory_(this) {} |
| |
| ~FillFromMetadataTask() override = default; |
| |
| void Start() override { |
| service_worker_context()->GetRegistrationUserDataByKeyPrefix( |
| sub_task_init().service_worker_registration_id, |
| {RegistrationKey(sub_task_init().unique_id)}, |
| base::BindOnce(&FillFromMetadataTask::DidGetMetadata, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| private: |
| void DidGetMetadata(const std::vector<std::string>& data, |
| blink::ServiceWorkerStatusCode status) { |
| switch (ToDatabaseStatus(status)) { |
| case DatabaseStatus::kFailed: |
| case DatabaseStatus::kNotFound: |
| *sub_task_init().error = |
| blink::mojom::BackgroundFetchError::STORAGE_ERROR; |
| FinishTask(); |
| return; |
| case DatabaseStatus::kOk: |
| break; |
| } |
| |
| if (data.size() != 1u) { |
| *sub_task_init().error = |
| blink::mojom::BackgroundFetchError::STORAGE_ERROR; |
| FinishTask(); |
| return; |
| } |
| |
| proto::BackgroundFetchMetadata metadata; |
| if (!metadata.ParseFromString(data[0])) { |
| *sub_task_init().error = |
| blink::mojom::BackgroundFetchError::STORAGE_ERROR; |
| FinishTask(); |
| return; |
| } |
| |
| if (sub_task_init().unique_id != metadata.registration().unique_id()) { |
| *sub_task_init().error = |
| blink::mojom::BackgroundFetchError::STORAGE_ERROR; |
| FinishTask(); |
| return; |
| } |
| |
| // Fill BackgroundFetchRegistrationId. |
| sub_task_init().initialization_data->registration_id = |
| BackgroundFetchRegistrationId( |
| sub_task_init().service_worker_registration_id, |
| url::Origin::Create(GURL(metadata.origin())), |
| metadata.registration().developer_id(), |
| metadata.registration().unique_id()); |
| |
| // Fill BackgroundFetchRegistration. |
| auto& registration = sub_task_init().initialization_data->registration; |
| // TODO(crbug.com/853874): Unify conversion logic. |
| registration.developer_id = metadata.registration().developer_id(); |
| registration.unique_id = metadata.registration().unique_id(); |
| registration.upload_total = metadata.registration().upload_total(); |
| registration.uploaded = metadata.registration().uploaded(); |
| registration.download_total = metadata.registration().download_total(); |
| registration.downloaded = metadata.registration().downloaded(); |
| |
| // Total number of requests. |
| sub_task_init().initialization_data->num_requests = metadata.num_fetches(); |
| |
| // TODO(rayankans): Fill BackgroundFetchOptions and the icon after it is |
| // persisted. |
| FinishTask(); |
| } |
| |
| base::WeakPtrFactory<FillFromMetadataTask> weak_factory_; // Keep as last. |
| |
| DISALLOW_COPY_AND_ASSIGN(FillFromMetadataTask); |
| }; |
| |
| // Asynchronously calls the SubTasks required to collect all the information for |
| // the BackgroundFetchInitializationData. |
| class FillBackgroundFetchInitializationDataTask : public InitializationSubTask { |
| public: |
| FillBackgroundFetchInitializationDataTask(DatabaseTaskHost* host, |
| const SubTaskInit& sub_task_init, |
| base::OnceClosure done_closure) |
| : InitializationSubTask(host, sub_task_init, std::move(done_closure)), |
| weak_factory_(this) {} |
| |
| ~FillBackgroundFetchInitializationDataTask() override = default; |
| |
| void Start() override { |
| // We need 3 queries to get the initialization data. These are wrapped |
| // in a BarrierClosure to avoid querying them serially. |
| // 1. Metadata |
| // 2. Active Requests |
| // 3. Completed Requests |
| // TODO(rayankans): 4. UI Title |
| base::RepeatingClosure barrier_closure = base::BarrierClosure( |
| 3u, |
| base::BindOnce(&FillBackgroundFetchInitializationDataTask::FinishTask, |
| weak_factory_.GetWeakPtr())); |
| AddSubTask(std::make_unique<FillFromMetadataTask>(this, sub_task_init(), |
| barrier_closure)); |
| AddSubTask(std::make_unique<GetCompletedRequestsTask>(this, sub_task_init(), |
| barrier_closure)); |
| AddSubTask(std::make_unique<GetActiveRequestsTask>(this, sub_task_init(), |
| barrier_closure)); |
| } |
| |
| private: |
| base::WeakPtrFactory<FillBackgroundFetchInitializationDataTask> |
| weak_factory_; // Keep as last. |
| |
| DISALLOW_COPY_AND_ASSIGN(FillBackgroundFetchInitializationDataTask); |
| }; |
| |
| } // namespace |
| |
| BackgroundFetchInitializationData::BackgroundFetchInitializationData() = |
| default; |
| |
| BackgroundFetchInitializationData::BackgroundFetchInitializationData( |
| BackgroundFetchInitializationData&&) = default; |
| |
| BackgroundFetchInitializationData::~BackgroundFetchInitializationData() = |
| default; |
| |
| GetInitializationDataTask::GetInitializationDataTask( |
| DatabaseTaskHost* host, |
| GetInitializationDataCallback callback) |
| : DatabaseTask(host), callback_(std::move(callback)), weak_factory_(this) {} |
| |
| GetInitializationDataTask::~GetInitializationDataTask() = default; |
| |
| void GetInitializationDataTask::Start() { |
| service_worker_context()->GetUserDataForAllRegistrationsByKeyPrefix( |
| kActiveRegistrationUniqueIdKeyPrefix, |
| base::BindOnce(&GetInitializationDataTask::DidGetRegistrations, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void GetInitializationDataTask::DidGetRegistrations( |
| const std::vector<std::pair<int64_t, std::string>>& user_data, |
| blink::ServiceWorkerStatusCode status) { |
| switch (ToDatabaseStatus(status)) { |
| case DatabaseStatus::kFailed: |
| error_ = blink::mojom::BackgroundFetchError::STORAGE_ERROR; |
| FinishTask(); |
| return; |
| case DatabaseStatus::kNotFound: |
| case DatabaseStatus::kOk: |
| break; |
| } |
| |
| if (user_data.empty()) { |
| FinishTask(); |
| return; |
| } |
| |
| base::RepeatingClosure barrier_closure = base::BarrierClosure( |
| user_data.size(), base::BindOnce(&GetInitializationDataTask::FinishTask, |
| weak_factory_.GetWeakPtr())); |
| for (const auto& ud : user_data) { |
| auto insertion_result = initialization_data_map_.emplace( |
| ud.second, BackgroundFetchInitializationData()); |
| DCHECK(insertion_result.second); // Check unique_id is in fact unique. |
| |
| AddSubTask(std::make_unique<FillBackgroundFetchInitializationDataTask>( |
| this, |
| InitializationSubTask::SubTaskInit{ |
| ud.first, ud.second, |
| &insertion_result.first->second /* initialization_data */, &error_}, |
| barrier_closure)); |
| } |
| } |
| void GetInitializationDataTask::FinishTask() { |
| std::vector<BackgroundFetchInitializationData> results; |
| results.reserve(initialization_data_map_.size()); |
| for (auto& id : initialization_data_map_) |
| results.emplace_back(std::move(id.second)); |
| |
| std::move(callback_).Run(error_, std::move(results)); |
| Finished(); // Destroys |this|. |
| } |
| |
| } // namespace background_fetch |
| |
| } // namespace content |