blob: e4e1d4679474cccd710fa26ee95f32d02fb03a91 [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_settled_fetches_task.h"
#include "base/barrier_closure.h"
#include "content/browser/background_fetch/background_fetch_data_manager.h"
#include "content/browser/background_fetch/storage/database_helpers.h"
#include "content/browser/cache_storage/cache_storage_manager.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/common/service_worker/service_worker_utils.h"
#include "services/network/public/cpp/cors/cors.h"
namespace content {
namespace background_fetch {
GetSettledFetchesTask::GetSettledFetchesTask(
DatabaseTaskHost* host,
BackgroundFetchRegistrationId registration_id,
std::unique_ptr<BackgroundFetchRequestMatchParams> match_params,
SettledFetchesCallback callback)
: DatabaseTask(host),
registration_id_(registration_id),
match_params_(std::move(match_params)),
settled_fetches_callback_(std::move(callback)),
weak_factory_(this) {}
GetSettledFetchesTask::~GetSettledFetchesTask() = default;
void GetSettledFetchesTask::Start() {
base::RepeatingClosure barrier_closure = base::BarrierClosure(
2u, base::BindOnce(&GetSettledFetchesTask::GetResponses,
weak_factory_.GetWeakPtr()));
cache_manager()->OpenCache(
registration_id_.origin(), CacheStorageOwner::kBackgroundFetch,
registration_id_.unique_id() /* cache_name */,
base::BindOnce(&GetSettledFetchesTask::DidOpenCache,
weak_factory_.GetWeakPtr(), barrier_closure));
service_worker_context()->GetRegistrationUserDataByKeyPrefix(
registration_id_.service_worker_registration_id(),
{CompletedRequestKeyPrefix(registration_id_.unique_id())},
base::BindOnce(&GetSettledFetchesTask::DidGetCompletedRequests,
weak_factory_.GetWeakPtr(), barrier_closure));
}
void GetSettledFetchesTask::DidOpenCache(
base::OnceClosure done_closure,
CacheStorageCacheHandle handle,
blink::mojom::CacheStorageError error) {
if (error != blink::mojom::CacheStorageError::kSuccess) {
SetStorageError(BackgroundFetchStorageError::kCacheStorageError);
} else {
DCHECK(handle.value());
handle_ = std::move(handle);
}
std::move(done_closure).Run();
}
void GetSettledFetchesTask::DidGetCompletedRequests(
base::OnceClosure done_closure,
const std::vector<std::string>& data,
blink::ServiceWorkerStatusCode status) {
switch (ToDatabaseStatus(status)) {
case DatabaseStatus::kOk:
break;
case DatabaseStatus::kFailed:
SetStorageError(BackgroundFetchStorageError::kServiceWorkerStorageError);
break;
case DatabaseStatus::kNotFound:
failure_reason_ = blink::mojom::BackgroundFetchFailureReason::FETCH_ERROR;
error_ = blink::mojom::BackgroundFetchError::INVALID_ID;
break;
}
completed_requests_.reserve(data.size());
for (const std::string& serialized_completed_request : data) {
completed_requests_.emplace_back();
if (!completed_requests_.back().ParseFromString(
serialized_completed_request)) {
// Service worker database has been corrupted. Abandon fetches.
SetStorageError(BackgroundFetchStorageError::kServiceWorkerStorageError);
failure_reason_ = blink::mojom::BackgroundFetchFailureReason::
SERVICE_WORKER_UNAVAILABLE;
AbandonFetches(registration_id_.service_worker_registration_id());
break;
}
}
std::move(done_closure).Run();
}
void GetSettledFetchesTask::GetResponses() {
// Handle potential errors.
if (HasStorageError()) {
FinishWithError(blink::mojom::BackgroundFetchError::STORAGE_ERROR);
return;
}
if (error_ != blink::mojom::BackgroundFetchError::NONE) {
FinishWithError(error_);
return;
}
if (completed_requests_.empty()) {
FinishWithError(blink::mojom::BackgroundFetchError::NONE);
return;
}
if (!match_params_->FilterByRequest()) {
// No request to match against has been specified. Process all completed
// requests.
// TODO(crbug.com/863016): Process all requests here, not just the
// completed ones.
base::RepeatingClosure barrier_closure = base::BarrierClosure(
completed_requests_.size(),
base::BindOnce(&GetSettledFetchesTask::FinishWithError,
weak_factory_.GetWeakPtr(),
blink::mojom::BackgroundFetchError::NONE));
settled_fetches_.reserve(completed_requests_.size());
for (const auto& completed_request : completed_requests_) {
settled_fetches_.emplace_back();
settled_fetches_.back().request =
ServiceWorkerUtils::DeserializeFetchRequestFromString(
completed_request.serialized_request());
FillResponse(&settled_fetches_.back(), barrier_closure);
}
return;
}
// Get response(s) only for the relevant fetch.
settled_fetches_.emplace_back();
settled_fetches_.back().request = match_params_->request_to_match();
if (match_params_->match_all()) {
FillResponses(base::BindOnce(&GetSettledFetchesTask::FinishWithError,
weak_factory_.GetWeakPtr(),
blink::mojom::BackgroundFetchError::NONE));
return;
} else {
FillResponse(&settled_fetches_.back(),
base::BindOnce(&GetSettledFetchesTask::FinishWithError,
weak_factory_.GetWeakPtr(),
blink::mojom::BackgroundFetchError::NONE));
return;
}
}
void GetSettledFetchesTask::FillResponse(
BackgroundFetchSettledFetch* settled_fetch,
base::OnceClosure callback) {
DCHECK(settled_fetch);
DCHECK(handle_.value());
auto request =
std::make_unique<ServiceWorkerFetchRequest>(settled_fetch->request);
handle_.value()->Match(std::move(request),
match_params_->cloned_cache_query_params(),
base::BindOnce(&GetSettledFetchesTask::DidMatchRequest,
weak_factory_.GetWeakPtr(),
settled_fetch, std::move(callback)));
}
void GetSettledFetchesTask::FillResponses(base::OnceClosure callback) {
DCHECK(match_params_->match_all());
DCHECK(match_params_->FilterByRequest());
DCHECK(!settled_fetches_.empty());
DCHECK(handle_.value());
// Make a copy.
auto request = std::make_unique<ServiceWorkerFetchRequest>(
match_params_->request_to_match());
handle_.value()->MatchAll(
std::move(request), match_params_->cloned_cache_query_params(),
base::BindOnce(&GetSettledFetchesTask::DidMatchAllResponsesForRequest,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void GetSettledFetchesTask::DidMatchRequest(
BackgroundFetchSettledFetch* settled_fetch,
base::OnceClosure callback,
blink::mojom::CacheStorageError error,
blink::mojom::FetchAPIResponsePtr cache_response) {
DCHECK(settled_fetch);
// Handle error cases.
if (error == blink::mojom::CacheStorageError::kErrorNotFound) {
// This is currently being called once a fetch finishes, or when match() is
// called.
// In the first case, not finding a response is an error state. In the
// second case, it just means the developer passed a non-matching request.
// The if condition below picks the first one.
// TODO(crbug.com/863016): Once we stop sending settled_fetches with
// BackgroundFetch events, this won't be a storage error.
if (!match_params_->FilterByRequest())
SetStorageError(BackgroundFetchStorageError::kCacheStorageError);
} else if (error != blink::mojom::CacheStorageError::kSuccess) {
SetStorageError(BackgroundFetchStorageError::kCacheStorageError);
}
if (!cache_response) {
FillUncachedResponse(settled_fetch, std::move(callback));
return;
}
settled_fetch->response = std::move(cache_response);
std::move(callback).Run();
}
void GetSettledFetchesTask::DidMatchAllResponsesForRequest(
base::OnceClosure callback,
blink::mojom::CacheStorageError error,
std::vector<blink::mojom::FetchAPIResponsePtr> cache_responses) {
if (error != blink::mojom::CacheStorageError::kSuccess &&
error != blink::mojom::CacheStorageError::kErrorNotFound) {
SetStorageError(BackgroundFetchStorageError::kCacheStorageError);
}
if (error != blink::mojom::CacheStorageError::kSuccess) {
DCHECK(!settled_fetches_.empty());
FillUncachedResponse(&settled_fetches_.back(), std::move(callback));
return;
}
settled_fetches_.clear();
settled_fetches_.reserve(cache_responses.size());
for (size_t i = 0; i < cache_responses.size(); ++i) {
settled_fetches_.emplace_back();
settled_fetches_.back().request = match_params_->request_to_match();
settled_fetches_.back().response = std::move(cache_responses[i]);
}
std::move(callback).Run();
}
// TODO(crbug.com/863016): Get rid of this method.
void GetSettledFetchesTask::FillUncachedResponse(
BackgroundFetchSettledFetch* settled_fetch,
base::OnceClosure callback) {
failure_reason_ = blink::mojom::BackgroundFetchFailureReason::FETCH_ERROR;
// TODO(rayankans): Fill unmatched response with error reports.
DCHECK(!settled_fetch->response);
settled_fetch->response = blink::mojom::FetchAPIResponse::New();
settled_fetch->response->response_type =
network::mojom::FetchResponseType::kError;
settled_fetch->response->url_list.push_back(settled_fetch->request.url);
std::move(callback).Run();
}
void GetSettledFetchesTask::FinishWithError(
blink::mojom::BackgroundFetchError error) {
if (HasStorageError())
error = blink::mojom::BackgroundFetchError::STORAGE_ERROR;
ReportStorageError();
if (error == blink::mojom::BackgroundFetchError::NONE) {
for (const auto& settled_fetch : settled_fetches_) {
if (!settled_fetch.response->status_code) {
// A status_code of 0 means no headers were returned.
failure_reason_ =
blink::mojom::BackgroundFetchFailureReason::FETCH_ERROR;
break;
}
if (!network::cors::IsOkStatus(settled_fetch.response->status_code)) {
failure_reason_ =
blink::mojom::BackgroundFetchFailureReason::BAD_STATUS;
break;
}
}
}
std::move(settled_fetches_callback_)
.Run(error, failure_reason_, std::move(settled_fetches_),
{} /* blob_data_handles */);
Finished(); // Destroys |this|.
}
std::string GetSettledFetchesTask::HistogramName() const {
return "GetSettledFetchesTask";
};
} // namespace background_fetch
} // namespace content