blob: 40f8c65c2e1cc3322894ec8584344b837a3a1d66 [file] [log] [blame]
// 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