| // 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_request_handler.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/macros.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_navigation_handle_core.h" |
| #include "content/browser/service_worker/service_worker_provider_host.h" |
| #include "content/browser/service_worker/service_worker_registration.h" |
| #include "content/browser/service_worker/service_worker_url_request_job.h" |
| #include "content/common/resource_request_body_impl.h" |
| #include "content/common/service_worker/service_worker_types.h" |
| #include "content/common/service_worker/service_worker_utils.h" |
| #include "content/public/browser/resource_context.h" |
| #include "content/public/common/browser_side_navigation_policy.h" |
| #include "content/public/common/child_process_host.h" |
| #include "content/public/common/origin_util.h" |
| #include "ipc/ipc_message.h" |
| #include "net/base/url_util.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_interceptor.h" |
| #include "storage/browser/blob/blob_storage_context.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| int kUserDataKey; // Key value is not important. |
| |
| class ServiceWorkerRequestInterceptor |
| : public net::URLRequestInterceptor { |
| public: |
| explicit ServiceWorkerRequestInterceptor(ResourceContext* resource_context) |
| : resource_context_(resource_context) {} |
| ~ServiceWorkerRequestInterceptor() override {} |
| net::URLRequestJob* MaybeInterceptRequest( |
| net::URLRequest* request, |
| net::NetworkDelegate* network_delegate) const override { |
| ServiceWorkerRequestHandler* handler = |
| ServiceWorkerRequestHandler::GetHandler(request); |
| if (!handler) |
| return NULL; |
| return handler->MaybeCreateJob( |
| request, network_delegate, resource_context_); |
| } |
| |
| private: |
| ResourceContext* resource_context_; |
| DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRequestInterceptor); |
| }; |
| |
| void FinalizeHandlerInitialization( |
| net::URLRequest* request, |
| ServiceWorkerProviderHost* provider_host, |
| storage::BlobStorageContext* blob_storage_context, |
| bool skip_service_worker, |
| FetchRequestMode request_mode, |
| FetchCredentialsMode credentials_mode, |
| FetchRedirectMode redirect_mode, |
| ResourceType resource_type, |
| RequestContextType request_context_type, |
| RequestContextFrameType frame_type, |
| scoped_refptr<ResourceRequestBodyImpl> body) { |
| std::unique_ptr<ServiceWorkerRequestHandler> handler( |
| provider_host->CreateRequestHandler( |
| request_mode, credentials_mode, redirect_mode, resource_type, |
| request_context_type, frame_type, blob_storage_context->AsWeakPtr(), |
| body, skip_service_worker)); |
| if (!handler) |
| return; |
| |
| request->SetUserData(&kUserDataKey, handler.release()); |
| } |
| |
| } // namespace |
| |
| // PlzNavigate |
| void ServiceWorkerRequestHandler::InitializeForNavigation( |
| net::URLRequest* request, |
| ServiceWorkerNavigationHandleCore* navigation_handle_core, |
| storage::BlobStorageContext* blob_storage_context, |
| bool skip_service_worker, |
| ResourceType resource_type, |
| RequestContextType request_context_type, |
| RequestContextFrameType frame_type, |
| bool is_parent_frame_secure, |
| scoped_refptr<ResourceRequestBodyImpl> body, |
| const base::Callback<WebContents*(void)>& web_contents_getter) { |
| CHECK(IsBrowserSideNavigationEnabled()); |
| |
| // Only create a handler when there is a ServiceWorkerNavigationHandlerCore |
| // to take ownership of a pre-created SeviceWorkerProviderHost. |
| if (!navigation_handle_core) |
| return; |
| |
| // Create the handler even for insecure HTTP since it's used in the |
| // case of redirect to HTTPS. |
| if (!request->url().SchemeIsHTTPOrHTTPS() && |
| !OriginCanAccessServiceWorkers(request->url())) { |
| return; |
| } |
| |
| if (!navigation_handle_core->context_wrapper() || |
| !navigation_handle_core->context_wrapper()->context()) { |
| return; |
| } |
| |
| // Initialize the SWProviderHost. |
| std::unique_ptr<ServiceWorkerProviderHost> provider_host = |
| ServiceWorkerProviderHost::PreCreateNavigationHost( |
| navigation_handle_core->context_wrapper()->context()->AsWeakPtr(), |
| is_parent_frame_secure, web_contents_getter); |
| |
| FinalizeHandlerInitialization( |
| request, provider_host.get(), blob_storage_context, skip_service_worker, |
| FETCH_REQUEST_MODE_NAVIGATE, FETCH_CREDENTIALS_MODE_INCLUDE, |
| FetchRedirectMode::MANUAL_MODE, resource_type, request_context_type, |
| frame_type, body); |
| |
| // Transfer ownership to the ServiceWorkerNavigationHandleCore. |
| // In the case of a successful navigation, the SWProviderHost will be |
| // transferred to its "final" destination in the OnProviderCreated handler. If |
| // the navigation fails, it will be destroyed along with the |
| // ServiceWorkerNavigationHandleCore. |
| navigation_handle_core->DidPreCreateProviderHost(std::move(provider_host)); |
| } |
| |
| void ServiceWorkerRequestHandler::InitializeHandler( |
| net::URLRequest* request, |
| ServiceWorkerContextWrapper* context_wrapper, |
| storage::BlobStorageContext* blob_storage_context, |
| int process_id, |
| int provider_id, |
| bool skip_service_worker, |
| FetchRequestMode request_mode, |
| FetchCredentialsMode credentials_mode, |
| FetchRedirectMode redirect_mode, |
| ResourceType resource_type, |
| RequestContextType request_context_type, |
| RequestContextFrameType frame_type, |
| scoped_refptr<ResourceRequestBodyImpl> body) { |
| // Create the handler even for insecure HTTP since it's used in the |
| // case of redirect to HTTPS. |
| if (!request->url().SchemeIsHTTPOrHTTPS() && |
| !OriginCanAccessServiceWorkers(request->url())) { |
| return; |
| } |
| |
| if (!context_wrapper || !context_wrapper->context() || |
| provider_id == kInvalidServiceWorkerProviderId) { |
| return; |
| } |
| |
| ServiceWorkerProviderHost* provider_host = |
| context_wrapper->context()->GetProviderHost(process_id, provider_id); |
| if (!provider_host || !provider_host->IsContextAlive()) |
| return; |
| |
| FinalizeHandlerInitialization(request, provider_host, blob_storage_context, |
| skip_service_worker, request_mode, |
| credentials_mode, redirect_mode, resource_type, |
| request_context_type, frame_type, body); |
| } |
| |
| ServiceWorkerRequestHandler* ServiceWorkerRequestHandler::GetHandler( |
| const net::URLRequest* request) { |
| return static_cast<ServiceWorkerRequestHandler*>( |
| request->GetUserData(&kUserDataKey)); |
| } |
| |
| std::unique_ptr<net::URLRequestInterceptor> |
| ServiceWorkerRequestHandler::CreateInterceptor( |
| ResourceContext* resource_context) { |
| return std::unique_ptr<net::URLRequestInterceptor>( |
| new ServiceWorkerRequestInterceptor(resource_context)); |
| } |
| |
| bool ServiceWorkerRequestHandler::IsControlledByServiceWorker( |
| const net::URLRequest* request) { |
| ServiceWorkerRequestHandler* handler = GetHandler(request); |
| if (!handler || !handler->provider_host_) |
| return false; |
| return handler->provider_host_->associated_registration() || |
| handler->provider_host_->running_hosted_version(); |
| } |
| |
| ServiceWorkerProviderHost* ServiceWorkerRequestHandler::GetProviderHost( |
| const net::URLRequest* request) { |
| ServiceWorkerRequestHandler* handler = GetHandler(request); |
| return handler ? handler->provider_host_.get() : nullptr; |
| } |
| |
| void ServiceWorkerRequestHandler::PrepareForCrossSiteTransfer( |
| int old_process_id) { |
| CHECK(!IsBrowserSideNavigationEnabled()); |
| if (!provider_host_ || !context_) |
| return; |
| old_process_id_ = old_process_id; |
| old_provider_id_ = provider_host_->provider_id(); |
| host_for_cross_site_transfer_ = context_->TransferProviderHostOut( |
| old_process_id, provider_host_->provider_id()); |
| DCHECK_EQ(provider_host_.get(), host_for_cross_site_transfer_.get()); |
| } |
| |
| void ServiceWorkerRequestHandler::CompleteCrossSiteTransfer( |
| int new_process_id, int new_provider_id) { |
| CHECK(!IsBrowserSideNavigationEnabled()); |
| if (!host_for_cross_site_transfer_.get() || !context_) |
| return; |
| DCHECK_EQ(provider_host_.get(), host_for_cross_site_transfer_.get()); |
| context_->TransferProviderHostIn(new_process_id, new_provider_id, |
| std::move(host_for_cross_site_transfer_)); |
| DCHECK_EQ(provider_host_->provider_id(), new_provider_id); |
| } |
| |
| void ServiceWorkerRequestHandler::MaybeCompleteCrossSiteTransferInOldProcess( |
| int old_process_id) { |
| CHECK(!IsBrowserSideNavigationEnabled()); |
| if (!host_for_cross_site_transfer_.get() || !context_ || |
| old_process_id_ != old_process_id) { |
| return; |
| } |
| CompleteCrossSiteTransfer(old_process_id_, old_provider_id_); |
| } |
| |
| bool ServiceWorkerRequestHandler::SanityCheckIsSameContext( |
| ServiceWorkerContextWrapper* wrapper) { |
| if (!wrapper) |
| return !context_; |
| return context_.get() == wrapper->context(); |
| } |
| |
| ServiceWorkerRequestHandler::~ServiceWorkerRequestHandler() { |
| } |
| |
| ServiceWorkerRequestHandler::ServiceWorkerRequestHandler( |
| base::WeakPtr<ServiceWorkerContextCore> context, |
| base::WeakPtr<ServiceWorkerProviderHost> provider_host, |
| base::WeakPtr<storage::BlobStorageContext> blob_storage_context, |
| ResourceType resource_type) |
| : context_(context), |
| provider_host_(provider_host), |
| blob_storage_context_(blob_storage_context), |
| resource_type_(resource_type), |
| old_process_id_(0), |
| old_provider_id_(kInvalidServiceWorkerProviderId) { |
| } |
| |
| } // namespace content |