blob: be41aa95de6203d05814549934aa47209b443f86 [file] [log] [blame]
// Copyright 2014 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/service_worker/service_worker_fetch_dispatcher.h"
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/devtools/service_worker_devtools_agent_host.h"
#include "content/browser/devtools/service_worker_devtools_manager.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/loader/resource_request_info_impl.h"
#include "content/browser/loader/resource_requester_info.h"
#include "content/browser/loader/url_loader_factory_impl.h"
#include "content/browser/service_worker/embedded_worker_status.h"
#include "content/browser/service_worker/service_worker_metrics.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
#include "content/common/service_worker/service_worker_messages.h"
#include "content/common/service_worker/service_worker_status_code.h"
#include "content/common/service_worker/service_worker_types.h"
#include "content/common/service_worker/service_worker_utils.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/browser_side_navigation_policy.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "net/base/request_priority.h"
#include "net/http/http_util.h"
#include "net/log/net_log.h"
#include "net/log/net_log_capture_mode.h"
#include "net/log/net_log_event_type.h"
#include "net/url_request/url_request.h"
namespace content {
namespace {
// This class wraps a mojo::AssociatedInterfacePtr<URLLoader>. It also is a
// URLLoader implementation and delegates URLLoader calls to the wrapped loader.
class DelegatingURLLoader final : public mojom::URLLoader {
public:
explicit DelegatingURLLoader(mojom::URLLoaderAssociatedPtr loader)
: binding_(this), loader_(std::move(loader)) {}
~DelegatingURLLoader() override {}
void FollowRedirect() override { loader_->FollowRedirect(); }
void SetPriority(net::RequestPriority priority,
int intra_priority_value) override {
loader_->SetPriority(priority, intra_priority_value);
}
mojom::URLLoaderPtr CreateInterfacePtrAndBind() {
auto p = binding_.CreateInterfacePtrAndBind();
// This unretained pointer is safe, because |binding_| is owned by |this|
// and the callback will never be called after |this| is destroyed.
binding_.set_connection_error_handler(
base::Bind(&DelegatingURLLoader::Cancel, base::Unretained(this)));
return p;
}
private:
// Called when the mojom::URLLoaderPtr in the service worker is deleted.
void Cancel() {
// Cancel loading as stated in url_loader.mojom.
loader_ = nullptr;
}
mojo::Binding<mojom::URLLoader> binding_;
mojom::URLLoaderAssociatedPtr loader_;
DISALLOW_COPY_AND_ASSIGN(DelegatingURLLoader);
};
ServiceWorkerDevToolsAgentHost* GetAgentHost(
const std::pair<int, int>& worker_id) {
return ServiceWorkerDevToolsManager::GetInstance()
->GetDevToolsAgentHostForWorker(worker_id.first, worker_id.second);
}
void NotifyNavigationPreloadRequestSentOnUI(
const ResourceRequest& request,
const std::pair<int, int>& worker_id,
const std::string& request_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (ServiceWorkerDevToolsAgentHost* agent_host = GetAgentHost(worker_id))
agent_host->NavigationPreloadRequestSent(request_id, request);
}
void NotifyNavigationPreloadResponseReceivedOnUI(
const GURL& url,
const ResourceResponseHead& head,
const std::pair<int, int>& worker_id,
const std::string& request_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (ServiceWorkerDevToolsAgentHost* agent_host = GetAgentHost(worker_id))
agent_host->NavigationPreloadResponseReceived(request_id, url, head);
}
void NotifyNavigationPreloadCompletedOnUI(
const ResourceRequestCompletionStatus& completion_status,
const std::pair<int, int>& worker_id,
const std::string& request_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (ServiceWorkerDevToolsAgentHost* agent_host = GetAgentHost(worker_id))
agent_host->NavigationPreloadCompleted(request_id, completion_status);
}
// This class wraps a mojo::InterfacePtr<URLLoaderClient>. It also is a
// URLLoaderClient implementation and delegates URLLoaderClient calls to the
// wrapped client.
class DelegatingURLLoaderClient final : public mojom::URLLoaderClient {
public:
using WorkerId = std::pair<int, int>;
explicit DelegatingURLLoaderClient(mojom::URLLoaderClientPtr client,
base::OnceClosure on_response,
const ResourceRequest& request)
: binding_(this),
client_(std::move(client)),
on_response_(std::move(on_response)),
url_(request.url) {
AddDevToolsCallback(
base::Bind(&NotifyNavigationPreloadRequestSentOnUI, request));
}
~DelegatingURLLoaderClient() override {
if (!completed_) {
// Let the service worker know that the request has been canceled.
ResourceRequestCompletionStatus status;
status.error_code = net::ERR_ABORTED;
client_->OnComplete(status);
}
}
void MayBeReportToDevTools(WorkerId worker_id, int fetch_event_id) {
worker_id_ = worker_id;
devtools_request_id_ = base::StringPrintf("preload-%d", fetch_event_id);
MayBeRunDevToolsCallbacks();
}
void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override {
client_->OnDataDownloaded(data_length, encoded_length);
}
void OnUploadProgress(int64_t current_position,
int64_t total_size,
const base::Closure& ack_callback) override {
client_->OnUploadProgress(current_position, total_size, ack_callback);
}
void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override {
client_->OnReceiveCachedMetadata(data);
}
void OnTransferSizeUpdated(int32_t transfer_size_diff) override {
client_->OnTransferSizeUpdated(transfer_size_diff);
}
void OnReceiveResponse(
const ResourceResponseHead& head,
mojom::DownloadedTempFileAssociatedPtrInfo downloaded_file) override {
client_->OnReceiveResponse(head, std::move(downloaded_file));
DCHECK(on_response_);
std::move(on_response_).Run();
AddDevToolsCallback(
base::Bind(&NotifyNavigationPreloadResponseReceivedOnUI, url_, head));
}
void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
const ResourceResponseHead& head) override {
client_->OnReceiveRedirect(redirect_info, head);
}
void OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body) override {
client_->OnStartLoadingResponseBody(std::move(body));
}
void OnComplete(
const ResourceRequestCompletionStatus& completion_status) override {
client_->OnComplete(completion_status);
completed_ = true;
AddDevToolsCallback(
base::Bind(&NotifyNavigationPreloadCompletedOnUI, completion_status));
}
void Bind(mojom::URLLoaderClientAssociatedPtrInfo* ptr_info) {
binding_.Bind(ptr_info);
}
private:
void MayBeRunDevToolsCallbacks() {
if (!worker_id_)
return;
while (!devtools_callbacks.empty()) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(std::move(devtools_callbacks.front()),
*worker_id_, devtools_request_id_));
devtools_callbacks.pop();
}
}
void AddDevToolsCallback(
base::Callback<void(const WorkerId&, const std::string&)> callback) {
devtools_callbacks.push(callback);
MayBeRunDevToolsCallbacks();
}
mojo::AssociatedBinding<mojom::URLLoaderClient> binding_;
mojom::URLLoaderClientPtr client_;
base::OnceClosure on_response_;
bool completed_ = false;
const GURL url_;
base::Optional<std::pair<int, int>> worker_id_;
std::string devtools_request_id_;
std::queue<base::Callback<void(const WorkerId&, const std::string&)>>
devtools_callbacks;
DISALLOW_COPY_AND_ASSIGN(DelegatingURLLoaderClient);
};
using EventType = ServiceWorkerMetrics::EventType;
EventType ResourceTypeToEventType(ResourceType resource_type) {
switch (resource_type) {
case RESOURCE_TYPE_MAIN_FRAME:
return EventType::FETCH_MAIN_FRAME;
case RESOURCE_TYPE_SUB_FRAME:
return EventType::FETCH_SUB_FRAME;
case RESOURCE_TYPE_SHARED_WORKER:
return EventType::FETCH_SHARED_WORKER;
case RESOURCE_TYPE_SERVICE_WORKER:
case RESOURCE_TYPE_LAST_TYPE:
NOTREACHED() << resource_type;
return EventType::FETCH_SUB_RESOURCE;
default:
return EventType::FETCH_SUB_RESOURCE;
}
}
std::unique_ptr<base::Value> NetLogServiceWorkerStatusCallback(
ServiceWorkerStatusCode status,
net::NetLogCaptureMode) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
dict->SetString("status", ServiceWorkerStatusToString(status));
return std::move(dict);
}
std::unique_ptr<base::Value> NetLogFetchEventCallback(
ServiceWorkerStatusCode status,
ServiceWorkerFetchEventResult result,
net::NetLogCaptureMode) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
dict->SetString("status", ServiceWorkerStatusToString(status));
dict->SetBoolean("has_response",
result == SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE);
return std::move(dict);
}
void EndNetLogEventWithServiceWorkerStatus(const net::NetLogWithSource& net_log,
net::NetLogEventType type,
ServiceWorkerStatusCode status) {
net_log.EndEvent(type,
base::Bind(&NetLogServiceWorkerStatusCallback, status));
}
ServiceWorkerMetrics::EventType FetchTypeToWaitUntilEventType(
ServiceWorkerFetchType type) {
if (type == ServiceWorkerFetchType::FOREIGN_FETCH)
return ServiceWorkerMetrics::EventType::FOREIGN_FETCH_WAITUNTIL;
return ServiceWorkerMetrics::EventType::FETCH_WAITUNTIL;
}
} // namespace
// Helper to receive the fetch event response even if
// ServiceWorkerFetchDispatcher has been destroyed.
class ServiceWorkerFetchDispatcher::ResponseCallback {
public:
ResponseCallback(base::WeakPtr<ServiceWorkerFetchDispatcher> fetch_dispatcher,
ServiceWorkerVersion* version)
: fetch_dispatcher_(fetch_dispatcher), version_(version) {}
void Run(int request_id,
ServiceWorkerFetchEventResult fetch_result,
const ServiceWorkerResponse& response,
base::Time dispatch_event_time) {
const bool handled =
(fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE);
if (!version_->FinishRequest(request_id, handled, dispatch_event_time))
NOTREACHED() << "Should only receive one reply per event";
// |fetch_dispatcher| is null if the URLRequest was killed.
if (fetch_dispatcher_)
fetch_dispatcher_->DidFinish(request_id, fetch_result, response);
}
private:
base::WeakPtr<ServiceWorkerFetchDispatcher> fetch_dispatcher_;
// Owns |this|.
ServiceWorkerVersion* version_;
DISALLOW_COPY_AND_ASSIGN(ResponseCallback);
};
// This class keeps the URL loader related assets alive while the FetchEvent is
// ongoing in the service worker.
class ServiceWorkerFetchDispatcher::URLLoaderAssets
: public base::RefCounted<ServiceWorkerFetchDispatcher::URLLoaderAssets> {
public:
URLLoaderAssets(mojom::URLLoaderFactoryPtr url_loader_factory,
std::unique_ptr<mojom::URLLoader> url_loader,
std::unique_ptr<DelegatingURLLoaderClient> url_loader_client)
: url_loader_factory_(std::move(url_loader_factory)),
url_loader_(std::move(url_loader)),
url_loader_client_(std::move(url_loader_client)) {}
void MayBeReportToDevTools(std::pair<int, int> worker_id,
int fetch_event_id) {
url_loader_client_->MayBeReportToDevTools(worker_id, fetch_event_id);
}
private:
friend class base::RefCounted<URLLoaderAssets>;
virtual ~URLLoaderAssets() {}
mojom::URLLoaderFactoryPtr url_loader_factory_;
std::unique_ptr<mojom::URLLoader> url_loader_;
std::unique_ptr<DelegatingURLLoaderClient> url_loader_client_;
DISALLOW_COPY_AND_ASSIGN(URLLoaderAssets);
};
ServiceWorkerFetchDispatcher::ServiceWorkerFetchDispatcher(
std::unique_ptr<ServiceWorkerFetchRequest> request,
ServiceWorkerVersion* version,
ResourceType resource_type,
const base::Optional<base::TimeDelta>& timeout,
const net::NetLogWithSource& net_log,
const base::Closure& prepare_callback,
const FetchCallback& fetch_callback)
: version_(version),
net_log_(net_log),
prepare_callback_(prepare_callback),
fetch_callback_(fetch_callback),
request_(std::move(request)),
resource_type_(resource_type),
timeout_(timeout),
did_complete_(false),
weak_factory_(this) {
net_log_.BeginEvent(net::NetLogEventType::SERVICE_WORKER_DISPATCH_FETCH_EVENT,
net::NetLog::StringCallback(
"event_type", ServiceWorkerMetrics::EventTypeToString(
GetEventType())));
}
ServiceWorkerFetchDispatcher::~ServiceWorkerFetchDispatcher() {
if (!did_complete_)
net_log_.EndEvent(
net::NetLogEventType::SERVICE_WORKER_DISPATCH_FETCH_EVENT);
}
void ServiceWorkerFetchDispatcher::Run() {
DCHECK(version_->status() == ServiceWorkerVersion::ACTIVATING ||
version_->status() == ServiceWorkerVersion::ACTIVATED)
<< version_->status();
if (version_->status() == ServiceWorkerVersion::ACTIVATING) {
net_log_.BeginEvent(
net::NetLogEventType::SERVICE_WORKER_WAIT_FOR_ACTIVATION);
version_->RegisterStatusChangeCallback(
base::Bind(&ServiceWorkerFetchDispatcher::DidWaitForActivation,
weak_factory_.GetWeakPtr()));
return;
}
StartWorker();
}
void ServiceWorkerFetchDispatcher::DidWaitForActivation() {
net_log_.EndEvent(net::NetLogEventType::SERVICE_WORKER_WAIT_FOR_ACTIVATION);
StartWorker();
}
void ServiceWorkerFetchDispatcher::StartWorker() {
// We might be REDUNDANT if a new worker started activating and kicked us out
// before we could finish activation.
if (version_->status() != ServiceWorkerVersion::ACTIVATED) {
DCHECK_EQ(ServiceWorkerVersion::REDUNDANT, version_->status());
DidFail(SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED);
return;
}
if (version_->running_status() == EmbeddedWorkerStatus::RUNNING) {
DispatchFetchEvent();
return;
}
net_log_.BeginEvent(net::NetLogEventType::SERVICE_WORKER_START_WORKER);
version_->RunAfterStartWorker(
GetEventType(), base::Bind(&ServiceWorkerFetchDispatcher::DidStartWorker,
weak_factory_.GetWeakPtr()),
base::Bind(&ServiceWorkerFetchDispatcher::DidFailToStartWorker,
weak_factory_.GetWeakPtr()));
}
void ServiceWorkerFetchDispatcher::DidStartWorker() {
net_log_.EndEvent(net::NetLogEventType::SERVICE_WORKER_START_WORKER);
DispatchFetchEvent();
}
void ServiceWorkerFetchDispatcher::DidFailToStartWorker(
ServiceWorkerStatusCode status) {
EndNetLogEventWithServiceWorkerStatus(
net_log_, net::NetLogEventType::SERVICE_WORKER_START_WORKER, status);
DidFail(status);
}
void ServiceWorkerFetchDispatcher::DispatchFetchEvent() {
DCHECK_EQ(EmbeddedWorkerStatus::RUNNING, version_->running_status())
<< "Worker stopped too soon after it was started.";
DCHECK(!prepare_callback_.is_null());
base::Closure prepare_callback = prepare_callback_;
prepare_callback.Run();
net_log_.BeginEvent(net::NetLogEventType::SERVICE_WORKER_FETCH_EVENT);
int fetch_event_id;
int event_finish_id;
if (timeout_) {
fetch_event_id = version_->StartRequestWithCustomTimeout(
GetEventType(),
base::Bind(&ServiceWorkerFetchDispatcher::DidFailToDispatch,
weak_factory_.GetWeakPtr()),
*timeout_, ServiceWorkerVersion::CONTINUE_ON_TIMEOUT);
event_finish_id = version_->StartRequestWithCustomTimeout(
FetchTypeToWaitUntilEventType(request_->fetch_type),
base::Bind(&ServiceWorkerUtils::NoOpStatusCallback), *timeout_,
ServiceWorkerVersion::CONTINUE_ON_TIMEOUT);
} else {
fetch_event_id = version_->StartRequest(
GetEventType(),
base::Bind(&ServiceWorkerFetchDispatcher::DidFailToDispatch,
weak_factory_.GetWeakPtr()));
event_finish_id = version_->StartRequest(
FetchTypeToWaitUntilEventType(request_->fetch_type),
base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
}
ResponseCallback* response_callback =
new ResponseCallback(weak_factory_.GetWeakPtr(), version_.get());
version_->RegisterRequestCallback<ServiceWorkerHostMsg_FetchEventResponse>(
fetch_event_id,
base::Bind(&ServiceWorkerFetchDispatcher::ResponseCallback::Run,
base::Owned(response_callback)));
if (url_loader_assets_) {
url_loader_assets_->MayBeReportToDevTools(
std::make_pair(
version_->embedded_worker()->process_id(),
version_->embedded_worker()->worker_devtools_agent_route_id()),
fetch_event_id);
}
// |event_dispatcher| is owned by |version_|. So it is safe to pass the
// unretained raw pointer of |version_| to OnFetchEventFinished callback.
// Pass |url_loader_assets_| to the callback to keep the URL loader related
// assets alive while the FetchEvent is ongoing in the service worker.
version_->event_dispatcher()->DispatchFetchEvent(
fetch_event_id, *request_, std::move(preload_handle_),
base::Bind(&ServiceWorkerFetchDispatcher::OnFetchEventFinished,
base::Unretained(version_.get()), event_finish_id,
url_loader_assets_));
}
void ServiceWorkerFetchDispatcher::DidFailToDispatch(
ServiceWorkerStatusCode status) {
EndNetLogEventWithServiceWorkerStatus(
net_log_, net::NetLogEventType::SERVICE_WORKER_FETCH_EVENT, status);
DidFail(status);
}
void ServiceWorkerFetchDispatcher::DidFail(ServiceWorkerStatusCode status) {
DCHECK_NE(SERVICE_WORKER_OK, status);
Complete(status, SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
ServiceWorkerResponse());
}
void ServiceWorkerFetchDispatcher::DidFinish(
int request_id,
ServiceWorkerFetchEventResult fetch_result,
const ServiceWorkerResponse& response) {
net_log_.EndEvent(net::NetLogEventType::SERVICE_WORKER_FETCH_EVENT);
Complete(SERVICE_WORKER_OK, fetch_result, response);
}
void ServiceWorkerFetchDispatcher::Complete(
ServiceWorkerStatusCode status,
ServiceWorkerFetchEventResult fetch_result,
const ServiceWorkerResponse& response) {
DCHECK(!fetch_callback_.is_null());
did_complete_ = true;
net_log_.EndEvent(
net::NetLogEventType::SERVICE_WORKER_DISPATCH_FETCH_EVENT,
base::Bind(&NetLogFetchEventCallback, status, fetch_result));
FetchCallback fetch_callback = fetch_callback_;
scoped_refptr<ServiceWorkerVersion> version = version_;
fetch_callback.Run(status, fetch_result, response, version);
}
bool ServiceWorkerFetchDispatcher::MaybeStartNavigationPreload(
net::URLRequest* original_request,
base::OnceClosure on_response) {
if (resource_type_ != RESOURCE_TYPE_MAIN_FRAME &&
resource_type_ != RESOURCE_TYPE_SUB_FRAME) {
return false;
}
if (!version_->navigation_preload_state().enabled)
return false;
// TODO(horo): Currently NavigationPreload doesn't support request body.
if (!request_->blob_uuid.empty())
return false;
ServiceWorkerVersion::NavigationPreloadSupportStatus support_status =
version_->GetNavigationPreloadSupportStatus();
if (support_status !=
ServiceWorkerVersion::NavigationPreloadSupportStatus::SUPPORTED) {
return false;
}
ResourceRequestInfoImpl* original_info =
ResourceRequestInfoImpl::ForRequest(original_request);
ResourceRequesterInfo* requester_info = original_info->requester_info();
if (IsBrowserSideNavigationEnabled()) {
DCHECK(requester_info->IsBrowserSideNavigation());
} else {
DCHECK(requester_info->IsRenderer());
if (!requester_info->filter())
return false;
}
DCHECK(!url_loader_assets_);
mojom::URLLoaderFactoryPtr url_loader_factory;
URLLoaderFactoryImpl::Create(
ResourceRequesterInfo::CreateForNavigationPreload(requester_info),
mojo::MakeRequest(&url_loader_factory));
ResourceRequest request;
request.method = original_request->method();
request.url = original_request->url();
// TODO(horo): Set first_party_for_cookies to support Same-site Cookies.
request.request_initiator = original_request->initiator().has_value()
? original_request->initiator()
: url::Origin(original_request->url());
request.referrer = GURL(original_request->referrer());
request.referrer_policy = original_info->GetReferrerPolicy();
request.visibility_state = original_info->GetVisibilityState();
request.load_flags = original_request->load_flags();
// Set to SUB_RESOURCE because we shouldn't trigger NavigationResourceThrottle
// for the service worker navigation preload request.
request.resource_type = RESOURCE_TYPE_SUB_RESOURCE;
request.priority = original_request->priority();
request.service_worker_mode = ServiceWorkerMode::NONE;
request.do_not_prompt_for_login = true;
request.render_frame_id = original_info->GetRenderFrameID();
request.is_main_frame = original_info->IsMainFrame();
request.parent_is_main_frame = original_info->ParentIsMainFrame();
request.enable_load_timing = original_info->is_load_timing_enabled();
request.report_raw_headers = original_info->ShouldReportRawHeaders();
DCHECK(net::HttpUtil::IsValidHeaderValue(
version_->navigation_preload_state().header));
ServiceWorkerMetrics::RecordNavigationPreloadRequestHeaderSize(
version_->navigation_preload_state().header.length());
request.headers = "Service-Worker-Navigation-Preload: " +
version_->navigation_preload_state().header + "\r\n" +
original_request->extra_request_headers().ToString();
const int request_id = ResourceDispatcherHostImpl::Get()->MakeRequestID();
DCHECK_LT(request_id, -1);
preload_handle_ = mojom::FetchEventPreloadHandle::New();
mojom::URLLoaderClientPtr url_loader_client_ptr;
preload_handle_->url_loader_client_request =
mojo::MakeRequest(&url_loader_client_ptr);
auto url_loader_client = base::MakeUnique<DelegatingURLLoaderClient>(
std::move(url_loader_client_ptr), std::move(on_response), request);
mojom::URLLoaderClientAssociatedPtrInfo url_loader_client_associated_ptr_info;
url_loader_client->Bind(&url_loader_client_associated_ptr_info);
mojom::URLLoaderAssociatedPtr url_loader_associated_ptr;
url_loader_factory->CreateLoaderAndStart(
mojo::MakeRequest(&url_loader_associated_ptr),
original_info->GetRouteID(), request_id, request,
std::move(url_loader_client_associated_ptr_info));
std::unique_ptr<DelegatingURLLoader> url_loader(
base::MakeUnique<DelegatingURLLoader>(
std::move(url_loader_associated_ptr)));
preload_handle_->url_loader = url_loader->CreateInterfacePtrAndBind();
url_loader_assets_ =
new URLLoaderAssets(std::move(url_loader_factory), std::move(url_loader),
std::move(url_loader_client));
return true;
}
ServiceWorkerMetrics::EventType ServiceWorkerFetchDispatcher::GetEventType()
const {
if (request_->fetch_type == ServiceWorkerFetchType::FOREIGN_FETCH)
return ServiceWorkerMetrics::EventType::FOREIGN_FETCH;
return ResourceTypeToEventType(resource_type_);
}
// static
void ServiceWorkerFetchDispatcher::OnFetchEventFinished(
ServiceWorkerVersion* version,
int event_finish_id,
scoped_refptr<URLLoaderAssets> url_loader_assets,
ServiceWorkerStatusCode status,
base::Time dispatch_event_time) {
version->FinishRequest(event_finish_id, status != SERVICE_WORKER_ERROR_ABORT,
dispatch_event_time);
}
} // namespace content