| // 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_controllee_request_handler.h" |
| |
| #include <set> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/optional.h" |
| #include "base/trace_event/trace_event.h" |
| #include "components/offline_pages/buildflags/buildflags.h" |
| #include "content/browser/loader/navigation_url_loader_impl.h" |
| #include "content/browser/navigation_subresource_loader_params.h" |
| #include "content/browser/service_worker/service_worker_context_core.h" |
| #include "content/browser/service_worker/service_worker_context_wrapper.h" |
| #include "content/browser/service_worker/service_worker_metrics.h" |
| #include "content/browser/service_worker/service_worker_navigation_loader.h" |
| #include "content/browser/service_worker/service_worker_provider_host.h" |
| #include "content/browser/service_worker/service_worker_registration.h" |
| #include "content/common/service_worker/service_worker_utils.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_client.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/url_util.h" |
| #include "services/network/public/cpp/resource_request_body.h" |
| #include "services/network/public/cpp/resource_response_info.h" |
| #include "third_party/blink/public/common/service_worker/service_worker_utils.h" |
| |
| #if BUILDFLAG(ENABLE_OFFLINE_PAGES) |
| #include "components/offline_pages/core/request_header/offline_page_header.h" |
| #endif // BUILDFLAG(ENABLE_OFFLINE_PAGES) |
| |
| namespace content { |
| |
| namespace { |
| |
| #if BUILDFLAG(ENABLE_OFFLINE_PAGES) |
| // A web page, regardless of whether the service worker is used or not, could |
| // be downloaded with the offline snapshot captured. The user can then open |
| // the downloaded page which is identified by the presence of a specific |
| // offline header in the network request. In this case, we want to fall back |
| // in order for the subsequent offline page interceptor to bring up the |
| // offline snapshot of the page. |
| bool ShouldFallbackToLoadOfflinePage( |
| const net::HttpRequestHeaders& extra_request_headers) { |
| std::string offline_header_value; |
| if (!extra_request_headers.GetHeader(offline_pages::kOfflinePageHeader, |
| &offline_header_value)) { |
| return false; |
| } |
| offline_pages::OfflinePageHeader offline_header(offline_header_value); |
| return offline_header.reason != |
| offline_pages::OfflinePageHeader::Reason::NONE && |
| offline_header.reason != |
| offline_pages::OfflinePageHeader::Reason::RELOAD; |
| } |
| #endif // BUILDFLAG(ENABLE_OFFLINE_PAGES) |
| |
| } // namespace |
| |
| ServiceWorkerControlleeRequestHandler::ServiceWorkerControlleeRequestHandler( |
| base::WeakPtr<ServiceWorkerContextCore> context, |
| base::WeakPtr<ServiceWorkerProviderHost> provider_host, |
| ResourceType resource_type, |
| bool skip_service_worker) |
| : context_(std::move(context)), |
| provider_host_(std::move(provider_host)), |
| resource_type_(resource_type), |
| skip_service_worker_(skip_service_worker), |
| force_update_started_(false) { |
| DCHECK(ServiceWorkerUtils::IsMainResourceType(resource_type)); |
| TRACE_EVENT_WITH_FLOW0("ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::" |
| "ServiceWorkerControlleeRequestHandler", |
| TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_OUT); |
| } |
| |
| ServiceWorkerControlleeRequestHandler:: |
| ~ServiceWorkerControlleeRequestHandler() { |
| TRACE_EVENT_WITH_FLOW0("ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::" |
| "~ServiceWorkerControlleeRequestHandler", |
| TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN); |
| MaybeScheduleUpdate(); |
| } |
| |
| void ServiceWorkerControlleeRequestHandler::MaybeScheduleUpdate() { |
| if (!provider_host_ || !provider_host_->controller()) |
| return; |
| |
| // For navigations, the update logic is taken care of |
| // during navigation and waits for the HintToUpdateServiceWorker message. |
| if (IsResourceTypeFrame(resource_type_)) |
| return; |
| |
| // For shared workers. The renderer doesn't yet send a |
| // HintToUpdateServiceWorker message. |
| // TODO(falken): Make the renderer send the message for shared worker, |
| // to simplify the code. |
| |
| // If DevTools forced an update, there is no need to update again. |
| if (force_update_started_) |
| return; |
| |
| provider_host_->controller()->ScheduleUpdate(); |
| } |
| |
| void ServiceWorkerControlleeRequestHandler::MaybeCreateLoader( |
| const network::ResourceRequest& tentative_resource_request, |
| BrowserContext* browser_context, |
| ResourceContext* resource_context, |
| NavigationLoaderInterceptor::LoaderCallback callback, |
| NavigationLoaderInterceptor::FallbackCallback fallback_callback) { |
| // InitializeProvider() will update the host. This is important to do before |
| // falling back to network below, so service worker APIs still work even if |
| // the service worker is bypassed for request interception. |
| if (!InitializeProvider(tentative_resource_request)) { |
| // We can't do anything other than to fall back to network. |
| std::move(callback).Run({}); |
| return; |
| } |
| |
| // Fall back to network if we were instructed to bypass the service worker for |
| // request interception, or if the context is gone so we have to bypass |
| // anyway. |
| if (skip_service_worker_ || !context_) { |
| std::move(callback).Run({}); |
| return; |
| } |
| |
| #if BUILDFLAG(ENABLE_OFFLINE_PAGES) |
| // Fall back for the subsequent offline page interceptor to load the offline |
| // snapshot of the page if required. |
| // |
| // TODO(crbug.com/876527): Figure out how offline page interception should |
| // interact with URLLoaderThrottles. It might be incorrect to use |
| // |tentative_resource_request.headers| here, since throttles can rewrite |
| // headers between now and when the request handler passed to |
| // |loader_callback_| is invoked. |
| if (ShouldFallbackToLoadOfflinePage(tentative_resource_request.headers)) { |
| std::move(callback).Run({}); |
| return; |
| } |
| #endif // BUILDFLAG(ENABLE_OFFLINE_PAGES) |
| |
| // TODO(bashi): Consider using a global navigation ID instead of using |this|. |
| // Using a global ID gives us a convenient way to analyze event flows across |
| // classes. |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::MaybeCreateLoader", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "URL", |
| tentative_resource_request.url.spec()); |
| |
| loader_callback_ = std::move(callback); |
| fallback_callback_ = std::move(fallback_callback); |
| registration_lookup_start_time_ = base::TimeTicks::Now(); |
| browser_context_ = browser_context; |
| resource_context_ = resource_context; |
| |
| // Look up a registration. |
| context_->storage()->FindRegistrationForDocument( |
| stripped_url_, |
| base::BindOnce( |
| &ServiceWorkerControlleeRequestHandler::ContinueWithRegistration, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| base::Optional<SubresourceLoaderParams> |
| ServiceWorkerControlleeRequestHandler::MaybeCreateSubresourceLoaderParams() { |
| // ContinueWithRegistration() for the request didn't find a matching service |
| // worker for this request, and |
| // ServiceWorkerProviderHost::SetControllerRegistration() was not called. |
| if (!provider_host_ || !provider_host_->controller()) |
| return base::nullopt; |
| |
| // Otherwise let's send the controller service worker information along |
| // with the navigation commit. |
| SubresourceLoaderParams params; |
| auto controller_info = blink::mojom::ControllerServiceWorkerInfo::New(); |
| controller_info->mode = provider_host_->GetControllerMode(); |
| // Note that |controller_info->remote_controller| is null if the controller |
| // has no fetch event handler. In that case the renderer frame won't get the |
| // controller pointer upon the navigation commit, and subresource loading will |
| // not be intercepted. (It might get intercepted later if the controller |
| // changes due to skipWaiting() so SetController is sent.) |
| mojo::Remote<blink::mojom::ControllerServiceWorker> remote = |
| provider_host_->GetRemoteControllerServiceWorker(); |
| if (remote.is_bound()) { |
| controller_info->remote_controller = remote.Unbind(); |
| } |
| |
| controller_info->client_id = provider_host_->client_uuid(); |
| if (provider_host_->fetch_request_window_id()) { |
| controller_info->fetch_request_window_id = |
| base::make_optional(provider_host_->fetch_request_window_id()); |
| } |
| base::WeakPtr<ServiceWorkerObjectHost> object_host = |
| provider_host_->GetOrCreateServiceWorkerObjectHost( |
| provider_host_->controller()); |
| if (object_host) { |
| params.controller_service_worker_object_host = object_host; |
| controller_info->object_info = object_host->CreateIncompleteObjectInfo(); |
| } |
| for (const auto feature : provider_host_->controller()->used_features()) { |
| controller_info->used_features.push_back(feature); |
| } |
| params.controller_service_worker_info = std::move(controller_info); |
| return base::Optional<SubresourceLoaderParams>(std::move(params)); |
| } |
| |
| bool ServiceWorkerControlleeRequestHandler::InitializeProvider( |
| const network::ResourceRequest& tentative_resource_request) { |
| ClearJob(); |
| |
| if (!provider_host_) { |
| return false; |
| } |
| |
| // Update the provider host with this request, clearing old controller state |
| // if this is a redirect. |
| provider_host_->SetControllerRegistration(nullptr, |
| /*notify_controllerchange=*/false); |
| stripped_url_ = net::SimplifyUrlForRequest(tentative_resource_request.url); |
| provider_host_->UpdateUrls( |
| stripped_url_, tentative_resource_request.site_for_cookies, |
| tentative_resource_request.trusted_params |
| ? tentative_resource_request.trusted_params->network_isolation_key |
| .GetTopFrameOrigin() |
| : base::nullopt); |
| return true; |
| } |
| |
| void ServiceWorkerControlleeRequestHandler::ContinueWithRegistration( |
| blink::ServiceWorkerStatusCode status, |
| scoped_refptr<ServiceWorkerRegistration> registration) { |
| ServiceWorkerMetrics::RecordLookupRegistrationTime( |
| status, base::TimeTicks::Now() - registration_lookup_start_time_); |
| |
| if (status != blink::ServiceWorkerStatusCode::kOk) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Status", |
| blink::ServiceWorkerStatusToString(status)); |
| CompleteWithoutLoader(); |
| return; |
| } |
| DCHECK(registration); |
| |
| if (!provider_host_) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "No Provider"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| provider_host_->AddMatchingRegistration(registration.get()); |
| |
| if (!context_) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "No Context"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| |
| bool allow_service_worker = false; |
| if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { |
| allow_service_worker = |
| GetContentClient()->browser()->AllowServiceWorkerOnUI( |
| registration->scope(), provider_host_->site_for_cookies(), |
| provider_host_->top_frame_origin(), /*script_url=*/GURL(), |
| browser_context_, provider_host_->web_contents_getter()); |
| } else { |
| allow_service_worker = |
| GetContentClient()->browser()->AllowServiceWorkerOnIO( |
| registration->scope(), provider_host_->site_for_cookies(), |
| provider_host_->top_frame_origin(), /*script_url=*/GURL(), |
| resource_context_, provider_host_->web_contents_getter()); |
| } |
| |
| if (!allow_service_worker) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "ServiceWorker is blocked"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| |
| if (!provider_host_->IsContextSecureForServiceWorker()) { |
| // TODO(falken): Figure out a way to surface in the page's DevTools |
| // console that the service worker was blocked for security. |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "Insecure context"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| |
| const bool need_to_update = |
| !force_update_started_ && context_->force_update_on_page_load(); |
| if (need_to_update) { |
| force_update_started_ = true; |
| context_->UpdateServiceWorker( |
| registration.get(), true /* force_bypass_cache */, |
| true /* skip_script_comparison */, |
| base::BindOnce( |
| &ServiceWorkerControlleeRequestHandler::DidUpdateRegistration, |
| weak_factory_.GetWeakPtr(), registration)); |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "Need to update"); |
| return; |
| } |
| |
| // Initiate activation of a waiting version. Usually a register job initiates |
| // activation but that doesn't happen if the browser exits prior to activation |
| // having occurred. This check handles that case. |
| if (registration->waiting_version()) |
| registration->ActivateWaitingVersionWhenReady(); |
| |
| scoped_refptr<ServiceWorkerVersion> active_version = |
| registration->active_version(); |
| if (!active_version) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "No active version, so falling back to network"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| |
| DCHECK(active_version->status() == ServiceWorkerVersion::ACTIVATING || |
| active_version->status() == ServiceWorkerVersion::ACTIVATED) |
| << ServiceWorkerVersion::VersionStatusToString(active_version->status()); |
| // Wait until it's activated before firing fetch events. |
| if (active_version->status() == ServiceWorkerVersion::ACTIVATING) { |
| registration->active_version()->RegisterStatusChangeCallback(base::BindOnce( |
| &ServiceWorkerControlleeRequestHandler::ContinueWithActivatedVersion, |
| weak_factory_.GetWeakPtr(), registration, active_version)); |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "Wait until finished SW activation"); |
| return; |
| } |
| |
| TRACE_EVENT_WITH_FLOW0( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); |
| |
| ContinueWithActivatedVersion(std::move(registration), |
| std::move(active_version)); |
| } |
| |
| void ServiceWorkerControlleeRequestHandler::ContinueWithActivatedVersion( |
| scoped_refptr<ServiceWorkerRegistration> registration, |
| scoped_refptr<ServiceWorkerVersion> active_version) { |
| if (!context_ || !provider_host_) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithActivatedVersion", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "The context or provider host is gone, so falling back to network"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| |
| if (active_version->status() != ServiceWorkerVersion::ACTIVATED) { |
| // TODO(falken): Clean this up and clarify in what cases we come here. I |
| // guess it's: |
| // - strange system error cases where promoting from ACTIVATING to ACTIVATED |
| // failed (shouldn't happen) |
| // - something calling Doom(), etc, making the active_version REDUNDANT |
| // - a version called skipWaiting() during activation so the expected |
| // version is no longer the active one (shouldn't happen: skipWaiting() |
| // waits for the active version to finish activating). |
| // In most cases, it sounds like falling back to network would not be right, |
| // since it's still in-scope. We probably should do: |
| // 1) If the provider host has an active version that is ACTIVATED, just |
| // use that, even if it wasn't the expected one. |
| // 2) If the provider host has an active version that is not ACTIVATED, |
| // just fail the load. The correct thing is probably to re-try |
| // activating that version, but there's a risk of an infinite loop of |
| // retries. |
| // 3) If the provider host does not have an active version, just fail the |
| // load. |
| TRACE_EVENT_WITH_FLOW2( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithActivatedVersion", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "The expected active version is not ACTIVATED, so falling back to " |
| "network", |
| "Status", |
| ServiceWorkerVersion::VersionStatusToString(active_version->status())); |
| CompleteWithoutLoader(); |
| return; |
| } |
| |
| provider_host_->SetControllerRegistration( |
| registration, false /* notify_controllerchange */); |
| |
| DCHECK_EQ(active_version, registration->active_version()); |
| DCHECK_EQ(active_version, provider_host_->controller()); |
| DCHECK_NE(active_version->fetch_handler_existence(), |
| ServiceWorkerVersion::FetchHandlerExistence::UNKNOWN); |
| ServiceWorkerMetrics::CountControlledPageLoad( |
| active_version->site_for_uma(), stripped_url_, |
| resource_type_ == ResourceType::kMainFrame); |
| |
| if (IsResourceTypeFrame(resource_type_)) |
| provider_host_->AddServiceWorkerToUpdate(active_version); |
| |
| if (active_version->fetch_handler_existence() != |
| ServiceWorkerVersion::FetchHandlerExistence::EXISTS) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithActivatedVersion", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "Skipped the ServiceWorker which has no fetch handler"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| |
| // Finally, we want to forward to the service worker! Make a |
| // ServiceWorkerNavigationLoader which does that work. |
| loader_wrapper_ = std::make_unique<ServiceWorkerNavigationLoaderWrapper>( |
| std::make_unique<ServiceWorkerNavigationLoader>( |
| std::move(fallback_callback_), provider_host_, |
| base::WrapRefCounted(context_->loader_factory_getter()))); |
| |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::ContinueWithActivatedVersion", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "Forwarded to the ServiceWorker"); |
| std::move(loader_callback_) |
| .Run(base::BindOnce(&ServiceWorkerNavigationLoader::StartRequest, |
| loader_wrapper_->get()->AsWeakPtr())); |
| } |
| |
| void ServiceWorkerControlleeRequestHandler::DidUpdateRegistration( |
| scoped_refptr<ServiceWorkerRegistration> original_registration, |
| blink::ServiceWorkerStatusCode status, |
| const std::string& status_message, |
| int64_t registration_id) { |
| DCHECK(force_update_started_); |
| |
| if (!context_) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::DidUpdateRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "The context is gone in DidUpdateRegistration"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| if (status != blink::ServiceWorkerStatusCode::kOk || |
| !original_registration->installing_version()) { |
| // Update failed. Look up the registration again since the original |
| // registration was possibly unregistered in the meantime. |
| context_->storage()->FindRegistrationForDocument( |
| stripped_url_, |
| base::BindOnce( |
| &ServiceWorkerControlleeRequestHandler::ContinueWithRegistration, |
| weak_factory_.GetWeakPtr())); |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::DidUpdateRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "Update failed, look up the registration again"); |
| return; |
| } |
| |
| TRACE_EVENT_WITH_FLOW0( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::DidUpdateRegistration", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); |
| |
| DCHECK_EQ(original_registration->id(), registration_id); |
| ServiceWorkerVersion* new_version = |
| original_registration->installing_version(); |
| new_version->ReportForceUpdateToDevTools(); |
| new_version->set_skip_waiting(true); |
| new_version->RegisterStatusChangeCallback(base::BindOnce( |
| &ServiceWorkerControlleeRequestHandler::OnUpdatedVersionStatusChanged, |
| weak_factory_.GetWeakPtr(), std::move(original_registration), |
| base::WrapRefCounted(new_version))); |
| } |
| |
| void ServiceWorkerControlleeRequestHandler::OnUpdatedVersionStatusChanged( |
| scoped_refptr<ServiceWorkerRegistration> registration, |
| scoped_refptr<ServiceWorkerVersion> version) { |
| if (!context_) { |
| TRACE_EVENT_WITH_FLOW1( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::OnUpdatedVersionStatusChanged", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Info", |
| "The context is gone in OnUpdatedVersionStatusChanged"); |
| CompleteWithoutLoader(); |
| return; |
| } |
| |
| TRACE_EVENT_WITH_FLOW0( |
| "ServiceWorker", |
| "ServiceWorkerControlleeRequestHandler::OnUpdatedVersionStatusChanged", |
| TRACE_ID_LOCAL(this), |
| TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); |
| |
| if (version->status() == ServiceWorkerVersion::ACTIVATED || |
| version->status() == ServiceWorkerVersion::REDUNDANT) { |
| // When the status is REDUNDANT, the update failed (eg: script error), we |
| // continue with the incumbent version. |
| // In case unregister job may have run, look up the registration again. |
| context_->storage()->FindRegistrationForDocument( |
| stripped_url_, |
| base::BindOnce( |
| &ServiceWorkerControlleeRequestHandler::ContinueWithRegistration, |
| weak_factory_.GetWeakPtr())); |
| return; |
| } |
| version->RegisterStatusChangeCallback(base::BindOnce( |
| &ServiceWorkerControlleeRequestHandler::OnUpdatedVersionStatusChanged, |
| weak_factory_.GetWeakPtr(), std::move(registration), version)); |
| } |
| |
| void ServiceWorkerControlleeRequestHandler::ClearJob() { |
| // Invalidate weak pointers to cancel RegisterStatusChangeCallback(). |
| // Otherwise we may end up calling ForwardToServiceWorer() |
| // or FallbackToNetwork() twice on the same |loader()|. |
| // TODO(bashi): Consider not to reuse this handler when restarting the |
| // request after S13nServiceWorker is shipped. |
| weak_factory_.InvalidateWeakPtrs(); |
| loader_wrapper_.reset(); |
| } |
| |
| void ServiceWorkerControlleeRequestHandler::CompleteWithoutLoader() { |
| std::move(loader_callback_).Run({}); |
| } |
| |
| } // namespace content |