| // 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/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_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/web/WebLocalFrame.h" |
| #include "third_party/WebKit/public/web/WebSandboxFlags.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| const char kUserDataKey[] = "SWProviderKey"; |
| |
| // 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; |
| } |
| |
| } // namespace |
| |
| void ServiceWorkerNetworkProvider::AttachToDocumentState( |
| base::SupportsUserData* datasource_userdata, |
| std::unique_ptr<ServiceWorkerNetworkProvider> network_provider) { |
| datasource_userdata->SetUserData(&kUserDataKey, network_provider.release()); |
| } |
| |
| ServiceWorkerNetworkProvider* ServiceWorkerNetworkProvider::FromDocumentState( |
| base::SupportsUserData* datasource_userdata) { |
| return static_cast<ServiceWorkerNetworkProvider*>( |
| datasource_userdata->GetUserData(&kUserDataKey)); |
| } |
| |
| // static |
| std::unique_ptr<ServiceWorkerNetworkProvider> |
| 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::Origin) != |
| blink::WebSandboxFlags::Origin); |
| } |
| |
| // 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 network_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. |
| context_ = new ServiceWorkerProviderContext( |
| provider_id_, provider_type, |
| ChildThreadImpl::current()->thread_safe_sender()); |
| if (ServiceWorkerUtils::IsMojoForServiceWorkerEnabled()) { |
| ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface( |
| &dispatcher_host_); |
| dispatcher_host_->OnProviderCreated(provider_id_, route_id, provider_type, |
| is_parent_frame_secure); |
| } else { |
| ChildThreadImpl::current()->Send(new ServiceWorkerHostMsg_ProviderCreated( |
| provider_id_, route_id, provider_type, is_parent_frame_secure)); |
| } |
| } |
| |
| 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. |
| if (ServiceWorkerUtils::IsMojoForServiceWorkerEnabled()) { |
| dispatcher_host_->OnProviderDestroyed(provider_id()); |
| } else { |
| ChildThreadImpl::current()->Send( |
| new ServiceWorkerHostMsg_ProviderDestroyed(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. |
| if (ServiceWorkerUtils::IsMojoForServiceWorkerEnabled()) { |
| dispatcher_host_->OnSetHostedVersionId(provider_id(), version_id, |
| embedded_worker_id); |
| } else { |
| ChildThreadImpl::current()->Send(new ServiceWorkerHostMsg_SetVersionId( |
| provider_id_, version_id, embedded_worker_id)); |
| } |
| } |
| |
| bool ServiceWorkerNetworkProvider::IsControlledByServiceWorker() const { |
| return context() && context()->controller(); |
| } |
| |
| } // namespace content |