| // Copyright 2017 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/renderer/loader/web_worker_fetch_context_impl.h" |
| |
| #include <utility> |
| |
| #include "base/feature_list.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/task_scheduler/post_task.h" |
| #include "content/child/child_thread_impl.h" |
| #include "content/child/thread_safe_sender.h" |
| #include "content/common/content_constants_internal.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/service_worker/service_worker_provider.mojom.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/origin_util.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "content/public/renderer/url_loader_throttle_provider.h" |
| #include "content/public/renderer/websocket_handshake_throttle_provider.h" |
| #include "content/renderer/loader/request_extra_data.h" |
| #include "content/renderer/loader/resource_dispatcher.h" |
| #include "content/renderer/loader/web_url_loader_impl.h" |
| #include "content/renderer/loader/web_url_request_util.h" |
| #include "content/renderer/service_worker/controller_service_worker_connector.h" |
| #include "content/renderer/service_worker/service_worker_subresource_loader.h" |
| #include "mojo/public/cpp/bindings/binding.h" |
| #include "mojo/public/cpp/bindings/strong_binding.h" |
| #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "third_party/blink/public/common/service_worker/service_worker_utils.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" |
| #include "third_party/blink/public/platform/web_security_origin.h" |
| |
| namespace content { |
| |
| WebWorkerFetchContextImpl::RewriteURLFunction g_rewrite_url = nullptr; |
| |
| namespace { |
| |
| // Runs on IO thread. |
| void CreateSubresourceLoaderFactoryForWorker( |
| mojom::ServiceWorkerContainerHostPtrInfo container_host_info, |
| const std::string& client_id, |
| std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory, |
| network::mojom::URLLoaderFactoryRequest request, |
| scoped_refptr<base::SequencedTaskRunner> task_runner) { |
| ServiceWorkerSubresourceLoaderFactory::Create( |
| base::MakeRefCounted<ControllerServiceWorkerConnector>( |
| std::move(container_host_info), nullptr /* controller_ptr */, |
| client_id), |
| network::SharedURLLoaderFactory::Create(std::move(fallback_factory)), |
| std::move(request), std::move(task_runner)); |
| } |
| |
| } // namespace |
| |
| // static |
| void WebWorkerFetchContextImpl::InstallRewriteURLFunction( |
| RewriteURLFunction rewrite_url) { |
| CHECK(!g_rewrite_url); |
| g_rewrite_url = rewrite_url; |
| } |
| |
| // An implementation of WebURLLoaderFactory that is aware of service workers. In |
| // the usual case, it creates a loader that uses |loader_factory_|. But if the |
| // worker fetch context is controlled by a service worker, it creates a loader |
| // that uses |service_worker_loader_factory_| for requests that should be |
| // intercepted by the service worker. |
| class WebWorkerFetchContextImpl::Factory : public blink::WebURLLoaderFactory { |
| public: |
| Factory(base::WeakPtr<ResourceDispatcher> resource_dispatcher, |
| scoped_refptr<network::SharedURLLoaderFactory> loader_factory) |
| : resource_dispatcher_(std::move(resource_dispatcher)), |
| loader_factory_(std::move(loader_factory)), |
| weak_ptr_factory_(this) {} |
| ~Factory() override = default; |
| |
| std::unique_ptr<blink::WebURLLoader> CreateURLLoader( |
| const blink::WebURLRequest& request, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) override { |
| DCHECK(task_runner); |
| DCHECK(resource_dispatcher_); |
| if (auto loader = CreateServiceWorkerURLLoader(request, task_runner)) |
| return loader; |
| return std::make_unique<WebURLLoaderImpl>( |
| resource_dispatcher_.get(), std::move(task_runner), loader_factory_); |
| } |
| |
| void SetServiceWorkerURLLoaderFactory( |
| network::mojom::URLLoaderFactoryPtr service_worker_loader_factory) { |
| if (!service_worker_loader_factory) { |
| service_worker_loader_factory_ = nullptr; |
| return; |
| } |
| service_worker_loader_factory_ = |
| base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( |
| std::move(service_worker_loader_factory)); |
| } |
| |
| base::WeakPtr<Factory> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } |
| |
| private: |
| std::unique_ptr<blink::WebURLLoader> CreateServiceWorkerURLLoader( |
| const blink::WebURLRequest& request, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| // TODO(horo): Unify this code path with |
| // ServiceWorkerNetworkProvider::CreateURLLoader that is used for document |
| // cases. |
| |
| // We need the service worker loader factory populated in order to create |
| // our own URLLoader for subresource loading via a service worker. |
| if (!service_worker_loader_factory_) |
| return nullptr; |
| |
| // If the URL is not http(s) or otherwise whitelisted, do not intercept the |
| // request. Schemes like 'blob' and 'file' are not eligible to be |
| // intercepted by service workers. |
| // TODO(falken): Let ServiceWorkerSubresourceLoaderFactory handle the |
| // request and move this check there (i.e., for such URLs, it should use |
| // its fallback factory). |
| if (!GURL(request.Url()).SchemeIsHTTPOrHTTPS() && |
| !OriginCanAccessServiceWorkers(request.Url())) { |
| return nullptr; |
| } |
| |
| // If GetSkipServiceWorker() returns true, no need to intercept the request. |
| if (request.GetSkipServiceWorker()) |
| return nullptr; |
| |
| // Create our own URLLoader to route the request to the controller service |
| // worker. |
| return std::make_unique<WebURLLoaderImpl>(resource_dispatcher_.get(), |
| std::move(task_runner), |
| service_worker_loader_factory_); |
| } |
| |
| base::WeakPtr<ResourceDispatcher> resource_dispatcher_; |
| scoped_refptr<network::SharedURLLoaderFactory> loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> service_worker_loader_factory_; |
| base::WeakPtrFactory<Factory> weak_ptr_factory_; |
| DISALLOW_COPY_AND_ASSIGN(Factory); |
| }; |
| |
| WebWorkerFetchContextImpl::WebWorkerFetchContextImpl( |
| RendererPreferences renderer_preferences, |
| mojom::ServiceWorkerWorkerClientRequest service_worker_client_request, |
| mojom::ServiceWorkerWorkerClientRegistryPtrInfo |
| service_worker_worker_client_registry_info, |
| mojom::ServiceWorkerContainerHostPtrInfo service_worker_container_host_info, |
| std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info, |
| std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory_info, |
| std::unique_ptr<URLLoaderThrottleProvider> throttle_provider, |
| std::unique_ptr<WebSocketHandshakeThrottleProvider> |
| websocket_handshake_throttle_provider, |
| ThreadSafeSender* thread_safe_sender, |
| std::unique_ptr<service_manager::Connector> service_manager_connection) |
| : binding_(this), |
| service_worker_client_request_(std::move(service_worker_client_request)), |
| service_worker_worker_client_registry_info_( |
| std::move(service_worker_worker_client_registry_info)), |
| service_worker_container_host_info_( |
| std::move(service_worker_container_host_info)), |
| loader_factory_info_(std::move(loader_factory_info)), |
| fallback_factory_info_(std::move(fallback_factory_info)), |
| thread_safe_sender_(thread_safe_sender), |
| renderer_preferences_(std::move(renderer_preferences)), |
| throttle_provider_(std::move(throttle_provider)), |
| websocket_handshake_throttle_provider_( |
| std::move(websocket_handshake_throttle_provider)), |
| service_manager_connection_(std::move(service_manager_connection)) {} |
| |
| WebWorkerFetchContextImpl::~WebWorkerFetchContextImpl() {} |
| |
| void WebWorkerFetchContextImpl::SetTerminateSyncLoadEvent( |
| base::WaitableEvent* terminate_sync_load_event) { |
| DCHECK(!terminate_sync_load_event_); |
| terminate_sync_load_event_ = terminate_sync_load_event; |
| } |
| |
| std::unique_ptr<blink::WebWorkerFetchContext> |
| WebWorkerFetchContextImpl::CloneForNestedWorker() { |
| mojom::ServiceWorkerWorkerClientRequest service_worker_client_request; |
| mojom::ServiceWorkerWorkerClientPtr service_worker_client_ptr; |
| service_worker_client_request = mojo::MakeRequest(&service_worker_client_ptr); |
| service_worker_worker_client_registry_->RegisterWorkerClient( |
| std::move(service_worker_client_ptr)); |
| |
| mojom::ServiceWorkerWorkerClientRegistryPtrInfo |
| service_worker_worker_client_registry_ptr_info; |
| service_worker_worker_client_registry_->CloneWorkerClientRegistry( |
| mojo::MakeRequest(&service_worker_worker_client_registry_ptr_info)); |
| |
| mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info; |
| if (blink::ServiceWorkerUtils::IsServicificationEnabled()) { |
| service_worker_container_host_->CloneForWorker( |
| mojo::MakeRequest(&host_ptr_info)); |
| } |
| |
| auto new_context = std::make_unique<WebWorkerFetchContextImpl>( |
| renderer_preferences_, std::move(service_worker_client_request), |
| std::move(service_worker_worker_client_registry_ptr_info), |
| std::move(host_ptr_info), loader_factory_->Clone(), |
| fallback_factory_->Clone(), |
| throttle_provider_ ? throttle_provider_->Clone() : nullptr, |
| websocket_handshake_throttle_provider_ |
| ? websocket_handshake_throttle_provider_->Clone() |
| : nullptr, |
| thread_safe_sender_.get(), service_manager_connection_->Clone()); |
| new_context->service_worker_provider_id_ = service_worker_provider_id_; |
| new_context->is_controlled_by_service_worker_ = |
| is_controlled_by_service_worker_; |
| new_context->is_on_sub_frame_ = is_on_sub_frame_; |
| new_context->ancestor_frame_id_ = ancestor_frame_id_; |
| new_context->appcache_host_id_ = appcache_host_id_; |
| return new_context; |
| } |
| |
| void WebWorkerFetchContextImpl::InitializeOnWorkerThread() { |
| DCHECK(!resource_dispatcher_); |
| DCHECK(!binding_.is_bound()); |
| resource_dispatcher_ = std::make_unique<ResourceDispatcher>(); |
| resource_dispatcher_->set_terminate_sync_load_event( |
| terminate_sync_load_event_); |
| |
| loader_factory_ = |
| network::SharedURLLoaderFactory::Create(std::move(loader_factory_info_)); |
| fallback_factory_ = network::SharedURLLoaderFactory::Create( |
| std::move(fallback_factory_info_)); |
| if (service_worker_client_request_.is_pending()) |
| binding_.Bind(std::move(service_worker_client_request_)); |
| |
| service_worker_worker_client_registry_.Bind( |
| std::move(service_worker_worker_client_registry_info_)); |
| |
| if (blink::ServiceWorkerUtils::IsServicificationEnabled()) { |
| service_worker_container_host_.Bind( |
| std::move(service_worker_container_host_info_)); |
| |
| blink::mojom::BlobRegistryPtr blob_registry_ptr; |
| service_manager_connection_->BindInterface( |
| mojom::kBrowserServiceName, mojo::MakeRequest(&blob_registry_ptr)); |
| blob_registry_ = base::MakeRefCounted< |
| base::RefCountedData<blink::mojom::BlobRegistryPtr>>( |
| std::move(blob_registry_ptr)); |
| } |
| } |
| |
| std::unique_ptr<blink::WebURLLoaderFactory> |
| WebWorkerFetchContextImpl::CreateURLLoaderFactory() { |
| DCHECK(loader_factory_); |
| DCHECK(!web_loader_factory_); |
| auto factory = std::make_unique<Factory>(resource_dispatcher_->GetWeakPtr(), |
| loader_factory_); |
| web_loader_factory_ = factory->GetWeakPtr(); |
| |
| if (blink::ServiceWorkerUtils::IsServicificationEnabled()) |
| ResetServiceWorkerURLLoaderFactory(); |
| |
| return factory; |
| } |
| |
| std::unique_ptr<blink::WebURLLoaderFactory> |
| WebWorkerFetchContextImpl::WrapURLLoaderFactory( |
| mojo::ScopedMessagePipeHandle url_loader_factory_handle) { |
| return std::make_unique<WebURLLoaderFactoryImpl>( |
| resource_dispatcher_->GetWeakPtr(), |
| base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( |
| network::mojom::URLLoaderFactoryPtrInfo( |
| std::move(url_loader_factory_handle), |
| network::mojom::URLLoaderFactory::Version_))); |
| } |
| |
| void WebWorkerFetchContextImpl::WillSendRequest(blink::WebURLRequest& request) { |
| if (renderer_preferences_.enable_do_not_track) { |
| request.SetHTTPHeaderField(blink::WebString::FromUTF8(kDoNotTrackHeader), |
| "1"); |
| } |
| |
| auto extra_data = std::make_unique<RequestExtraData>(); |
| extra_data->set_service_worker_provider_id(service_worker_provider_id_); |
| extra_data->set_render_frame_id(ancestor_frame_id_); |
| extra_data->set_initiated_in_secure_context(is_secure_context_); |
| if (throttle_provider_) { |
| extra_data->set_url_loader_throttles(throttle_provider_->CreateThrottles( |
| ancestor_frame_id_, request, WebURLRequestToResourceType(request))); |
| } |
| request.SetExtraData(std::move(extra_data)); |
| request.SetAppCacheHostID(appcache_host_id_); |
| |
| if (IsControlledByServiceWorker() == |
| blink::mojom::ControllerServiceWorkerMode::kNoController) { |
| // TODO(falken): Is still this needed? It used to set kForeign for foreign |
| // fetch. |
| request.SetSkipServiceWorker(true); |
| } |
| |
| if (g_rewrite_url) |
| request.SetURL(g_rewrite_url(request.Url().GetString().Utf8(), false)); |
| |
| if (!renderer_preferences_.enable_referrers) { |
| request.SetHTTPReferrer(blink::WebString(), |
| blink::kWebReferrerPolicyDefault); |
| } |
| } |
| |
| blink::mojom::ControllerServiceWorkerMode |
| WebWorkerFetchContextImpl::IsControlledByServiceWorker() const { |
| return is_controlled_by_service_worker_; |
| } |
| |
| void WebWorkerFetchContextImpl::SetIsOnSubframe(bool is_on_sub_frame) { |
| is_on_sub_frame_ = is_on_sub_frame; |
| } |
| |
| bool WebWorkerFetchContextImpl::IsOnSubframe() const { |
| return is_on_sub_frame_; |
| } |
| |
| blink::WebURL WebWorkerFetchContextImpl::SiteForCookies() const { |
| return site_for_cookies_; |
| } |
| |
| void WebWorkerFetchContextImpl::DidRunContentWithCertificateErrors() { |
| Send(new FrameHostMsg_DidRunContentWithCertificateErrors(ancestor_frame_id_)); |
| } |
| |
| void WebWorkerFetchContextImpl::DidDisplayContentWithCertificateErrors() { |
| Send(new FrameHostMsg_DidDisplayContentWithCertificateErrors( |
| ancestor_frame_id_)); |
| } |
| |
| void WebWorkerFetchContextImpl::DidRunInsecureContent( |
| const blink::WebSecurityOrigin& origin, |
| const blink::WebURL& url) { |
| Send(new FrameHostMsg_DidRunInsecureContent( |
| ancestor_frame_id_, GURL(origin.ToString().Utf8()), url)); |
| } |
| |
| void WebWorkerFetchContextImpl::SetSubresourceFilterBuilder( |
| std::unique_ptr<blink::WebDocumentSubresourceFilter::Builder> |
| subresource_filter_builder) { |
| subresource_filter_builder_ = std::move(subresource_filter_builder); |
| } |
| |
| std::unique_ptr<blink::WebDocumentSubresourceFilter> |
| WebWorkerFetchContextImpl::TakeSubresourceFilter() { |
| if (!subresource_filter_builder_) |
| return nullptr; |
| return std::move(subresource_filter_builder_)->Build(); |
| } |
| |
| std::unique_ptr<blink::WebSocketHandshakeThrottle> |
| WebWorkerFetchContextImpl::CreateWebSocketHandshakeThrottle() { |
| if (!websocket_handshake_throttle_provider_) |
| return nullptr; |
| return websocket_handshake_throttle_provider_->CreateThrottle( |
| ancestor_frame_id_); |
| } |
| |
| void WebWorkerFetchContextImpl::set_service_worker_provider_id(int id) { |
| service_worker_provider_id_ = id; |
| } |
| |
| void WebWorkerFetchContextImpl::set_is_controlled_by_service_worker( |
| blink::mojom::ControllerServiceWorkerMode mode) { |
| is_controlled_by_service_worker_ = mode; |
| } |
| |
| void WebWorkerFetchContextImpl::set_ancestor_frame_id(int id) { |
| ancestor_frame_id_ = id; |
| } |
| |
| void WebWorkerFetchContextImpl::set_site_for_cookies( |
| const blink::WebURL& site_for_cookies) { |
| site_for_cookies_ = site_for_cookies; |
| } |
| |
| void WebWorkerFetchContextImpl::set_is_secure_context(bool flag) { |
| is_secure_context_ = flag; |
| } |
| |
| void WebWorkerFetchContextImpl::set_origin_url(const GURL& origin_url) { |
| origin_url_ = origin_url; |
| } |
| |
| void WebWorkerFetchContextImpl::set_client_id(const std::string& client_id) { |
| client_id_ = client_id; |
| } |
| |
| void WebWorkerFetchContextImpl::SetApplicationCacheHostID(int id) { |
| appcache_host_id_ = id; |
| } |
| |
| int WebWorkerFetchContextImpl::ApplicationCacheHostID() const { |
| return appcache_host_id_; |
| } |
| |
| void WebWorkerFetchContextImpl::OnControllerChanged( |
| blink::mojom::ControllerServiceWorkerMode mode) { |
| set_is_controlled_by_service_worker(mode); |
| |
| if (blink::ServiceWorkerUtils::IsServicificationEnabled()) |
| ResetServiceWorkerURLLoaderFactory(); |
| } |
| |
| bool WebWorkerFetchContextImpl::Send(IPC::Message* message) { |
| return thread_safe_sender_->Send(message); |
| } |
| |
| void WebWorkerFetchContextImpl::ResetServiceWorkerURLLoaderFactory() { |
| DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled()); |
| if (!web_loader_factory_) |
| return; |
| if (IsControlledByServiceWorker() != |
| blink::mojom::ControllerServiceWorkerMode::kControlled) { |
| web_loader_factory_->SetServiceWorkerURLLoaderFactory(nullptr); |
| return; |
| } |
| |
| network::mojom::URLLoaderFactoryPtr service_worker_url_loader_factory; |
| mojom::ServiceWorkerContainerHostPtrInfo host_ptr_info; |
| service_worker_container_host_->CloneForWorker( |
| mojo::MakeRequest(&host_ptr_info)); |
| // To avoid potential dead-lock while synchronous loading, create the |
| // SubresourceLoaderFactory on a background thread. |
| auto task_runner = base::CreateSequencedTaskRunnerWithTraits( |
| {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); |
| task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &CreateSubresourceLoaderFactoryForWorker, std::move(host_ptr_info), |
| client_id_, fallback_factory_->Clone(), |
| mojo::MakeRequest(&service_worker_url_loader_factory), task_runner)); |
| web_loader_factory_->SetServiceWorkerURLLoaderFactory( |
| std::move(service_worker_url_loader_factory)); |
| } |
| |
| } // namespace content |