blob: 99c273865f68dc55786466b85b1606d4c13aaa9d [file] [log] [blame]
// Copyright 2017 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/background_fetch_data_manager.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/guid.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "content/browser/background_fetch/background_fetch.pb.h"
#include "content/browser/background_fetch/background_fetch_data_manager_observer.h"
#include "content/browser/background_fetch/background_fetch_request_info.h"
#include "content/browser/background_fetch/background_fetch_request_match_params.h"
#include "content/browser/background_fetch/background_fetch_test_base.h"
#include "content/browser/background_fetch/background_fetch_test_data_manager.h"
#include "content/browser/background_fetch/storage/database_helpers.h"
#include "content/browser/background_fetch/storage/image_helpers.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/cache_storage/cache_storage_cache_handle.h"
#include "content/browser/cache_storage/cache_storage_manager.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/common/background_fetch/background_fetch_types.h"
#include "content/common/service_worker/service_worker_utils.h"
#include "content/public/browser/background_fetch_response.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/test_utils.h"
#include "mojo/public/cpp/system/data_pipe_drainer.h"
#include "services/network/public/mojom/fetch_api.mojom.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_data_handle.h"
#include "storage/browser/blob/blob_impl.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom.h"
#include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
namespace content {
namespace {
using background_fetch::BackgroundFetchInitializationData;
using ::testing::_;
using ::testing::UnorderedElementsAre;
using ::testing::IsEmpty;
const char kUserDataPrefix[] = "bgfetch_";
const char kExampleDeveloperId[] = "my-example-id";
const char kAlternativeDeveloperId[] = "my-other-id";
const char kExampleUniqueId[] = "7e57ab1e-c0de-a150-ca75-1e75f005ba11";
const char kAlternativeUniqueId[] = "bb48a9fb-c21f-4c2d-a9ae-58bd48a9fb53";
const char kInitialTitle[] = "Initial Title";
constexpr size_t kResponseSize = 42u;
void DidGetInitializationData(
base::Closure quit_closure,
std::vector<BackgroundFetchInitializationData>* out_result,
blink::mojom::BackgroundFetchError error,
std::vector<BackgroundFetchInitializationData> result) {
DCHECK_EQ(error, blink::mojom::BackgroundFetchError::NONE);
*out_result = std::move(result);
std::move(quit_closure).Run();
}
void DidCreateRegistration(
base::OnceClosure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
blink::mojom::BackgroundFetchError error,
blink::mojom::BackgroundFetchRegistrationPtr registration) {
*out_error = error;
std::move(quit_closure).Run();
}
void DidGetError(base::OnceClosure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
blink::mojom::BackgroundFetchError error) {
*out_error = error;
std::move(quit_closure).Run();
}
void DidGetRegistrationUserDataByKeyPrefix(
base::OnceClosure quit_closure,
std::vector<std::string>* out_data,
const std::vector<std::string>& data,
blink::ServiceWorkerStatusCode status) {
DCHECK(out_data);
DCHECK_EQ(blink::ServiceWorkerStatusCode::kOk, status);
*out_data = data;
std::move(quit_closure).Run();
}
void DidStoreUserData(base::OnceClosure quit_closure,
blink::ServiceWorkerStatusCode status) {
DCHECK_EQ(blink::ServiceWorkerStatusCode::kOk, status);
std::move(quit_closure).Run();
}
void GetNumUserData(base::Closure quit_closure,
int* out_size,
const std::vector<std::string>& data,
blink::ServiceWorkerStatusCode status) {
DCHECK(out_size);
DCHECK_EQ(blink::ServiceWorkerStatusCode::kOk, status);
*out_size = data.size();
std::move(quit_closure).Run();
}
struct ResponseStateStats {
int pending_requests = 0;
int active_requests = 0;
int completed_requests = 0;
};
bool operator==(const ResponseStateStats& s1, const ResponseStateStats& s2) {
return s1.pending_requests == s2.pending_requests &&
s1.active_requests == s2.active_requests &&
s1.completed_requests == s2.completed_requests;
}
std::vector<blink::mojom::FetchAPIRequestPtr> CreateValidRequests(
const url::Origin& origin,
size_t num_requests = 1u) {
std::vector<blink::mojom::FetchAPIRequestPtr> requests(num_requests);
for (size_t i = 0; i < requests.size(); i++) {
requests[i] = blink::mojom::FetchAPIRequest::New();
requests[i]->referrer = blink::mojom::Referrer::New();
// Creates a URL of the form: `http://example.com/x`
requests[i]->url = GURL(origin.GetURL().spec() + base::NumberToString(i));
}
return requests;
}
blink::mojom::FetchAPIRequestPtr CreateValidRequestWithMethod(
const url::Origin& origin,
const std::string& method) {
auto request = blink::mojom::FetchAPIRequest::New();
request->referrer = blink::mojom::Referrer::New();
request->url = origin.GetURL();
request->method = method;
return request;
}
SkBitmap CreateTestIcon(int size = 42, SkColor color = SK_ColorGREEN) {
SkBitmap icon;
icon.allocN32Pixels(size, size);
icon.eraseColor(SK_ColorGREEN);
return icon;
}
void ExpectIconProperties(const SkBitmap& icon, int size, SkColor color) {
EXPECT_FALSE(icon.isNull());
EXPECT_EQ(icon.width(), size);
EXPECT_EQ(icon.height(), size);
for (int i = 0; i < icon.width(); i++) {
for (int j = 0; j < icon.height(); j++)
EXPECT_EQ(icon.getColor(i, j), color);
}
}
std::vector<blink::mojom::FetchAPIRequestPtr> CloneRequestVector(
const std::vector<blink::mojom::FetchAPIRequestPtr>& requests) {
std::vector<blink::mojom::FetchAPIRequestPtr> requests_out;
for (const auto& request : requests)
requests_out.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
return requests_out;
}
} // namespace
class BackgroundFetchDataManagerTest
: public BackgroundFetchTestBase,
public BackgroundFetchDataManagerObserver {
public:
BackgroundFetchDataManagerTest() {
RestartDataManagerFromPersistentStorage();
}
~BackgroundFetchDataManagerTest() override {
background_fetch_data_manager_->RemoveObserver(this);
}
// Re-creates the data manager. Useful for testing that data was persisted.
void RestartDataManagerFromPersistentStorage() {
background_fetch_data_manager_ =
std::make_unique<BackgroundFetchTestDataManager>(
browser_context(), storage_partition(),
embedded_worker_test_helper()->context_wrapper());
background_fetch_data_manager_->AddObserver(this);
background_fetch_data_manager_->InitializeOnIOThread();
}
// Synchronous version of BackgroundFetchDataManager::GetInitializationData().
std::vector<BackgroundFetchInitializationData> GetInitializationData() {
// Simulate browser restart. This re-initializes |data_manager_|, since
// this DatabaseTask should only be called on browser startup.
RestartDataManagerFromPersistentStorage();
std::vector<BackgroundFetchInitializationData> result;
base::RunLoop run_loop;
background_fetch_data_manager_->GetInitializationData(base::BindOnce(
&DidGetInitializationData, run_loop.QuitClosure(), &result));
run_loop.Run();
return result;
}
// Synchronous version of BackgroundFetchDataManager::CreateRegistration().
void CreateRegistration(
const BackgroundFetchRegistrationId& registration_id,
std::vector<blink::mojom::FetchAPIRequestPtr> requests,
blink::mojom::BackgroundFetchOptionsPtr options,
const SkBitmap& icon,
blink::mojom::BackgroundFetchError* out_error) {
DCHECK(out_error);
base::RunLoop run_loop;
background_fetch_data_manager_->CreateRegistration(
registration_id, std::move(requests), std::move(options), icon,
/* start_paused= */ false,
base::BindOnce(&DidCreateRegistration, run_loop.QuitClosure(),
out_error));
run_loop.Run();
// Check that a cache was created.
if (*out_error == blink::mojom::BackgroundFetchError::NONE)
DCHECK(HasCache(registration_id.unique_id()));
}
blink::mojom::BackgroundFetchRegistrationPtr GetRegistration(
int64_t service_worker_registration_id,
const url::Origin& origin,
const std::string developer_id,
blink::mojom::BackgroundFetchError* out_error) {
DCHECK(out_error);
auto registration = blink::mojom::BackgroundFetchRegistration::New();
base::RunLoop run_loop;
background_fetch_data_manager_->GetRegistration(
service_worker_registration_id, origin, developer_id,
base::BindOnce(&BackgroundFetchDataManagerTest::DidGetRegistration,
base::Unretained(this), run_loop.QuitClosure(),
out_error, registration.get()));
run_loop.Run();
return registration;
}
std::unique_ptr<proto::BackgroundFetchMetadata> GetMetadata(
int64_t service_worker_registration_id,
const std::string& unique_id) {
std::unique_ptr<proto::BackgroundFetchMetadata> metadata;
base::RunLoop run_loop;
embedded_worker_test_helper()->context_wrapper()->GetRegistrationUserData(
service_worker_registration_id,
{background_fetch::RegistrationKey(unique_id)},
base::BindOnce(&BackgroundFetchDataManagerTest::DidGetMetadata,
base::Unretained(this), run_loop.QuitClosure(),
&metadata));
run_loop.Run();
return metadata;
}
std::vector<std::string> GetDeveloperIds(
int64_t service_worker_registration_id,
const url::Origin& origin,
blink::mojom::BackgroundFetchError* out_error) {
DCHECK(out_error);
std::vector<std::string> ids;
base::RunLoop run_loop;
background_fetch_data_manager_->GetDeveloperIdsForServiceWorker(
service_worker_registration_id, origin,
base::BindOnce(&BackgroundFetchDataManagerTest::DidGetDeveloperIds,
base::Unretained(this), run_loop.QuitClosure(),
out_error, &ids));
run_loop.Run();
return ids;
}
// Synchronous version of
// BackgroundFetchDataManager::PopNextRequest().
void PopNextRequest(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchError* out_error,
scoped_refptr<BackgroundFetchRequestInfo>* out_request_info) {
DCHECK(out_error);
DCHECK(out_request_info);
base::RunLoop run_loop;
background_fetch_data_manager_->PopNextRequest(
registration_id,
base::BindOnce(&BackgroundFetchDataManagerTest::DidPopNextRequest,
base::Unretained(this), run_loop.QuitClosure(),
out_error, out_request_info));
run_loop.Run();
}
// Synchronous version of
// BackgroundFetchDataManager::GetRequestBlob().
std::string GetRequestBlobAsString(
const BackgroundFetchRegistrationId& registration_id,
const scoped_refptr<BackgroundFetchRequestInfo>& request_info,
blink::mojom::BackgroundFetchError* out_error) {
DCHECK(out_error);
blink::mojom::SerializedBlobPtr blob;
base::RunLoop run_loop;
background_fetch_data_manager_->GetRequestBlob(
registration_id, request_info,
base::BindOnce(&BackgroundFetchDataManagerTest::DidGetRequestBlob,
base::Unretained(this), run_loop.QuitClosure(),
out_error, &blob));
run_loop.Run();
if (blob && blob->blob) {
blink::mojom::BlobPtr blob_ptr(std::move(blob->blob));
return CopyBody(blob_ptr.get());
}
return std::string();
}
// Synchronous version of
// BackgroundFetchDataManager::MarkRegistrationForDeletion().
void MarkRegistrationForDeletion(
const BackgroundFetchRegistrationId& registration_id,
bool check_for_failure,
blink::mojom::BackgroundFetchError* out_error,
blink::mojom::BackgroundFetchFailureReason* out_failure_reason) {
DCHECK(out_error);
DCHECK(out_failure_reason);
base::RunLoop run_loop;
background_fetch_data_manager_->MarkRegistrationForDeletion(
registration_id, check_for_failure,
base::BindOnce(
&BackgroundFetchDataManagerTest::DidMarkRegistrationForDeletion,
base::Unretained(this), run_loop.QuitClosure(), out_error,
out_failure_reason));
run_loop.Run();
}
// Synchronous version of BackgroundFetchDataManager::DeleteRegistration().
void DeleteRegistration(const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchError* out_error) {
DCHECK(out_error);
base::RunLoop run_loop;
background_fetch_data_manager_->DeleteRegistration(
registration_id,
base::BindOnce(&DidGetError, run_loop.QuitClosure(), out_error));
run_loop.Run();
}
// Synchronous version of BackgroundFetchDataManager::MarkRequestAsComplete().
void MarkRequestAsComplete(
const BackgroundFetchRegistrationId& registration_id,
BackgroundFetchRequestInfo* request_info,
blink::mojom::BackgroundFetchError* out_error) {
DCHECK(out_error);
base::RunLoop run_loop;
background_fetch_data_manager_->MarkRequestAsComplete(
registration_id, request_info,
base::BindOnce(
&BackgroundFetchDataManagerTest::DidMarkRequestAsComplete,
base::Unretained(this), run_loop.QuitClosure(), out_error));
run_loop.Run();
}
// Synchronous version of
// BackgroundFetchDataManager::MatchRequests().
void MatchRequests(const BackgroundFetchRegistrationId& registration_id,
blink::mojom::FetchAPIRequestPtr request_to_match,
blink::mojom::CacheQueryOptionsPtr cache_query_options,
bool match_all,
blink::mojom::BackgroundFetchError* out_error,
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr>*
out_settled_fetches) {
DCHECK(out_error);
DCHECK(out_settled_fetches);
base::RunLoop run_loop;
auto match_params = std::make_unique<BackgroundFetchRequestMatchParams>(
std::move(request_to_match), std::move(cache_query_options), match_all);
background_fetch_data_manager_->MatchRequests(
registration_id, std::move(match_params),
base::BindOnce(&BackgroundFetchDataManagerTest::DidMatchRequests,
base::Unretained(this), run_loop.QuitClosure(),
out_error, out_settled_fetches));
run_loop.Run();
}
// Synchronous version of
// ServiceWorkerContextWrapper::GetRegistrationUserDataByKeyPrefix.
std::vector<std::string> GetRegistrationUserDataByKeyPrefix(
int64_t service_worker_registration_id,
const std::string& key_prefix) {
std::vector<std::string> data;
base::RunLoop run_loop;
embedded_worker_test_helper()
->context_wrapper()
->GetRegistrationUserDataByKeyPrefix(
service_worker_registration_id, key_prefix,
base::BindOnce(&DidGetRegistrationUserDataByKeyPrefix,
run_loop.QuitClosure(), &data));
run_loop.Run();
return data;
}
// Synchronously writes data to the SW DB.
void StoreUserData(int64_t service_worker_registration_id,
const std::string& key,
const std::string& value) {
std::vector<std::string> data;
base::RunLoop run_loop;
embedded_worker_test_helper()->context_wrapper()->StoreRegistrationUserData(
service_worker_registration_id, origin().GetURL(), {{key, value}},
base::BindOnce(&DidStoreUserData, run_loop.QuitClosure()));
run_loop.Run();
}
// Synchronous version of CacheStorageManager::HasCache().
bool HasCache(const std::string& cache_name) {
bool result = false;
base::RunLoop run_loop;
CacheStorageHandle cache_storage =
background_fetch_data_manager_->cache_manager()->OpenCacheStorage(
origin(), CacheStorageOwner::kBackgroundFetch);
cache_storage.value()->HasCache(
cache_name,
base::BindOnce(&BackgroundFetchDataManagerTest::DidFindCache,
base::Unretained(this), run_loop.QuitClosure(),
&result));
run_loop.Run();
return result;
}
// Synchronous version of CacheStorageManager::MatchCache().
bool MatchCache(const blink::mojom::FetchAPIRequestPtr& request) {
bool result = false;
base::RunLoop run_loop;
CacheStorageHandle cache_storage =
background_fetch_data_manager_->cache_manager()->OpenCacheStorage(
origin(), CacheStorageOwner::kBackgroundFetch);
auto match_options = blink::mojom::CacheQueryOptions::New();
match_options->ignore_search = true;
cache_storage.value()->MatchCache(
kExampleUniqueId, BackgroundFetchSettledFetch::CloneRequest(request),
std::move(match_options),
base::BindOnce(&BackgroundFetchDataManagerTest::DidMatchCache,
base::Unretained(this), run_loop.QuitClosure(),
&result));
run_loop.Run();
return result;
}
void DeleteFromCache(const blink::mojom::FetchAPIRequestPtr& request) {
CacheStorageCacheHandle handle;
{
base::RunLoop run_loop;
CacheStorageHandle cache_storage =
background_fetch_data_manager_->cache_manager()->OpenCacheStorage(
origin(), CacheStorageOwner::kBackgroundFetch);
cache_storage.value()->OpenCache(
/* cache_name= */ kExampleUniqueId,
base::BindOnce(&BackgroundFetchDataManagerTest::DidOpenCache,
base::Unretained(this), run_loop.QuitClosure(),
&handle));
run_loop.Run();
}
DCHECK(handle.value());
{
base::RunLoop run_loop;
std::vector<blink::mojom::BatchOperationPtr> operation_ptr_vec;
operation_ptr_vec.push_back(blink::mojom::BatchOperation::New());
operation_ptr_vec[0]->operation_type =
blink::mojom::OperationType::kDelete;
operation_ptr_vec[0]->request =
BackgroundFetchSettledFetch::CloneRequest(request);
operation_ptr_vec[0]->match_options =
blink::mojom::CacheQueryOptions::New();
operation_ptr_vec[0]->match_options->ignore_search = true;
handle.value()->BatchOperation(
std::move(operation_ptr_vec), /* fail_on_duplicates= */ true,
base::BindOnce(&BackgroundFetchDataManagerTest::DidDeleteFromCache,
base::Unretained(this), run_loop.QuitClosure()),
base::DoNothing());
run_loop.Run();
}
}
void PutInCache(const blink::mojom::FetchAPIRequestPtr& request,
blink::mojom::FetchAPIResponsePtr response) {
CacheStorageCacheHandle handle;
{
base::RunLoop run_loop;
CacheStorageHandle cache_storage =
background_fetch_data_manager_->cache_manager()->OpenCacheStorage(
origin(), CacheStorageOwner::kBackgroundFetch);
cache_storage.value()->OpenCache(
/* cache_name= */ kExampleUniqueId,
base::BindOnce(&BackgroundFetchDataManagerTest::DidOpenCache,
base::Unretained(this), run_loop.QuitClosure(),
&handle));
run_loop.Run();
}
DCHECK(handle.value());
{
base::RunLoop run_loop;
std::vector<blink::mojom::BatchOperationPtr> operation_ptr_vec;
operation_ptr_vec.push_back(blink::mojom::BatchOperation::New());
operation_ptr_vec[0]->operation_type = blink::mojom::OperationType::kPut;
operation_ptr_vec[0]->request =
BackgroundFetchSettledFetch::CloneRequest(request);
operation_ptr_vec[0]->response = std::move(response);
handle.value()->BatchOperation(
std::move(operation_ptr_vec), /* fail_on_duplicates= */ true,
base::BindOnce(&BackgroundFetchDataManagerTest::DidDeleteFromCache,
base::Unretained(this), run_loop.QuitClosure()),
base::DoNothing());
run_loop.Run();
}
}
// Returns the title and the icon.
std::pair<std::string, SkBitmap> GetUIOptions(
int64_t service_worker_registration_id) {
auto results = GetRegistrationUserDataByKeyPrefix(
service_worker_registration_id, background_fetch::kUIOptionsKeyPrefix);
DCHECK_LT(results.size(), 2u)
<< "Using GetUIOptions with multiple registrations is unimplemented";
proto::BackgroundFetchUIOptions ui_options;
if (results.empty())
return {"", SkBitmap()};
bool did_parse = ui_options.ParseFromString(results[0]);
DCHECK(did_parse);
std::pair<std::string, SkBitmap> result{ui_options.title(), SkBitmap()};
if (ui_options.icon().empty())
return result;
// Deserialize icon.
{
base::RunLoop run_loop;
background_fetch::DeserializeIcon(
std::unique_ptr<std::string>(ui_options.release_icon()),
base::BindOnce(
[](base::OnceClosure quit_closure, SkBitmap* out_icon,
SkBitmap icon) {
DCHECK(out_icon);
*out_icon = std::move(icon);
std::move(quit_closure).Run();
},
run_loop.QuitClosure(), &result.second));
run_loop.Run();
}
return result;
}
// Gets information about the number of background fetch requests by state.
ResponseStateStats GetRequestStats(int64_t service_worker_registration_id) {
ResponseStateStats stats;
{
base::RunLoop run_loop;
embedded_worker_test_helper()
->context_wrapper()
->GetRegistrationUserDataByKeyPrefix(
service_worker_registration_id,
background_fetch::kPendingRequestKeyPrefix,
base::BindOnce(&GetNumUserData, run_loop.QuitClosure(),
&stats.pending_requests));
run_loop.Run();
}
{
base::RunLoop run_loop;
embedded_worker_test_helper()
->context_wrapper()
->GetRegistrationUserDataByKeyPrefix(
service_worker_registration_id,
background_fetch::kActiveRequestKeyPrefix,
base::BindOnce(&GetNumUserData, run_loop.QuitClosure(),
&stats.active_requests));
run_loop.Run();
}
{
base::RunLoop run_loop;
embedded_worker_test_helper()
->context_wrapper()
->GetRegistrationUserDataByKeyPrefix(
service_worker_registration_id,
background_fetch::kCompletedRequestKeyPrefix,
base::BindOnce(&GetNumUserData, run_loop.QuitClosure(),
&stats.completed_requests));
run_loop.Run();
}
return stats;
}
void AnnotateRequestInfoWithFakeDownloadManagerData(
BackgroundFetchRequestInfo* request_info,
bool success = false,
bool over_quota = false) {
DCHECK(request_info);
std::string headers =
success ? "HTTP/1.1 200 OK\n" : "HTTP/1.1 404 Not found\n";
auto response = std::make_unique<BackgroundFetchResponse>(
std::vector<GURL>(1u, request_info->fetch_request()->url),
base::MakeRefCounted<net::HttpResponseHeaders>(headers));
if (!success) {
// Fill |request_info| with a failed result.
request_info->SetResult(std::make_unique<BackgroundFetchResult>(
std::move(response), base::Time::Now(),
BackgroundFetchResult::FailureReason::FETCH_ERROR));
return;
}
std::string response_data(
over_quota ? kBackgroundFetchMaxQuotaBytes + 1 : kResponseSize, 'x');
auto blob_builder =
std::make_unique<storage::BlobDataBuilder>(base::GenerateGUID());
blob_builder->AppendData(response_data);
auto handle = background_fetch_data_manager_->blob_storage_context()
->context()
->AddFinishedBlob(std::move(blob_builder));
request_info->SetResult(std::make_unique<BackgroundFetchResult>(
std::move(response), base::Time::Now(), base::FilePath(),
std::move(*handle), /* file_size= */ 0u));
}
// BackgroundFetchDataManagerObserver mocks:
MOCK_METHOD6(
OnRegistrationCreated,
void(const BackgroundFetchRegistrationId& registration_id,
const blink::mojom::BackgroundFetchRegistration& registration,
blink::mojom::BackgroundFetchOptionsPtr options,
const SkBitmap& icon,
int num_requests,
bool start_paused));
MOCK_METHOD7(
OnRegistrationLoadedAtStartup,
void(const BackgroundFetchRegistrationId& registration_id,
const blink::mojom::BackgroundFetchRegistration& registration,
blink::mojom::BackgroundFetchOptionsPtr options,
const SkBitmap& icon,
int num_completed_requests,
int num_requests,
std::vector<scoped_refptr<BackgroundFetchRequestInfo>>
active_fetch_requests));
MOCK_METHOD1(OnRegistrationQueried,
void(blink::mojom::BackgroundFetchRegistration* registration));
MOCK_METHOD1(OnServiceWorkerDatabaseCorrupted,
void(int64_t service_worker_registration_id));
MOCK_METHOD3(OnRequestCompleted,
void(const std::string& unique_id,
blink::mojom::FetchAPIRequestPtr request,
blink::mojom::FetchAPIResponsePtr response));
protected:
void DidGetRegistration(
base::OnceClosure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
blink::mojom::BackgroundFetchRegistration* out_registration,
blink::mojom::BackgroundFetchError error,
blink::mojom::BackgroundFetchRegistrationPtr registration) {
*out_error = error;
*out_registration = *registration;
std::move(quit_closure).Run();
}
void DidGetMetadata(
base::OnceClosure quit_closure,
std::unique_ptr<proto::BackgroundFetchMetadata>* out_metadata,
const std::vector<std::string>& data,
blink::ServiceWorkerStatusCode status) {
if (status == blink::ServiceWorkerStatusCode::kOk) {
DCHECK_EQ(data.size(), 1u);
auto metadata = std::make_unique<proto::BackgroundFetchMetadata>();
if (metadata->ParseFromString(data[0]))
*out_metadata = std::move(metadata);
}
std::move(quit_closure).Run();
}
void DidGetDeveloperIds(base::Closure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
std::vector<std::string>* out_ids,
blink::mojom::BackgroundFetchError error,
const std::vector<std::string>& ids) {
*out_error = error;
*out_ids = ids;
std::move(quit_closure).Run();
}
void DidPopNextRequest(
base::OnceClosure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
scoped_refptr<BackgroundFetchRequestInfo>* out_request_info,
blink::mojom::BackgroundFetchError error,
scoped_refptr<BackgroundFetchRequestInfo> request_info) {
*out_error = error;
*out_request_info = request_info;
std::move(quit_closure).Run();
}
void DidGetRequestBlob(base::OnceClosure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
blink::mojom::SerializedBlobPtr* out_blob,
blink::mojom::BackgroundFetchError error,
blink::mojom::SerializedBlobPtr blob) {
*out_error = error;
*out_blob = std::move(blob);
std::move(quit_closure).Run();
}
void DidMarkRequestAsComplete(base::OnceClosure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
blink::mojom::BackgroundFetchError error) {
*out_error = error;
std::move(quit_closure).Run();
}
void DidMarkRegistrationForDeletion(
base::OnceClosure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
blink::mojom::BackgroundFetchFailureReason* out_failure_reason,
blink::mojom::BackgroundFetchError error,
blink::mojom::BackgroundFetchFailureReason failure_reason) {
*out_error = error;
*out_failure_reason = failure_reason;
std::move(quit_closure).Run();
}
void DidMatchRequests(
base::OnceClosure quit_closure,
blink::mojom::BackgroundFetchError* out_error,
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr>*
out_settled_fetches,
blink::mojom::BackgroundFetchError error,
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr>
settled_fetches) {
*out_error = error;
*out_settled_fetches = std::move(settled_fetches);
std::move(quit_closure).Run();
}
void DidFindCache(base::OnceClosure quit_closure,
bool* out_result,
bool has_cache,
blink::mojom::CacheStorageError error) {
DCHECK_EQ(error, blink::mojom::CacheStorageError::kSuccess);
*out_result = has_cache;
std::move(quit_closure).Run();
}
void DidMatchCache(base::OnceClosure quit_closure,
bool* out_result,
blink::mojom::CacheStorageError error,
blink::mojom::FetchAPIResponsePtr response) {
// This counts as matched if an entry was found in the cache which
// also has a non-empty response.
*out_result = !response.is_null() && !response->url_list.empty();
std::move(quit_closure).Run();
}
void DidOpenCache(base::OnceClosure quit_closure,
CacheStorageCacheHandle* out_handle,
CacheStorageCacheHandle handle,
blink::mojom::CacheStorageError error) {
DCHECK(out_handle);
DCHECK_EQ(error, blink::mojom::CacheStorageError::kSuccess);
*out_handle = std::move(handle);
std::move(quit_closure).Run();
}
void DidDeleteFromCache(base::OnceClosure quit_closure,
blink::mojom::CacheStorageVerboseErrorPtr error) {
DCHECK_EQ(error->value, blink::mojom::CacheStorageError::kSuccess);
std::move(quit_closure).Run();
}
class DataPipeDrainerClient : public mojo::DataPipeDrainer::Client {
public:
explicit DataPipeDrainerClient(std::string* output) : output_(output) {}
void Run() { run_loop_.Run(); }
void OnDataAvailable(const void* data, size_t num_bytes) override {
output_->append(reinterpret_cast<const char*>(data), num_bytes);
}
void OnDataComplete() override { run_loop_.Quit(); }
private:
base::RunLoop run_loop_;
std::string* output_;
};
std::string CopyBody(blink::mojom::Blob* blob) {
mojo::DataPipe pipe;
blob->ReadAll(std::move(pipe.producer_handle), /* client= */ nullptr);
std::string output;
DataPipeDrainerClient client(&output);
mojo::DataPipeDrainer drainer(&client, std::move(pipe.consumer_handle));
client.Run();
return output;
}
blink::mojom::SerializedBlobPtr BuildBlob(const std::string data) {
auto blob_data = std::make_unique<storage::BlobDataBuilder>(
"blob-id:" + base::GenerateGUID());
blob_data->AppendData(data);
std::unique_ptr<storage::BlobDataHandle> blob_handle =
background_fetch_data_manager_->blob_storage_context_->context()
->AddFinishedBlob(std::move(blob_data));
auto blob = blink::mojom::SerializedBlob::New();
blob->uuid = blob_handle->uuid();
blob->size = blob_handle->size();
storage::BlobImpl::Create(
std::make_unique<storage::BlobDataHandle>(*blob_handle),
MakeRequest(&blob->blob));
return blob;
}
std::unique_ptr<BackgroundFetchTestDataManager>
background_fetch_data_manager_;
};
TEST_F(BackgroundFetchDataManagerTest, NoDuplicateRegistrations) {
// Tests that the BackgroundFetchDataManager correctly rejects creating a
// registration with a |developer_id| for which there is already an active
// registration.
int64_t service_worker_registration_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
service_worker_registration_id);
BackgroundFetchRegistrationId registration_id1(service_worker_registration_id,
origin(), kExampleDeveloperId,
kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin());
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
blink::mojom::BackgroundFetchFailureReason failure_reason;
// Deactivating the not-yet-created registration should fail.
MarkRegistrationForDeletion(registration_id1, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ID);
// Creating the initial registration should succeed.
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id1, _, _, _, _, _));
CreateRegistration(registration_id1, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Different |unique_id|, since this is a new Background Fetch registration,
// even though it shares the same |developer_id|.
BackgroundFetchRegistrationId registration_id2(service_worker_registration_id,
origin(), kExampleDeveloperId,
kAlternativeUniqueId);
// Attempting to create a second registration with the same |developer_id| and
// |service_worker_registration_id| should yield an error.
CreateRegistration(registration_id2, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::DUPLICATED_DEVELOPER_ID);
// Deactivating the second registration that failed to be created should fail.
MarkRegistrationForDeletion(registration_id2, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ID);
// Deactivating the initial registration should succeed.
MarkRegistrationForDeletion(registration_id1, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// And now registering the second registration should work fine, since there
// is no longer an *active* registration with the same |developer_id|, even
// though the initial registration has not yet been deleted.
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id2, _, _, _, _, _));
CreateRegistration(registration_id2, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
}
TEST_F(BackgroundFetchDataManagerTest, ExceedingQuotaFailsCreation) {
// Tests that the BackgroundFetchDataManager correctly rejects creating a
// registration where the provided download total exceeds the available quota.
int64_t service_worker_registration_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
service_worker_registration_id);
BackgroundFetchRegistrationId registration_id(service_worker_registration_id,
origin(), kExampleDeveloperId,
kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin());
auto options = blink::mojom::BackgroundFetchOptions::New();
options->download_total = kBackgroundFetchMaxQuotaBytes + 1;
blink::mojom::BackgroundFetchError error;
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::QUOTA_EXCEEDED);
}
TEST_F(BackgroundFetchDataManagerTest, RegistrationLimitIsEnforced) {
// Tests that the BackgroundFetchDataManager correctly rejects creating a
// registration when an origin exceeds the allowed number of registrations.
int64_t swid1 = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, swid1);
int64_t swid2 = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, swid2);
ASSERT_NE(swid1, swid2);
blink::mojom::BackgroundFetchError error;
// Create two registrations for every Service Worker.
for (int i = 0; i < 2; i++) {
// First Service Worker.
BackgroundFetchRegistrationId registration_id1(
swid1, origin(), kExampleDeveloperId + base::NumberToString(i),
base::GenerateGUID());
CreateRegistration(
registration_id1, std::vector<blink::mojom::FetchAPIRequestPtr>(),
blink::mojom::BackgroundFetchOptions::New(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Second service Worker.
BackgroundFetchRegistrationId registration_id2(
swid2, origin(), kExampleDeveloperId + base::NumberToString(i),
base::GenerateGUID());
CreateRegistration(
registration_id2, std::vector<blink::mojom::FetchAPIRequestPtr>(),
blink::mojom::BackgroundFetchOptions::New(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Create another registration in the first Service Worker,
// bringing us to the limit.
{
BackgroundFetchRegistrationId registration_id(
swid1, origin(), "developer_id1", base::GenerateGUID());
CreateRegistration(
registration_id, std::vector<blink::mojom::FetchAPIRequestPtr>(),
blink::mojom::BackgroundFetchOptions::New(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// A registration this time should fail.
{
BackgroundFetchRegistrationId registration_id(
swid1, origin(), "developer_id2", base::GenerateGUID());
CreateRegistration(
registration_id, std::vector<blink::mojom::FetchAPIRequestPtr>(),
blink::mojom::BackgroundFetchOptions::New(), SkBitmap(), &error);
ASSERT_EQ(error,
blink::mojom::BackgroundFetchError::REGISTRATION_LIMIT_EXCEEDED);
}
// The registration should also fail for the other Service Worker.
{
BackgroundFetchRegistrationId registration_id(
swid2, origin(), "developer_id3", base::GenerateGUID());
CreateRegistration(
registration_id, std::vector<blink::mojom::FetchAPIRequestPtr>(),
blink::mojom::BackgroundFetchOptions::New(), SkBitmap(), &error);
ASSERT_EQ(error,
blink::mojom::BackgroundFetchError::REGISTRATION_LIMIT_EXCEEDED);
}
}
TEST_F(BackgroundFetchDataManagerTest, GetDeveloperIds) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
// Verify that no developer IDs can be found.
auto developer_ids = GetDeveloperIds(sw_id, origin(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_THAT(developer_ids, IsEmpty());
// Create a single registration.
BackgroundFetchRegistrationId registration_id1(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id1, _, _, _, _, _));
CreateRegistration(registration_id1, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Verify that the developer ID can be found.
developer_ids = GetDeveloperIds(sw_id, origin(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_THAT(developer_ids, UnorderedElementsAre(kExampleDeveloperId));
RestartDataManagerFromPersistentStorage();
// After a restart, GetDeveloperIds should still find the IDs.
developer_ids = GetDeveloperIds(sw_id, origin(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_THAT(developer_ids, UnorderedElementsAre(kExampleDeveloperId));
// Create another registration.
BackgroundFetchRegistrationId registration_id2(
sw_id, origin(), kAlternativeDeveloperId, kAlternativeUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id2, _, _, _, _, _));
CreateRegistration(registration_id2, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Verify that both developer IDs can be found.
developer_ids = GetDeveloperIds(sw_id, origin(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_THAT(developer_ids, UnorderedElementsAre(kExampleDeveloperId,
kAlternativeDeveloperId));
RestartDataManagerFromPersistentStorage();
// After a restart, GetDeveloperIds should still find the IDs.
developer_ids = GetDeveloperIds(sw_id, origin(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_THAT(developer_ids, UnorderedElementsAre(kExampleDeveloperId,
kAlternativeDeveloperId));
}
TEST_F(BackgroundFetchDataManagerTest, StorageVersionIsPersisted) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
{
// Create a single registration.
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
CreateRegistration(registration_id, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
auto storage_versions = GetRegistrationUserDataByKeyPrefix(
sw_id, background_fetch::StorageVersionKey(kExampleUniqueId));
ASSERT_EQ(storage_versions.size(), 1u);
EXPECT_EQ(storage_versions[0], base::NumberToString(proto::SV_CURRENT));
}
TEST_F(BackgroundFetchDataManagerTest, GetRegistration) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
// Create a single registration.
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Verify that the registration can be retrieved.
auto registration =
GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(kExampleUniqueId, registration->unique_id);
EXPECT_EQ(kExampleDeveloperId, registration->developer_id);
EXPECT_EQ(0u, registration->upload_total);
// Verify that retrieving using the wrong developer id doesn't work.
registration =
GetRegistration(sw_id, origin(), kAlternativeDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ID);
RestartDataManagerFromPersistentStorage();
// After a restart, GetRegistration should still find the registration.
registration = GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(kExampleUniqueId, registration->unique_id);
EXPECT_EQ(kExampleDeveloperId, registration->developer_id);
}
TEST_F(BackgroundFetchDataManagerTest, GetMetadata) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
size_t num_requests = 2u;
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), num_requests);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
// Create a single registration.
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Verify that the metadata can be retrieved.
auto metadata = GetMetadata(sw_id, kExampleUniqueId);
ASSERT_TRUE(metadata);
EXPECT_EQ(metadata->origin(), origin().Serialize());
EXPECT_NE(metadata->creation_microseconds_since_unix_epoch(), 0);
EXPECT_EQ(metadata->num_fetches(), static_cast<int>(num_requests));
RestartDataManagerFromPersistentStorage();
// After a restart, GetMetadata should still find the registration.
metadata = GetMetadata(sw_id, kExampleUniqueId);
ASSERT_TRUE(metadata);
EXPECT_EQ(metadata->origin(), origin().Serialize());
EXPECT_NE(metadata->creation_microseconds_since_unix_epoch(), 0);
EXPECT_EQ(metadata->num_fetches(), static_cast<int>(num_requests));
}
TEST_F(BackgroundFetchDataManagerTest, RegistrationUploadInfo) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
const std::string upload_data = "Upload!";
// Create a single registration.
{
// One upload and one download.
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
requests[0]->blob = BuildBlob(upload_data);
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
auto registration =
GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(registration->unique_id, kExampleUniqueId);
EXPECT_EQ(registration->developer_id, kExampleDeveloperId);
EXPECT_EQ(registration->upload_total, upload_data.size());
}
TEST_F(BackgroundFetchDataManagerTest, LargeIconNotPersisted) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
size_t num_requests = 2u;
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), num_requests);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
SkBitmap icon = CreateTestIcon(/* size= */ 512);
ASSERT_FALSE(background_fetch::ShouldPersistIcon(icon));
// Create a single registration.
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
icon, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Verify that the metadata can be retrieved.
auto metadata = GetMetadata(sw_id, kExampleUniqueId);
ASSERT_TRUE(metadata);
EXPECT_EQ(metadata->origin(), origin().Serialize());
EXPECT_NE(metadata->creation_microseconds_since_unix_epoch(), 0);
EXPECT_EQ(metadata->num_fetches(), static_cast<int>(num_requests));
EXPECT_TRUE(GetUIOptions(sw_id).second.isNull());
}
TEST_F(BackgroundFetchDataManagerTest, CreateAndDeleteRegistration) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id1(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
blink::mojom::BackgroundFetchFailureReason failure_reason;
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id1, _, _, _, _, _));
CreateRegistration(registration_id1, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
RestartDataManagerFromPersistentStorage();
// Different |unique_id|, since this is a new Background Fetch registration,
// even though it shares the same |developer_id|.
BackgroundFetchRegistrationId registration_id2(
sw_id, origin(), kExampleDeveloperId, kAlternativeUniqueId);
// Attempting to create a second registration with the same |developer_id| and
// |service_worker_registration_id| should yield an error, even after
// restarting.
CreateRegistration(registration_id2, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::DUPLICATED_DEVELOPER_ID);
// Verify that the registration can be retrieved before deletion.
auto registration =
GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(kExampleUniqueId, registration->unique_id);
EXPECT_EQ(kExampleDeveloperId, registration->developer_id);
// Deactivating the registration should succeed.
MarkRegistrationForDeletion(registration_id1, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(failure_reason, blink::mojom::BackgroundFetchFailureReason::NONE);
// Verify that the registration cannot be retrieved after deletion
registration = GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ID);
RestartDataManagerFromPersistentStorage();
// Verify again that the registration cannot be retrieved after deletion and
// a restart.
registration = GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::INVALID_ID);
// And now registering the second registration should work fine, even after
// restarting, since there is no longer an *active* registration with the same
// |developer_id|, even though the initial registration has not yet been
// deleted.
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id2, _, _, _, _, _));
CreateRegistration(registration_id2, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
RestartDataManagerFromPersistentStorage();
// Deleting the inactive first registration should succeed.
DeleteRegistration(registration_id1, &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
TEST_F(BackgroundFetchDataManagerTest, MarkRegistrationForDeletion) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id1(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
blink::mojom::BackgroundFetchFailureReason failure_reason;
CreateRegistration(registration_id1, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Create a |developer_id| such that the other one is a substring.
std::string developer_id2 = std::string(kExampleDeveloperId) + "!";
BackgroundFetchRegistrationId registration_id2(sw_id, origin(), developer_id2,
kAlternativeUniqueId);
CreateRegistration(registration_id2, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Get all active registration mappings.
{
auto registrations = GetRegistrationUserDataByKeyPrefix(
sw_id, background_fetch::ActiveRegistrationUniqueIdKey(""));
EXPECT_EQ(registrations.size(), 2u);
}
// Deactivate the first registration.
MarkRegistrationForDeletion(registration_id1, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(failure_reason, blink::mojom::BackgroundFetchFailureReason::NONE);
// The second registration should still exist.
{
auto registrations = GetRegistrationUserDataByKeyPrefix(
sw_id, background_fetch::ActiveRegistrationUniqueIdKey(""));
ASSERT_EQ(registrations.size(), 1u);
EXPECT_EQ(registrations[0], kAlternativeUniqueId);
}
}
TEST_F(BackgroundFetchDataManagerTest,
MarkRegistrationForDeletionFailureReason) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id1(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 1u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
blink::mojom::BackgroundFetchFailureReason failure_reason;
// Complete the fetch successfully.
{
CreateRegistration(registration_id1, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id1, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* succeeded= */ true);
MarkRequestAsComplete(registration_id1, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Mark the Registration for deletion.
MarkRegistrationForDeletion(registration_id1, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(failure_reason, blink::mojom::BackgroundFetchFailureReason::NONE);
}
BackgroundFetchRegistrationId registration_id2(
sw_id, origin(), kAlternativeDeveloperId, kAlternativeUniqueId);
// Complete the fetch with a BAD_STATUS.
{
CreateRegistration(registration_id2, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id2, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
MarkRequestAsComplete(registration_id2, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Mark the Registration for deletion.
MarkRegistrationForDeletion(registration_id2, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// The fetch resulted in a 404.
EXPECT_EQ(failure_reason,
blink::mojom::BackgroundFetchFailureReason::BAD_STATUS);
}
}
TEST_F(BackgroundFetchDataManagerTest, PopNextRequestAndMarkAsComplete) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
blink::mojom::BackgroundFetchError error;
scoped_refptr<BackgroundFetchRequestInfo> request_info;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
// There registration hasn't been created yet, so there are no pending
// requests.
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_FALSE(request_info);
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 0, /* active_requests= */ 0,
/* completed_requests= */ 0}));
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 2, /* active_requests= */ 0,
/* completed_requests= */ 0}));
// Popping should work now.
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_TRUE(request_info);
EXPECT_EQ(request_info->request_index(), 0);
EXPECT_FALSE(request_info->download_guid().empty());
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 1, /* active_requests= */ 1,
/* completed_requests= */ 0}));
// Mark as complete.
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 1, /* active_requests= */ 0,
/* completed_requests= */ 1}));
RestartDataManagerFromPersistentStorage();
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_TRUE(request_info);
EXPECT_EQ(request_info->request_index(), 1);
EXPECT_FALSE(request_info->download_guid().empty());
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 0, /* active_requests= */ 1,
/* completed_requests= */ 1}));
// Mark as complete.
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 0, /* active_requests= */ 0,
/* completed_requests= */ 2}));
// We are out of pending requests.
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_FALSE(request_info);
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 0, /* active_requests= */ 0,
/* completed_requests= */ 2}));
}
TEST_F(BackgroundFetchDataManagerTest, GetUploadBody) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
std::vector<blink::mojom::FetchAPIRequestPtr> requests;
{
auto request = blink::mojom::FetchAPIRequest::New();
request->url = GURL("https://example.com/upload");
request->method = "POST";
request->referrer = blink::mojom::Referrer::New();
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
}
requests[0]->blob = BuildBlob("upload1");
requests[1]->blob = BuildBlob("upload2");
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
scoped_refptr<BackgroundFetchRequestInfo> request_info1;
PopNextRequest(registration_id, &error, &request_info1);
ASSERT_TRUE(request_info1);
EXPECT_EQ(request_info1->request_index(), 0);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
scoped_refptr<BackgroundFetchRequestInfo> request_info2;
PopNextRequest(registration_id, &error, &request_info2);
ASSERT_TRUE(request_info2);
EXPECT_EQ(request_info2->request_index(), 1);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
auto payload1 =
GetRequestBlobAsString(registration_id, request_info1, &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(payload1, "upload1");
auto payload2 =
GetRequestBlobAsString(registration_id, request_info2, &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(payload2, "upload2");
}
TEST_F(BackgroundFetchDataManagerTest, RegistrationBytesUpdated) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
auto requests = CreateValidRequests(origin(), /* num_requests= */ 3u);
const std::string upload_payload = "Upload Data";
requests[0]->blob = BuildBlob(upload_payload);
requests[1]->blob = BuildBlob(upload_payload);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
auto registration =
GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(registration->downloaded, 0u);
EXPECT_EQ(registration->uploaded, 0u);
EXPECT_EQ(registration->upload_total, 2 * upload_payload.size());
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* succeeded= */ true);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
registration = GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(registration->downloaded, kResponseSize);
EXPECT_EQ(registration->uploaded, upload_payload.size());
RestartDataManagerFromPersistentStorage();
PopNextRequest(registration_id, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* succeeded= */ true);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
registration = GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(registration->downloaded, 2 * kResponseSize);
EXPECT_EQ(registration->uploaded, 2 * upload_payload.size());
PopNextRequest(registration_id, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* succeeded= */ false);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
registration = GetRegistration(sw_id, origin(), kExampleDeveloperId, &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(registration->downloaded, 2 * kResponseSize);
EXPECT_EQ(registration->uploaded, 2 * upload_payload.size());
}
TEST_F(BackgroundFetchDataManagerTest, ExceedingQuotaIsReported) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
auto requests = CreateValidRequests(origin(), /* num_requests= */ 3u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(
request_info.get(), /* succeeded= */ true, /* over_quota= */ true);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::QUOTA_EXCEEDED);
}
TEST_F(BackgroundFetchDataManagerTest, WriteToCache) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
auto requests = CreateValidRequests(origin(), /* num_requests= */ 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* success= */ true);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_TRUE(HasCache(kExampleUniqueId));
EXPECT_FALSE(HasCache("foo"));
EXPECT_TRUE(MatchCache(requests[0]));
EXPECT_FALSE(MatchCache(requests[1]));
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* success= */ true);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_TRUE(MatchCache(requests[0]));
EXPECT_TRUE(MatchCache(requests[1]));
RestartDataManagerFromPersistentStorage();
EXPECT_TRUE(MatchCache(requests[0]));
EXPECT_TRUE(MatchCache(requests[1]));
}
TEST_F(BackgroundFetchDataManagerTest, CacheDeleted) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
auto request = blink::mojom::FetchAPIRequest::New();
request->referrer = blink::mojom::Referrer::New();
request->url = GURL(origin().GetURL().spec());
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
blink::mojom::BackgroundFetchFailureReason failure_reason;
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
std::vector<blink::mojom::FetchAPIRequestPtr> request_vec;
request_vec.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
CreateRegistration(registration_id, std::move(request_vec),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* success= */ true);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_TRUE(HasCache(kExampleUniqueId));
EXPECT_TRUE(MatchCache(request));
MarkRegistrationForDeletion(registration_id, /* check_for_failure= */ true,
&error, &failure_reason);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
DeleteRegistration(registration_id, &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_FALSE(HasCache(kExampleUniqueId));
}
TEST_F(BackgroundFetchDataManagerTest, MatchRequests) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
size_t num_requests = 2u;
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), num_requests);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 2, /* active_requests= */ 0,
/* completed_requests= */ 0}));
// Nothing is downloaded yet.
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
MatchRequests(registration_id, /* request_to_match= */ nullptr,
/* cache_query_options= */ nullptr, /* match_all= */ true,
&error, &settled_fetches);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(settled_fetches.size(), num_requests);
for (size_t i = 0; i < num_requests; i++) {
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
RestartDataManagerFromPersistentStorage();
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 0, /* active_requests= */ 0,
/* completed_requests= */ num_requests}));
MatchRequests(registration_id, /* request_to_match= */ nullptr,
/* cache_query_options= */ nullptr, /* match_all= */ true,
&error, &settled_fetches);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// We are marking the responses as failed in Download Manager.
EXPECT_EQ(settled_fetches.size(), num_requests);
}
TEST_F(BackgroundFetchDataManagerTest, MatchRequestsWithBody) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
const std::string upload_data = "Upload data!";
requests[1]->blob = BuildBlob(upload_data);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, std::move(requests), std::move(options),
SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
MatchRequests(registration_id, /* request_to_match= */ nullptr,
/* cache_query_options= */ nullptr, /* match_all= */ true,
&error, &settled_fetches);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_EQ(settled_fetches.size(), 2u);
EXPECT_FALSE(settled_fetches[0]->request->blob);
auto& request = settled_fetches[1]->request;
ASSERT_TRUE(request->blob);
EXPECT_EQ(request->blob->size, upload_data.size());
ASSERT_TRUE(request->blob->blob);
blink::mojom::BlobPtr blob(std::move(request->blob->blob));
EXPECT_EQ(CopyBody(blob.get()), upload_data);
}
TEST_F(BackgroundFetchDataManagerTest, MatchRequestsFromCache) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
auto requests = CreateValidRequests(origin(), /* num_requests= */ 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
// Nothing is downloaded yet.
MatchRequests(registration_id, /* request_to_match= */ nullptr,
/* cache_query_options= */ nullptr, /* match_all= */ true,
&error, &settled_fetches);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(settled_fetches.size(), requests.size());
for (size_t i = 0; i < requests.size(); i++) {
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* success= */ true);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
RestartDataManagerFromPersistentStorage();
MatchRequests(registration_id, /* request_to_match= */ nullptr,
/* cache_query_options= */ nullptr, /* match_all= */ true,
&error, &settled_fetches);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_EQ(settled_fetches.size(), requests.size());
ASSERT_TRUE(settled_fetches[0]->response &&
settled_fetches[0]->response->blob);
ASSERT_TRUE(settled_fetches[1]->response &&
settled_fetches[1]->response->blob);
EXPECT_EQ(settled_fetches[0]->response->blob->size, kResponseSize);
EXPECT_EQ(settled_fetches[1]->response->blob->size, kResponseSize);
// Sanity check that the responses are written to / read from the cache.
EXPECT_TRUE(MatchCache(requests[0]));
EXPECT_TRUE(MatchCache(requests[1]));
EXPECT_EQ(settled_fetches[0]->response->cache_storage_cache_name,
kExampleUniqueId);
EXPECT_EQ(settled_fetches[1]->response->cache_storage_cache_name,
kExampleUniqueId);
}
TEST_F(BackgroundFetchDataManagerTest, MatchRequestsForASpecificRequest) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
auto requests = CreateValidRequests(origin(), /* num_requests= */ 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
for (size_t i = 0; i < requests.size(); i++) {
SCOPED_TRACE(i);
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 0, /* active_requests= */ 0,
/* completed_requests= */ requests.size()}));
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
MatchRequests(registration_id, /* request_to_match= */ std::move(requests[0]),
/* cache_query_options= */ nullptr, /* match_all= */ false,
&error, &settled_fetches);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// We are marking the responses as failed in Download Manager.
EXPECT_EQ(settled_fetches.size(), 1u);
// Try matching a non existing request.
auto non_existing_request = blink::mojom::FetchAPIRequest::New();
non_existing_request->url = GURL("https://example.com/missing-file.txt");
MatchRequests(registration_id,
/* request_to_match= */ std::move(non_existing_request),
/* cache_query_options= */ nullptr, /* match_all= */ false,
&error, &settled_fetches);
EXPECT_TRUE(settled_fetches.empty());
}
TEST_F(BackgroundFetchDataManagerTest, MatchRequestsForAnIncompleteRequest) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
auto requests = CreateValidRequests(origin(), /* num_requests= */ 3u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
for (size_t i = 0; i < requests.size() - 1; i++) {
SCOPED_TRACE(i);
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 1, /* active_requests= */ 0,
/* completed_requests= */ requests.size() - 1}));
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
MatchRequests(registration_id, /* request_to_match= */ std::move(requests[2]),
/* cache_query_options= */ nullptr, /* match_all= */ false,
&error, &settled_fetches);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_EQ(settled_fetches.size(), 1u);
EXPECT_TRUE(settled_fetches[0]->response.is_null());
}
TEST_F(BackgroundFetchDataManagerTest, IgnoreMethodAndMatchAll) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
std::vector<blink::mojom::FetchAPIRequestPtr> requests;
requests.push_back(CreateValidRequestWithMethod(origin(), "GET"));
requests.push_back(CreateValidRequestWithMethod(origin(), "POST"));
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
for (size_t i = 0; i < requests.size(); i++) {
SCOPED_TRACE(i);
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
EXPECT_EQ(
GetRequestStats(sw_id),
(ResponseStateStats{/* pending_requests= */ 0, /* active_requests= */ 0,
/* completed_requests= */ requests.size()}));
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
blink::mojom::CacheQueryOptionsPtr cache_query_options =
blink::mojom::CacheQueryOptions::New();
cache_query_options->ignore_method = true;
MatchRequests(registration_id, /* request_to_match= */ std::move(requests[0]),
std::move(cache_query_options), /* match_all= */ true, &error,
&settled_fetches);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_EQ(settled_fetches.size(), 2u);
}
TEST_F(BackgroundFetchDataManagerTest, MatchRequestsWithDuplicates) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
std::vector<blink::mojom::FetchAPIRequestPtr> requests;
const std::string base_path = "https://example.com/foo";
// Add base request.
{
auto request = blink::mojom::FetchAPIRequest::New();
request->url = GURL(base_path);
request->method = "GET";
request->referrer = blink::mojom::Referrer::New();
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
}
// Add base request with a query.
{
auto request = blink::mojom::FetchAPIRequest::New();
request->method = "GET";
request->url = GURL(base_path + "?a=b");
request->referrer = blink::mojom::Referrer::New();
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
request->url = GURL(base_path + "?c=d");
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
}
// Add base request with different method.
{
auto request = blink::mojom::FetchAPIRequest::New();
request->url = GURL(base_path);
request->method = "POST";
request->referrer = blink::mojom::Referrer::New();
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
requests.push_back(BackgroundFetchSettledFetch::CloneRequest(request));
}
blink::mojom::BackgroundFetchError error;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
auto options = blink::mojom::BackgroundFetchOptions::New();
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
auto request_to_match =
BackgroundFetchSettledFetch::CloneRequest(requests[0]);
auto query_options = blink::mojom::CacheQueryOptions::New();
MatchRequests(registration_id,
BackgroundFetchSettledFetch::CloneRequest(request_to_match),
query_options->Clone(), /* match_all= */ true, &error,
&settled_fetches);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Match only the GETs with the same url.
EXPECT_EQ(settled_fetches.size(), 2u);
query_options->ignore_search = true;
MatchRequests(registration_id,
BackgroundFetchSettledFetch::CloneRequest(request_to_match),
query_options->Clone(), /* match_all= */ true, &error,
&settled_fetches);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Match only the GETs with the same url path.
EXPECT_EQ(settled_fetches.size(), 5u);
query_options->ignore_method = true;
MatchRequests(registration_id,
BackgroundFetchSettledFetch::CloneRequest(request_to_match),
query_options->Clone(), /* match_all= */ true, &error,
&settled_fetches);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Match everything.
EXPECT_EQ(settled_fetches.size(), requests.size());
}
TEST_F(BackgroundFetchDataManagerTest, Cleanup) {
// Tests that the BackgroundFetchDataManager cleans up registrations
// marked for deletion.
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
blink::mojom::BackgroundFetchFailureReason failure_reason;
EXPECT_EQ(0u,
GetRegistrationUserDataByKeyPrefix(sw_id, kUserDataPrefix).size());
// Create a registration.
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// We expect as many pending entries as there are requests.
EXPECT_EQ(requests.size(),
GetRegistrationUserDataByKeyPrefix(
sw_id, background_fetch::kPendingRequestKeyPrefix)
.size());
// And deactivate it.
MarkRegistrationForDeletion(registration_id, /* check_for_failure= */ true,
&error, &failure_reason);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
RestartDataManagerFromPersistentStorage();
// Metadata proto + UI options + storage version + remaining pending fetches.
EXPECT_EQ(5u,
GetRegistrationUserDataByKeyPrefix(sw_id, kUserDataPrefix).size());
// Cleanup should delete the registration.
background_fetch_data_manager_->Cleanup();
thread_bundle_.RunUntilIdle();
EXPECT_EQ(0u,
GetRegistrationUserDataByKeyPrefix(sw_id, kUserDataPrefix).size());
RestartDataManagerFromPersistentStorage();
// The deletion should have been persisted.
EXPECT_EQ(0u,
GetRegistrationUserDataByKeyPrefix(sw_id, kUserDataPrefix).size());
}
TEST_F(BackgroundFetchDataManagerTest, GetInitializationData) {
{
// No registered ServiceWorkers.
std::vector<BackgroundFetchInitializationData> data =
GetInitializationData();
EXPECT_TRUE(data.empty());
}
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
{
// Register ServiceWorker with no Background Fetch Registrations.
std::vector<BackgroundFetchInitializationData> data =
GetInitializationData();
EXPECT_TRUE(data.empty());
}
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
auto options = blink::mojom::BackgroundFetchOptions::New();
options->title = kInitialTitle;
options->download_total = 42u;
blink::mojom::BackgroundFetchError error;
// Register a Background Fetch.
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id, _, _, _, _, _));
CreateRegistration(registration_id, CloneRequestVector(requests),
options.Clone(), CreateTestIcon(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
{
std::vector<BackgroundFetchInitializationData> data =
GetInitializationData();
ASSERT_EQ(data.size(), 1u);
const BackgroundFetchInitializationData& init = data[0];
EXPECT_EQ(init.registration_id, registration_id);
EXPECT_EQ(init.registration->unique_id, kExampleUniqueId);
EXPECT_EQ(init.registration->developer_id, kExampleDeveloperId);
EXPECT_EQ(init.options->title, kInitialTitle);
EXPECT_EQ(init.options->download_total, 42u);
EXPECT_EQ(init.ui_title, kInitialTitle);
EXPECT_EQ(init.num_requests, requests.size());
EXPECT_EQ(init.num_completed_requests, 0u);
EXPECT_TRUE(init.active_fetch_requests.empty());
// Check icon.
ASSERT_FALSE(init.icon.drawsNothing());
EXPECT_NO_FATAL_FAILURE(ExpectIconProperties(init.icon, 42, SK_ColorGREEN));
}
// Mark one request as complete and start another.
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
{
std::vector<BackgroundFetchInitializationData> data =
GetInitializationData();
ASSERT_EQ(data.size(), 1u);
EXPECT_EQ(data[0].num_requests, requests.size());
EXPECT_EQ(data[0].num_completed_requests, 1u);
ASSERT_EQ(data[0].active_fetch_requests.size(), 1u);
const auto& init_request_info = data[0].active_fetch_requests[0];
ASSERT_TRUE(init_request_info);
EXPECT_EQ(request_info->download_guid(),
init_request_info->download_guid());
EXPECT_EQ(request_info->request_index(),
init_request_info->request_index());
EXPECT_EQ(ServiceWorkerUtils::SerializeFetchRequestToString(
*(request_info->fetch_request())),
ServiceWorkerUtils::SerializeFetchRequestToString(
*(init_request_info->fetch_request())));
}
// Create another registration.
BackgroundFetchRegistrationId registration_id2(
sw_id, origin(), kAlternativeDeveloperId, kAlternativeUniqueId);
{
EXPECT_CALL(*this, OnRegistrationCreated(registration_id2, _, _, _, _, _));
CreateRegistration(registration_id2, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
{
std::vector<BackgroundFetchInitializationData> data =
GetInitializationData();
ASSERT_EQ(data.size(), 2u);
}
}
TEST_F(BackgroundFetchDataManagerTest, CreateInParallel) {
// Tests that multiple parallel calls to the BackgroundFetchDataManager are
// linearized and handled one at a time, rather than producing inconsistent
// results due to interleaving.
int64_t service_worker_registration_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId,
service_worker_registration_id);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin());
auto options = blink::mojom::BackgroundFetchOptions::New();
std::vector<blink::mojom::BackgroundFetchError> errors(5);
// We expect a single successful registration to be created.
EXPECT_CALL(*this, OnRegistrationCreated(_, _, _, _, _, _));
const int num_parallel_creates = 5;
base::RunLoop run_loop;
base::RepeatingClosure quit_once_all_finished_closure =
base::BarrierClosure(num_parallel_creates, run_loop.QuitClosure());
for (int i = 0; i < num_parallel_creates; i++) {
// New |unique_id| per iteration, since each is a distinct registration.
BackgroundFetchRegistrationId registration_id(
service_worker_registration_id, origin(), kExampleDeveloperId,
base::GenerateGUID());
background_fetch_data_manager_->CreateRegistration(
registration_id, std::move(requests), options.Clone(), SkBitmap(),
/* start_paused = */ false,
base::BindOnce(&DidCreateRegistration, quit_once_all_finished_closure,
&errors[i]));
}
run_loop.Run();
int success_count = 0;
int duplicated_developer_id_count = 0;
for (auto error : errors) {
switch (error) {
case blink::mojom::BackgroundFetchError::NONE:
success_count++;
break;
case blink::mojom::BackgroundFetchError::DUPLICATED_DEVELOPER_ID:
duplicated_developer_id_count++;
break;
default:
break;
}
}
// Exactly one of the calls should have succeeded in creating a registration,
// and all the others should have failed with DUPLICATED_DEVELOPER_ID.
EXPECT_EQ(1, success_count);
EXPECT_EQ(num_parallel_creates - 1, duplicated_developer_id_count);
}
TEST_F(BackgroundFetchDataManagerTest, StorageErrorsReported) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
auto requests = CreateValidRequests(origin(), /* num_requests= */ 3u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
{
base::HistogramTester histogram_tester;
CreateRegistration(registration_id, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
histogram_tester.ExpectBucketCount(
"BackgroundFetch.Storage.CreateMetadataTask", 0 /* kNone */, 1);
}
BackgroundFetchRegistrationId registration_id2(
sw_id, url::Origin::Create(GURL("https://examplebad.com")),
kAlternativeDeveloperId, kAlternativeUniqueId);
{
base::HistogramTester histogram_tester;
// This should fail because the Service Worker doesn't exist.
CreateRegistration(registration_id2, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::STORAGE_ERROR);
histogram_tester.ExpectBucketCount(
"BackgroundFetch.Storage.CreateMetadataTask",
1 /* kServiceWorkerStorageError */, 1);
}
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_TRUE(request_info);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* success= */ true);
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
{
MatchRequests(registration_id, /* request_to_match= */ nullptr,
/* cache_query_options= */ nullptr, /* match_all= */ false,
&error, &settled_fetches);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Delete all entries to get a CachStorageError.
for (const auto& request : requests) {
DeleteFromCache(request);
ASSERT_FALSE(MatchCache(request));
}
{
base::HistogramTester histogram_tester;
MatchRequests(registration_id, /* request_to_match= */ nullptr,
/* cache_query_options= */ nullptr, /* match_all= */ true,
&error, &settled_fetches);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::STORAGE_ERROR);
histogram_tester.ExpectBucketCount(
"BackgroundFetch.Storage.MatchRequestsTask", 2 /* kCacheStorageError */,
1);
}
}
TEST_F(BackgroundFetchDataManagerTest, NotifyObserversOnRequestCompletion) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id1(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 1u);
auto options = blink::mojom::BackgroundFetchOptions::New();
blink::mojom::BackgroundFetchError error;
blink::mojom::BackgroundFetchFailureReason failure_reason;
// Complete the fetch successfully, and expect an OnRequestCompleted() call.
{
CreateRegistration(registration_id1, CloneRequestVector(requests),
options.Clone(), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id1, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* succeeded= */ true);
EXPECT_CALL(*this, OnRequestCompleted(kExampleUniqueId, _, _));
MarkRequestAsComplete(registration_id1, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Mark the Registration for deletion.
MarkRegistrationForDeletion(registration_id1, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
EXPECT_EQ(failure_reason, blink::mojom::BackgroundFetchFailureReason::NONE);
}
BackgroundFetchRegistrationId registration_id2(
sw_id, origin(), kAlternativeDeveloperId, kAlternativeUniqueId);
// Complete the fetch with a BAD_STATUS, and expect an OnRequestCompleted()
// call.
{
CreateRegistration(registration_id2, CloneRequestVector(requests),
std::move(options), SkBitmap(), &error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id2, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get());
EXPECT_CALL(*this, OnRequestCompleted(kAlternativeUniqueId, _, _));
MarkRequestAsComplete(registration_id2, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// Mark the Registration for deletion.
MarkRegistrationForDeletion(registration_id2, /* check_for_failure= */ true,
&error, &failure_reason);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
// The fetch resulted in a 404.
EXPECT_EQ(failure_reason,
blink::mojom::BackgroundFetchFailureReason::BAD_STATUS);
}
}
TEST_F(BackgroundFetchDataManagerTest, CacheUrlMigration) {
int64_t sw_id = RegisterServiceWorker();
ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
BackgroundFetchRegistrationId registration_id(
sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
std::vector<blink::mojom::FetchAPIRequestPtr> requests =
CreateValidRequests(origin(), 2u);
// Create a Registration and complete one of the entries.
{
blink::mojom::BackgroundFetchError error;
CreateRegistration(registration_id, CloneRequestVector(requests),
blink::mojom::BackgroundFetchOptions::New(), SkBitmap(),
&error);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
scoped_refptr<BackgroundFetchRequestInfo> request_info;
PopNextRequest(registration_id, &error, &request_info);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
/* succeeded= */ true);
EXPECT_CALL(*this, OnRequestCompleted(kExampleUniqueId, _, _));
MarkRequestAsComplete(registration_id, request_info.get(), &error);
EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
}
// Delete the entries from Cache and rewrite them in the old format.
DeleteFromCache(requests[0]);
DeleteFromCache(requests[1]);
PutInCache(requests[0], blink::mojom::FetchAPIResponse::New());
auto response = blink::mojom::FetchAPIResponse::New();
response->blob = BuildBlob("data!");
response->url_list = {requests[1]->url};
PutInCache(requests[1], std::move(response));
// Rewrite the storage version.
StoreUserData(sw_id, background_fetch::StorageVersionKey(kExampleUniqueId),
base::NumberToString(proto::SV_UNIQUE_CACHE_KEYS));
// Run the DB migration task.
std::vector<BackgroundFetchInitializationData> data = GetInitializationData();
ASSERT_EQ(data.size(), 1u);
// Check that the storage version was updated.
auto storage_version = GetRegistrationUserDataByKeyPrefix(
sw_id, background_fetch::StorageVersionKey(kExampleUniqueId));
ASSERT_EQ(storage_version.size(), 1u);
EXPECT_EQ(storage_version[0], base::NumberToString(proto::SV_CURRENT));
// Check that the data in the cache makes sense.
std::vector<blink::mojom::BackgroundFetchSettledFetchPtr> settled_fetches;
blink::mojom::BackgroundFetchError error;
MatchRequests(registration_id, /* request_to_match= */ nullptr,
/* cache_query_options= */ nullptr, /* match_all= */ true,
&error, &settled_fetches);
ASSERT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
ASSERT_EQ(settled_fetches.size(), requests.size());
EXPECT_EQ(settled_fetches[0]->request->url, requests[0]->url);
EXPECT_EQ(settled_fetches[1]->request->url, requests[1]->url);
ASSERT_TRUE(settled_fetches[1]->response);
blink::mojom::BlobPtr blob(
std::move(settled_fetches[1]->response->blob->blob));
EXPECT_EQ(CopyBody(blob.get()), "data!");
}
} // namespace content