| // 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/child/service_worker/service_worker_network_provider.h" |
| |
| #include "base/atomic_sequence_num.h" |
| #include "content/child/child_thread_impl.h" |
| #include "content/child/request_extra_data.h" |
| #include "content/child/service_worker/service_worker_handle_reference.h" |
| #include "content/child/service_worker/service_worker_provider_context.h" |
| #include "content/common/navigation_params.h" |
| #include "content/common/service_worker/service_worker_messages.h" |
| #include "content/common/service_worker/service_worker_provider_host_info.h" |
| #include "content/common/service_worker/service_worker_utils.h" |
| #include "content/public/common/browser_side_navigation_policy.h" |
| #include "ipc/ipc_sync_channel.h" |
| #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" |
| #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h" |
| #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| #include "third_party/WebKit/public/web/WebSandboxFlags.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // Must be unique in the child process. |
| int GetNextProviderId() { |
| static base::StaticAtomicSequenceNumber sequence; |
| return sequence.GetNext(); // We start at zero. |
| } |
| |
| // Returns whether it's possible for a document whose frame is a descendant of |
| // |frame| to be a secure context, not considering scheme exceptions (since any |
| // document can be a secure context if it has a scheme exception). See |
| // Document::isSecureContextImpl for more details. |
| bool IsFrameSecure(blink::WebFrame* frame) { |
| while (frame) { |
| if (!frame->GetSecurityOrigin().IsPotentiallyTrustworthy()) |
| return false; |
| frame = frame->Parent(); |
| } |
| return true; |
| } |
| |
| // An WebServiceWorkerNetworkProvider for frame. This wraps |
| // ServiceWorkerNetworkProvider implementation and is owned by blink. |
| class WebServiceWorkerNetworkProviderForFrame |
| : public blink::WebServiceWorkerNetworkProvider { |
| public: |
| WebServiceWorkerNetworkProviderForFrame( |
| std::unique_ptr<ServiceWorkerNetworkProvider> provider) |
| : provider_(std::move(provider)) {} |
| |
| void WillSendRequest(blink::WebURLRequest& request) override { |
| RequestExtraData* extra_data = |
| static_cast<RequestExtraData*>(request.GetExtraData()); |
| if (!extra_data) |
| extra_data = new RequestExtraData(); |
| extra_data->set_service_worker_provider_id(provider_->provider_id()); |
| request.SetExtraData(extra_data); |
| |
| // If the provider does not have a controller at this point, the renderer |
| // expects the request to never be handled by a controlling service worker, |
| // so set the ServiceWorkerMode to skip local workers here. Otherwise, a |
| // service worker that is in the process of becoming the controller (i.e., |
| // via claim()) on the browser-side could handle the request and break |
| // the assumptions of the renderer. |
| if (request.GetFrameType() != blink::WebURLRequest::kFrameTypeTopLevel && |
| request.GetFrameType() != blink::WebURLRequest::kFrameTypeNested && |
| !provider_->IsControlledByServiceWorker() && |
| request.GetServiceWorkerMode() != |
| blink::WebURLRequest::ServiceWorkerMode::kNone) { |
| request.SetServiceWorkerMode( |
| blink::WebURLRequest::ServiceWorkerMode::kForeign); |
| } |
| } |
| |
| bool IsControlledByServiceWorker() override { |
| return provider_->IsControlledByServiceWorker(); |
| } |
| |
| int GetProviderID() const override { return provider_->provider_id(); } |
| |
| int64_t ServiceWorkerID() override { |
| if (provider_->context() && provider_->context()->controller()) |
| return provider_->context()->controller()->version_id(); |
| return kInvalidServiceWorkerVersionId; |
| } |
| |
| ServiceWorkerNetworkProvider* provider() { return provider_.get(); } |
| |
| private: |
| std::unique_ptr<ServiceWorkerNetworkProvider> provider_; |
| }; |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<blink::WebServiceWorkerNetworkProvider> |
| ServiceWorkerNetworkProvider::CreateForNavigation( |
| int route_id, |
| const RequestNavigationParams& request_params, |
| blink::WebLocalFrame* frame, |
| bool content_initiated) { |
| bool browser_side_navigation = IsBrowserSideNavigationEnabled(); |
| bool should_create_provider_for_window = false; |
| int service_worker_provider_id = kInvalidServiceWorkerProviderId; |
| std::unique_ptr<ServiceWorkerNetworkProvider> network_provider; |
| |
| // Determine if a ServiceWorkerNetworkProvider should be created and properly |
| // initialized for the navigation. A default ServiceWorkerNetworkProvider |
| // will always be created since it is expected in a certain number of places, |
| // however it will have an invalid id. |
| // PlzNavigate: |service_worker_provider_id| can be sent by the browser, if |
| // it already created the SeviceWorkerProviderHost. |
| if (browser_side_navigation && !content_initiated) { |
| should_create_provider_for_window = |
| request_params.should_create_service_worker; |
| service_worker_provider_id = request_params.service_worker_provider_id; |
| DCHECK(ServiceWorkerUtils::IsBrowserAssignedProviderId( |
| service_worker_provider_id) || |
| service_worker_provider_id == kInvalidServiceWorkerProviderId); |
| } else { |
| should_create_provider_for_window = |
| ((frame->EffectiveSandboxFlags() & blink::WebSandboxFlags::kOrigin) != |
| blink::WebSandboxFlags::kOrigin); |
| } |
| |
| // Now create the ServiceWorkerNetworkProvider (with invalid id if needed). |
| if (should_create_provider_for_window) { |
| // Ideally Document::isSecureContext would be called here, but the document |
| // is not created yet, and due to redirects the URL may change. So pass |
| // is_parent_frame_secure to the browser process, so it can determine the |
| // context security when deciding whether to allow a service worker to |
| // control the document. |
| const bool is_parent_frame_secure = IsFrameSecure(frame->Parent()); |
| |
| if (service_worker_provider_id == kInvalidServiceWorkerProviderId) { |
| network_provider = std::unique_ptr<ServiceWorkerNetworkProvider>( |
| new ServiceWorkerNetworkProvider(route_id, |
| SERVICE_WORKER_PROVIDER_FOR_WINDOW, |
| is_parent_frame_secure)); |
| } else { |
| CHECK(browser_side_navigation); |
| DCHECK(ServiceWorkerUtils::IsBrowserAssignedProviderId( |
| service_worker_provider_id)); |
| network_provider = std::unique_ptr<ServiceWorkerNetworkProvider>( |
| new ServiceWorkerNetworkProvider( |
| route_id, SERVICE_WORKER_PROVIDER_FOR_WINDOW, |
| service_worker_provider_id, is_parent_frame_secure)); |
| } |
| } else { |
| network_provider = std::unique_ptr<ServiceWorkerNetworkProvider>( |
| new ServiceWorkerNetworkProvider()); |
| } |
| return base::MakeUnique<WebServiceWorkerNetworkProviderForFrame>( |
| std::move(network_provider)); |
| } |
| |
| // static |
| ServiceWorkerNetworkProvider* |
| ServiceWorkerNetworkProvider::FromWebServiceWorkerNetworkProvider( |
| blink::WebServiceWorkerNetworkProvider* provider) { |
| return static_cast<WebServiceWorkerNetworkProviderForFrame*>(provider) |
| ->provider(); |
| } |
| |
| ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider( |
| int route_id, |
| ServiceWorkerProviderType provider_type, |
| int browser_provider_id, |
| bool is_parent_frame_secure) |
| : provider_id_(browser_provider_id) { |
| if (provider_id_ == kInvalidServiceWorkerProviderId) |
| return; |
| if (!ChildThreadImpl::current()) |
| return; // May be null in some tests. |
| ServiceWorkerProviderHostInfo provider_info( |
| provider_id_, route_id, provider_type, is_parent_frame_secure); |
| context_ = new ServiceWorkerProviderContext( |
| provider_id_, provider_type, |
| ChildThreadImpl::current()->thread_safe_sender()); |
| ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface( |
| &dispatcher_host_); |
| dispatcher_host_->OnProviderCreated(std::move(provider_info)); |
| } |
| |
| ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider( |
| int route_id, |
| ServiceWorkerProviderType provider_type, |
| bool is_parent_frame_secure) |
| : ServiceWorkerNetworkProvider(route_id, |
| provider_type, |
| GetNextProviderId(), |
| is_parent_frame_secure) {} |
| |
| ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider() |
| : provider_id_(kInvalidServiceWorkerProviderId) {} |
| |
| ServiceWorkerNetworkProvider::~ServiceWorkerNetworkProvider() { |
| if (provider_id_ == kInvalidServiceWorkerProviderId) |
| return; |
| if (!ChildThreadImpl::current()) |
| return; // May be null in some tests. |
| dispatcher_host_->OnProviderDestroyed(provider_id()); |
| } |
| |
| void ServiceWorkerNetworkProvider::SetServiceWorkerVersionId( |
| int64_t version_id, |
| int embedded_worker_id) { |
| DCHECK_NE(kInvalidServiceWorkerProviderId, provider_id_); |
| if (!ChildThreadImpl::current()) |
| return; // May be null in some tests. |
| dispatcher_host_->OnSetHostedVersionId(provider_id(), version_id, |
| embedded_worker_id); |
| } |
| |
| bool ServiceWorkerNetworkProvider::IsControlledByServiceWorker() const { |
| return context() && context()->controller(); |
| } |
| |
| } // namespace content |