blob: 12022bd8072cba5f567533de9cc3fba27a796d31 [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_event_dispatcher.h"
#include <map>
#include <sstream>
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/stringprintf.h"
#include "content/browser/background_fetch/background_fetch_registration_id.h"
#include "content/browser/background_fetch/background_fetch_registration_service_impl.h"
#include "content/browser/devtools/devtools_background_services_context_impl.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/common/background_fetch/background_fetch_types.h"
#include "content/public/browser/browser_thread.h"
namespace content {
namespace {
// Returns the histogram suffix for the given |event| type.
std::string HistogramSuffixForEventType(ServiceWorkerMetrics::EventType event) {
switch (event) {
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_ABORT:
return "AbortEvent";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_CLICK:
return "ClickEvent";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_FAIL:
return "FailEvent";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_SUCCESS:
return "SuccessEvent";
default:
NOTREACHED();
return std::string();
}
}
// Returns a human-readable string for the given |event| type.
std::string EventTypeToString(ServiceWorkerMetrics::EventType event) {
switch (event) {
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_ABORT:
return "BackgroundFetchAbortEvent";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_CLICK:
return "BackgroundFetchClickEvent";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_FAIL:
return "BackgroundFetchFailEvent";
case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_SUCCESS:
return "BackgroundFetchSuccessEvent";
default:
NOTREACHED();
return std::string();
}
}
// Records the result of a dispatched Background Fetch event.
void RecordDispatchResult(
ServiceWorkerMetrics::EventType event,
BackgroundFetchEventDispatcher::DispatchResult result) {
std::string histogram_name = "BackgroundFetch.EventDispatchResult." +
HistogramSuffixForEventType(event);
// Used because the |histogram_name| is not a constant.
base::UmaHistogramEnumeration(
histogram_name, result,
BackgroundFetchEventDispatcher::DISPATCH_RESULT_COUNT);
}
// Records the failure reason of a failed dispatch for |metric_name|.
void RecordFailureResult(ServiceWorkerMetrics::EventType event,
const char* metric_name,
blink::ServiceWorkerStatusCode service_worker_status) {
std::string event_type = HistogramSuffixForEventType(event);
std::string histogram_name =
base::StringPrintf("BackgroundFetch.EventDispatchFailure.%s.%s",
metric_name, event_type.c_str());
// Used because the |histogram_name| is not a constant.
base::UmaHistogramEnumeration(histogram_name, service_worker_status);
}
} // namespace
BackgroundFetchEventDispatcher::BackgroundFetchEventDispatcher(
BackgroundFetchContext* background_fetch_context,
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
DevToolsBackgroundServicesContextImpl* devtools_context)
: background_fetch_context_(background_fetch_context),
service_worker_context_(std::move(service_worker_context)),
devtools_context_(devtools_context) {
// Constructed on the UI thread, then lives on the IO thread.
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(background_fetch_context_);
DCHECK(devtools_context_);
}
BackgroundFetchEventDispatcher::~BackgroundFetchEventDispatcher() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
void BackgroundFetchEventDispatcher::DispatchBackgroundFetchCompletionEvent(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchRegistrationDataPtr registration_data,
base::OnceClosure finished_closure) {
DCHECK(registration_data);
auto registration = blink::mojom::BackgroundFetchRegistration::New(
std::move(registration_data),
BackgroundFetchRegistrationServiceImpl::CreateInterfaceInfo(
std::move(registration_id), background_fetch_context_));
switch (registration->registration_data->failure_reason) {
case blink::mojom::BackgroundFetchFailureReason::NONE:
DCHECK_EQ(registration->registration_data->result,
blink::mojom::BackgroundFetchResult::SUCCESS);
DispatchBackgroundFetchSuccessEvent(registration_id,
std::move(registration),
std::move(finished_closure));
return;
case blink::mojom::BackgroundFetchFailureReason::CANCELLED_FROM_UI:
case blink::mojom::BackgroundFetchFailureReason::CANCELLED_BY_DEVELOPER:
DCHECK_EQ(registration->registration_data->result,
blink::mojom::BackgroundFetchResult::FAILURE);
DispatchBackgroundFetchAbortEvent(registration_id,
std::move(registration),
std::move(finished_closure));
return;
case blink::mojom::BackgroundFetchFailureReason::BAD_STATUS:
case blink::mojom::BackgroundFetchFailureReason::FETCH_ERROR:
case blink::mojom::BackgroundFetchFailureReason::SERVICE_WORKER_UNAVAILABLE:
case blink::mojom::BackgroundFetchFailureReason::QUOTA_EXCEEDED:
case blink::mojom::BackgroundFetchFailureReason::DOWNLOAD_TOTAL_EXCEEDED:
DCHECK_EQ(registration->registration_data->result,
blink::mojom::BackgroundFetchResult::FAILURE);
DispatchBackgroundFetchFailEvent(registration_id, std::move(registration),
std::move(finished_closure));
return;
}
NOTREACHED();
}
void BackgroundFetchEventDispatcher::DispatchBackgroundFetchAbortEvent(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchRegistrationPtr registration,
base::OnceClosure finished_closure) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
LogBackgroundFetchCompletionForDevTools(
registration_id, ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_ABORT,
registration->registration_data->failure_reason);
LoadServiceWorkerRegistrationForDispatch(
registration_id, ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_ABORT,
std::move(finished_closure),
base::AdaptCallbackForRepeating(base::BindOnce(
&BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchAbortEvent,
std::move(registration))));
}
void BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchAbortEvent(
blink::mojom::BackgroundFetchRegistrationPtr registration,
scoped_refptr<ServiceWorkerVersion> service_worker_version,
int request_id) {
DCHECK(service_worker_version);
DCHECK(registration);
service_worker_version->endpoint()->DispatchBackgroundFetchAbortEvent(
std::move(registration),
service_worker_version->CreateSimpleEventCallback(request_id));
}
void BackgroundFetchEventDispatcher::DispatchBackgroundFetchClickEvent(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchRegistrationDataPtr registration_data,
base::OnceClosure finished_closure) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(registration_data);
auto registration = blink::mojom::BackgroundFetchRegistration::New(
std::move(registration_data),
BackgroundFetchRegistrationServiceImpl::CreateInterfaceInfo(
std::move(registration_id), background_fetch_context_));
LoadServiceWorkerRegistrationForDispatch(
registration_id, ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_CLICK,
std::move(finished_closure),
base::AdaptCallbackForRepeating(base::BindOnce(
&BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchClickEvent,
std::move(registration))));
}
void BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchClickEvent(
blink::mojom::BackgroundFetchRegistrationPtr registration,
scoped_refptr<ServiceWorkerVersion> service_worker_version,
int request_id) {
DCHECK(service_worker_version);
DCHECK(registration);
service_worker_version->endpoint()->DispatchBackgroundFetchClickEvent(
std::move(registration),
service_worker_version->CreateSimpleEventCallback(request_id));
}
void BackgroundFetchEventDispatcher::DispatchBackgroundFetchFailEvent(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchRegistrationPtr registration,
base::OnceClosure finished_closure) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
LogBackgroundFetchCompletionForDevTools(
registration_id, ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_FAIL,
registration->registration_data->failure_reason);
LoadServiceWorkerRegistrationForDispatch(
registration_id, ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_FAIL,
std::move(finished_closure),
base::AdaptCallbackForRepeating(base::BindOnce(
&BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchFailEvent,
std::move(registration))));
}
void BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchFailEvent(
blink::mojom::BackgroundFetchRegistrationPtr registration,
scoped_refptr<ServiceWorkerVersion> service_worker_version,
int request_id) {
DCHECK(service_worker_version);
DCHECK(registration);
service_worker_version->endpoint()->DispatchBackgroundFetchFailEvent(
std::move(registration),
service_worker_version->CreateSimpleEventCallback(request_id));
}
void BackgroundFetchEventDispatcher::DispatchBackgroundFetchSuccessEvent(
const BackgroundFetchRegistrationId& registration_id,
blink::mojom::BackgroundFetchRegistrationPtr registration,
base::OnceClosure finished_closure) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
LogBackgroundFetchCompletionForDevTools(
registration_id,
ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_SUCCESS,
registration->registration_data->failure_reason);
LoadServiceWorkerRegistrationForDispatch(
registration_id,
ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_SUCCESS,
std::move(finished_closure),
base::AdaptCallbackForRepeating(
base::BindOnce(&BackgroundFetchEventDispatcher::
DoDispatchBackgroundFetchSuccessEvent,
std::move(registration))));
}
void BackgroundFetchEventDispatcher::DoDispatchBackgroundFetchSuccessEvent(
blink::mojom::BackgroundFetchRegistrationPtr registration,
scoped_refptr<ServiceWorkerVersion> service_worker_version,
int request_id) {
DCHECK(service_worker_version);
DCHECK(registration);
service_worker_version->endpoint()->DispatchBackgroundFetchSuccessEvent(
std::move(registration),
service_worker_version->CreateSimpleEventCallback(request_id));
}
void BackgroundFetchEventDispatcher::LoadServiceWorkerRegistrationForDispatch(
const BackgroundFetchRegistrationId& registration_id,
ServiceWorkerMetrics::EventType event,
base::OnceClosure finished_closure,
ServiceWorkerLoadedCallback loaded_callback) {
service_worker_context_->FindReadyRegistrationForId(
registration_id.service_worker_registration_id(),
registration_id.origin().GetURL(),
base::BindOnce(
&BackgroundFetchEventDispatcher::StartActiveWorkerForDispatch, event,
std::move(finished_closure), std::move(loaded_callback)));
}
void BackgroundFetchEventDispatcher::StartActiveWorkerForDispatch(
ServiceWorkerMetrics::EventType event,
base::OnceClosure finished_closure,
ServiceWorkerLoadedCallback loaded_callback,
blink::ServiceWorkerStatusCode service_worker_status,
scoped_refptr<ServiceWorkerRegistration> registration) {
if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
DidDispatchEvent(event, std::move(finished_closure), DispatchPhase::FINDING,
service_worker_status);
return;
}
ServiceWorkerVersion* service_worker_version = registration->active_version();
DCHECK(service_worker_version);
service_worker_version->RunAfterStartWorker(
event,
base::BindOnce(&BackgroundFetchEventDispatcher::DispatchEvent, event,
std::move(finished_closure), std::move(loaded_callback),
base::WrapRefCounted(service_worker_version)));
}
void BackgroundFetchEventDispatcher::DispatchEvent(
ServiceWorkerMetrics::EventType event,
base::OnceClosure finished_closure,
ServiceWorkerLoadedCallback loaded_callback,
scoped_refptr<ServiceWorkerVersion> service_worker_version,
blink::ServiceWorkerStatusCode start_worker_status) {
if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
DidDispatchEvent(event, std::move(finished_closure),
DispatchPhase::STARTING, start_worker_status);
return;
}
int request_id = service_worker_version->StartRequest(
event,
base::BindOnce(&BackgroundFetchEventDispatcher::DidDispatchEvent, event,
std::move(finished_closure), DispatchPhase::DISPATCHING));
std::move(loaded_callback).Run(std::move(service_worker_version), request_id);
}
void BackgroundFetchEventDispatcher::DidDispatchEvent(
ServiceWorkerMetrics::EventType event,
base::OnceClosure finished_closure,
DispatchPhase dispatch_phase,
blink::ServiceWorkerStatusCode service_worker_status) {
// Record the histograms tracking event dispatching success.
switch (dispatch_phase) {
case DispatchPhase::FINDING:
RecordDispatchResult(event, DISPATCH_RESULT_CANNOT_FIND_WORKER);
RecordFailureResult(event, "FindWorker", service_worker_status);
break;
case DispatchPhase::STARTING:
RecordDispatchResult(event, DISPATCH_RESULT_CANNOT_START_WORKER);
RecordFailureResult(event, "StartWorker", service_worker_status);
break;
case DispatchPhase::DISPATCHING:
if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
RecordDispatchResult(event, DISPATCH_RESULT_CANNOT_DISPATCH_EVENT);
RecordFailureResult(event, "Dispatch", service_worker_status);
} else {
RecordDispatchResult(event, DISPATCH_RESULT_SUCCESS);
}
break;
}
std::move(finished_closure).Run();
}
void BackgroundFetchEventDispatcher::LogBackgroundFetchCompletionForDevTools(
const BackgroundFetchRegistrationId& registration_id,
ServiceWorkerMetrics::EventType event_type,
blink::mojom::BackgroundFetchFailureReason failure_reason) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!devtools_context_->IsRecording(
DevToolsBackgroundService::kBackgroundFetch)) {
return;
}
std::map<std::string, std::string> metadata = {
{"Event Type", EventTypeToString(event_type)}};
if (failure_reason != blink::mojom::BackgroundFetchFailureReason::NONE) {
std::stringstream stream;
stream << failure_reason;
metadata["Failure Reason"] = stream.str();
}
devtools_context_->LogBackgroundServiceEventOnIO(
registration_id.service_worker_registration_id(),
registration_id.origin(), DevToolsBackgroundService::kBackgroundFetch,
/* event_name= */ "Background Fetch completed",
/* instance_id= */ registration_id.developer_id(), metadata);
}
} // namespace content