blob: 2013efcb1926773311307bab8989dd4b4fa12294 [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_context.h"
#include <utility>
#include "base/bind_helpers.h"
#include "base/memory/ptr_util.h"
#include "content/browser/background_fetch/background_fetch_job_controller.h"
#include "content/browser/background_fetch/background_fetch_registration_id.h"
#include "content/browser/background_fetch/background_fetch_registration_notifier.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/public/browser/background_fetch_delegate.h"
#include "content/public/browser/blob_handle.h"
#include "content/public/browser/browser_context.h"
#include "net/url_request/url_request_context_getter.h"
namespace content {
namespace {
void IgnoreError(blink::mojom::BackgroundFetchError) {
// TODO(johnme): Log errors to UMA.
}
// Records the |error| status issued by the DataManager after it was requested
// to create and store a new Background Fetch registration.
void RecordRegistrationCreatedError(blink::mojom::BackgroundFetchError error) {
// TODO(peter): Add UMA.
}
// Records the |error| status issued by the DataManager after the storage
// associated with a registration has been completely deleted.
void RecordRegistrationDeletedError(blink::mojom::BackgroundFetchError error) {
// TODO(peter): Add UMA.
}
} // namespace
BackgroundFetchContext::BackgroundFetchContext(
BrowserContext* browser_context,
const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
: browser_context_(browser_context),
data_manager_(browser_context, service_worker_context),
event_dispatcher_(service_worker_context),
registration_notifier_(
std::make_unique<BackgroundFetchRegistrationNotifier>()),
delegate_proxy_(browser_context_->GetBackgroundFetchDelegate()),
weak_factory_(this) {
// Although this lives only on the IO thread, it is constructed on UI thread.
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
BackgroundFetchContext::~BackgroundFetchContext() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
void BackgroundFetchContext::StartFetch(
const BackgroundFetchRegistrationId& registration_id,
const std::vector<ServiceWorkerFetchRequest>& requests,
const BackgroundFetchOptions& options,
blink::mojom::BackgroundFetchService::FetchCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
data_manager_.CreateRegistration(
registration_id, requests, options,
base::BindOnce(&BackgroundFetchContext::DidCreateRegistration,
weak_factory_.GetWeakPtr(), registration_id, options,
std::move(callback)));
}
void BackgroundFetchContext::DidCreateRegistration(
const BackgroundFetchRegistrationId& registration_id,
const BackgroundFetchOptions& options,
blink::mojom::BackgroundFetchService::FetchCallback callback,
blink::mojom::BackgroundFetchError error,
const base::Optional<BackgroundFetchRegistration>& registration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
RecordRegistrationCreatedError(error);
if (error == blink::mojom::BackgroundFetchError::NONE) {
DCHECK(registration);
// Create the BackgroundFetchJobController to do the actual fetching.
CreateController(registration_id, options, registration.value());
}
std::move(callback).Run(error, registration);
}
void BackgroundFetchContext::AddRegistrationObserver(
const std::string& unique_id,
blink::mojom::BackgroundFetchRegistrationObserverPtr observer) {
registration_notifier_->AddObserver(unique_id, std::move(observer));
}
void BackgroundFetchContext::CreateController(
const BackgroundFetchRegistrationId& registration_id,
const BackgroundFetchOptions& options,
const BackgroundFetchRegistration& registration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
auto controller = std::make_unique<BackgroundFetchJobController>(
&delegate_proxy_, registration_id, options, registration, &data_manager_,
// Safe because JobControllers are destroyed before RegistrationNotifier.
base::BindRepeating(&BackgroundFetchRegistrationNotifier::Notify,
base::Unretained(registration_notifier_.get())),
base::BindOnce(&BackgroundFetchContext::DidFinishJob,
weak_factory_.GetWeakPtr(), base::Bind(&IgnoreError)));
// Start fetching the first few requests immediately. At some point in the
// future we may want a more elaborate scheduling mechanism here.
controller->Start();
job_controllers_.insert(
std::make_pair(registration_id.unique_id(), std::move(controller)));
}
void BackgroundFetchContext::Abort(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchService::AbortCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DidFinishJob(std::move(callback), registration_id, true /* aborted */);
}
void BackgroundFetchContext::DidFinishJob(
base::OnceCallback<void(blink::mojom::BackgroundFetchError)> callback,
const BackgroundFetchRegistrationId& registration_id,
bool aborted) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// If |aborted| is true, this will also propagate the event to any active
// JobController for the registration, to terminate in-progress requests.
data_manager_.MarkRegistrationForDeletion(
registration_id, aborted,
base::BindOnce(&BackgroundFetchContext::DidMarkForDeletion,
weak_factory_.GetWeakPtr(), registration_id, aborted,
std::move(callback)));
}
void BackgroundFetchContext::DidMarkForDeletion(
const BackgroundFetchRegistrationId& registration_id,
bool aborted,
base::OnceCallback<void(blink::mojom::BackgroundFetchError)> callback,
blink::mojom::BackgroundFetchError error) {
std::move(callback).Run(error);
// It's normal to get INVALID_ID errors here - it means the registration was
// already inactive (marked for deletion). This happens when an abort (from
// developer or from user) races with the download completing/failing, or even
// when two aborts race. TODO(johnme): Log STORAGE_ERRORs to UMA though.
if (error != blink::mojom::BackgroundFetchError::NONE)
return;
if (aborted) {
CleanupRegistration(registration_id, {});
event_dispatcher_.DispatchBackgroundFetchAbortEvent(
registration_id, base::Bind(&base::DoNothing));
} else {
data_manager_.GetSettledFetchesForRegistration(
registration_id,
base::BindOnce(&BackgroundFetchContext::DidGetSettledFetches,
weak_factory_.GetWeakPtr(), registration_id));
}
}
void BackgroundFetchContext::DidGetSettledFetches(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchError error,
bool background_fetch_succeeded,
std::vector<BackgroundFetchSettledFetch> settled_fetches,
std::vector<std::unique_ptr<BlobHandle>> blob_handles) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (error != blink::mojom::BackgroundFetchError::NONE) {
CleanupRegistration(registration_id, {});
return;
}
// The `backgroundfetched` event will be invoked when all requests in the
// registration have completed successfully. In all other cases, the
// `backgroundfetchfail` event will be invoked instead.
if (background_fetch_succeeded) {
event_dispatcher_.DispatchBackgroundFetchedEvent(
registration_id, std::move(settled_fetches),
base::Bind(&BackgroundFetchContext::CleanupRegistration,
weak_factory_.GetWeakPtr(), registration_id,
// The blob uuid is sent as part of |settled_fetches|. Bind
// |blob_handles| to the callback to keep them alive until
// the waitUntil event is resolved.
std::move(blob_handles)));
} else {
event_dispatcher_.DispatchBackgroundFetchFailEvent(
registration_id, std::move(settled_fetches),
base::Bind(&BackgroundFetchContext::CleanupRegistration,
weak_factory_.GetWeakPtr(), registration_id,
// The blob uuid is sent as part of |settled_fetches|. Bind
// |blob_handles| to the callback to keep them alive until
// the waitUntil event is resolved.
std::move(blob_handles)));
}
}
void BackgroundFetchContext::CleanupRegistration(
const BackgroundFetchRegistrationId& registration_id,
const std::vector<std::unique_ptr<BlobHandle>>& blob_handles) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// If we had an active JobController, it is no longer necessary, as the
// notification's UI can no longer be updated after the fetch is aborted, or
// after the waitUntil promise of the backgroundfetched/backgroundfetchfail
// event has been resolved.
job_controllers_.erase(registration_id.unique_id());
// At this point, JavaScript can no longer obtain BackgroundFetchRegistration
// objects for this registration, and those objects are the only thing that
// requires us to keep the registration's data around. So once the
// RegistrationNotifier informs us that all existing observers (and hence
// BackgroundFetchRegistration objects) have been garbage collected, it'll be
// safe to delete the registration. This callback doesn't run if the browser
// is shutdown before that happens - BackgroundFetchDataManager::Cleanup acts
// as a fallback in that case, and deletes the registration on next startup.
registration_notifier_->AddGarbageCollectionCallback(
registration_id.unique_id(),
base::Bind(&BackgroundFetchContext::LastObserverGarbageCollected,
weak_factory_.GetWeakPtr(), registration_id));
}
void BackgroundFetchContext::LastObserverGarbageCollected(
const BackgroundFetchRegistrationId& registration_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
data_manager_.DeleteRegistration(
registration_id, base::BindOnce(&RecordRegistrationDeletedError));
}
} // namespace content