| // 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/service_worker/service_worker_provider_context.h" |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/macros.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "content/child/thread_safe_sender.h" |
| #include "content/common/service_worker/service_worker_container.mojom.h" |
| #include "content/common/service_worker/service_worker_types.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/resource_type.h" |
| #include "content/renderer/service_worker/controller_service_worker_connector.h" |
| #include "content/renderer/service_worker/web_service_worker_impl.h" |
| #include "content/renderer/service_worker/web_service_worker_registration_impl.h" |
| #include "mojo/public/cpp/bindings/associated_binding_set.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" |
| #include "services/network/public/mojom/url_loader_factory.mojom.h" |
| #include "services/network/test/test_url_loader_client.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" |
| #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h" |
| #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" |
| #include "third_party/blink/public/platform/web_feature.mojom.h" |
| |
| namespace content { |
| namespace service_worker_provider_context_unittest { |
| |
| class MockServiceWorkerObjectHost |
| : public blink::mojom::ServiceWorkerObjectHost { |
| public: |
| explicit MockServiceWorkerObjectHost(int64_t version_id) |
| : version_id_(version_id) { |
| bindings_.set_connection_error_handler( |
| base::BindRepeating(&MockServiceWorkerObjectHost::OnConnectionError, |
| base::Unretained(this))); |
| } |
| ~MockServiceWorkerObjectHost() override = default; |
| |
| blink::mojom::ServiceWorkerObjectInfoPtr CreateObjectInfo() { |
| auto info = blink::mojom::ServiceWorkerObjectInfo::New(); |
| info->version_id = version_id_; |
| bindings_.AddBinding(this, mojo::MakeRequest(&info->host_ptr_info)); |
| info->request = mojo::MakeRequest(&remote_object_); |
| return info; |
| } |
| |
| void OnConnectionError() { |
| if (error_callback_) |
| std::move(error_callback_).Run(); |
| } |
| |
| void RunOnConnectionError(base::OnceClosure error_callback) { |
| DCHECK(!error_callback_); |
| error_callback_ = std::move(error_callback); |
| } |
| |
| int GetBindingCount() const { return bindings_.size(); } |
| |
| private: |
| // Implements blink::mojom::ServiceWorkerObjectHost. |
| void PostMessageToServiceWorker( |
| ::blink::TransferableMessage message) override { |
| NOTREACHED(); |
| } |
| void TerminateForTesting(TerminateForTestingCallback callback) override { |
| NOTREACHED(); |
| } |
| |
| const int64_t version_id_; |
| mojo::AssociatedBindingSet<blink::mojom::ServiceWorkerObjectHost> bindings_; |
| blink::mojom::ServiceWorkerObjectAssociatedPtr remote_object_; |
| base::OnceClosure error_callback_; |
| }; |
| |
| class MockServiceWorkerRegistrationObjectHost |
| : public blink::mojom::ServiceWorkerRegistrationObjectHost { |
| public: |
| explicit MockServiceWorkerRegistrationObjectHost(int64_t registration_id) |
| : registration_id_(registration_id) { |
| bindings_.set_connection_error_handler( |
| base::Bind(&MockServiceWorkerRegistrationObjectHost::OnConnectionError, |
| base::Unretained(this))); |
| } |
| ~MockServiceWorkerRegistrationObjectHost() override = default; |
| |
| blink::mojom::ServiceWorkerRegistrationObjectInfoPtr CreateObjectInfo( |
| MockServiceWorkerObjectHost* active, |
| MockServiceWorkerObjectHost* waiting, |
| MockServiceWorkerObjectHost* installing) { |
| auto info = blink::mojom::ServiceWorkerRegistrationObjectInfo::New(); |
| info->registration_id = registration_id_; |
| bindings_.AddBinding(this, mojo::MakeRequest(&info->host_ptr_info)); |
| info->request = mojo::MakeRequest(&remote_registration_); |
| |
| info->active = active->CreateObjectInfo(); |
| info->waiting = waiting->CreateObjectInfo(); |
| info->installing = installing->CreateObjectInfo(); |
| return info; |
| } |
| |
| int GetBindingCount() const { return bindings_.size(); } |
| |
| private: |
| // Implements blink::mojom::ServiceWorkerRegistrationObjectHost. |
| void Update(UpdateCallback callback) override { |
| std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kNone, |
| base::nullopt); |
| } |
| void Unregister(UnregisterCallback callback) override { |
| std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kNone, |
| base::nullopt); |
| } |
| void EnableNavigationPreload( |
| bool enable, |
| EnableNavigationPreloadCallback callback) override { |
| std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kNone, |
| base::nullopt); |
| } |
| void GetNavigationPreloadState( |
| GetNavigationPreloadStateCallback callback) override { |
| std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kNone, |
| base::nullopt, nullptr); |
| } |
| void SetNavigationPreloadHeader( |
| const std::string& value, |
| SetNavigationPreloadHeaderCallback callback) override { |
| std::move(callback).Run(blink::mojom::ServiceWorkerErrorType::kNone, |
| base::nullopt); |
| } |
| |
| void OnConnectionError() { |
| // If there are still bindings, |this| is still being used. |
| if (!bindings_.empty()) |
| return; |
| // Will destroy corresponding remote WebServiceWorkerRegistrationImpl |
| // instance. |
| remote_registration_.reset(); |
| } |
| |
| int64_t registration_id_; |
| mojo::AssociatedBindingSet<blink::mojom::ServiceWorkerRegistrationObjectHost> |
| bindings_; |
| blink::mojom::ServiceWorkerRegistrationObjectAssociatedPtr |
| remote_registration_; |
| }; |
| |
| class MockWebServiceWorkerProviderClientImpl |
| : public blink::WebServiceWorkerProviderClient { |
| public: |
| MockWebServiceWorkerProviderClientImpl() {} |
| |
| ~MockWebServiceWorkerProviderClientImpl() override {} |
| |
| void SetController(std::unique_ptr<blink::WebServiceWorker::Handle> handle, |
| bool should_notify_controller_change) override { |
| was_set_controller_called_ = true; |
| } |
| |
| void DispatchMessageEvent( |
| std::unique_ptr<blink::WebServiceWorker::Handle> handle, |
| blink::TransferableMessage message) override { |
| was_dispatch_message_event_called_ = true; |
| } |
| |
| void CountFeature(blink::mojom::WebFeature feature) override { |
| used_features_.insert(feature); |
| } |
| |
| bool was_set_controller_called() const { return was_set_controller_called_; } |
| |
| bool was_dispatch_message_event_called() const { |
| return was_dispatch_message_event_called_; |
| } |
| |
| const std::set<blink::mojom::WebFeature>& used_features() const { |
| return used_features_; |
| } |
| |
| private: |
| bool was_set_controller_called_ = false; |
| bool was_dispatch_message_event_called_ = false; |
| std::set<blink::mojom::WebFeature> used_features_; |
| }; |
| |
| // S13nServiceWorker: a fake URLLoaderFactory implementation that basically |
| // does nothing but records the requests. |
| class FakeURLLoaderFactory final : public network::mojom::URLLoaderFactory { |
| public: |
| FakeURLLoaderFactory() = default; |
| ~FakeURLLoaderFactory() override = default; |
| |
| void AddBinding(network::mojom::URLLoaderFactoryRequest request) { |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| // network::mojom::URLLoaderFactory: |
| void CreateLoaderAndStart(network::mojom::URLLoaderRequest request, |
| int32_t routing_id, |
| int32_t request_id, |
| uint32_t options, |
| const network::ResourceRequest& url_request, |
| network::mojom::URLLoaderClientPtr client, |
| const net::MutableNetworkTrafficAnnotationTag& |
| traffic_annotation) override { |
| // Does nothing, but just record the request and hold the client (to avoid |
| // connection errors). |
| last_url_ = url_request.url; |
| clients_.push_back(std::move(client)); |
| if (start_loader_callback_) |
| std::move(start_loader_callback_).Run(); |
| } |
| void Clone(network::mojom::URLLoaderFactoryRequest factory) override { |
| bindings_.AddBinding(this, std::move(factory)); |
| } |
| |
| void set_start_loader_callback(base::OnceClosure closure) { |
| start_loader_callback_ = std::move(closure); |
| } |
| |
| size_t clients_count() const { return clients_.size(); } |
| GURL last_request_url() const { return last_url_; } |
| |
| private: |
| mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_; |
| std::vector<network::mojom::URLLoaderClientPtr> clients_; |
| base::OnceClosure start_loader_callback_; |
| GURL last_url_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeURLLoaderFactory); |
| }; |
| |
| // S13nServiceWorker: a fake ControllerServiceWorker implementation that |
| // basically does nothing but records DispatchFetchEvent calls. |
| class FakeControllerServiceWorker : public mojom::ControllerServiceWorker { |
| public: |
| FakeControllerServiceWorker() = default; |
| ~FakeControllerServiceWorker() override = default; |
| |
| // mojom::ControllerServiceWorker: |
| void DispatchFetchEvent( |
| blink::mojom::DispatchFetchEventParamsPtr params, |
| blink::mojom::ServiceWorkerFetchResponseCallbackPtr response_callback, |
| DispatchFetchEventCallback callback) override { |
| fetch_event_count_++; |
| fetch_event_request_ = params->request; |
| std::move(callback).Run(blink::mojom::ServiceWorkerEventStatus::COMPLETED, |
| base::TimeTicks()); |
| if (fetch_event_callback_) |
| std::move(fetch_event_callback_).Run(); |
| } |
| void Clone(mojom::ControllerServiceWorkerRequest request) override { |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| void set_fetch_callback(base::OnceClosure closure) { |
| fetch_event_callback_ = std::move(closure); |
| } |
| int fetch_event_count() const { return fetch_event_count_; } |
| const network::ResourceRequest& fetch_event_request() const { |
| return fetch_event_request_; |
| } |
| |
| void Disconnect() { bindings_.CloseAllBindings(); } |
| |
| private: |
| int fetch_event_count_ = 0; |
| network::ResourceRequest fetch_event_request_; |
| base::OnceClosure fetch_event_callback_; |
| mojo::BindingSet<mojom::ControllerServiceWorker> bindings_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeControllerServiceWorker); |
| }; |
| |
| class FakeServiceWorkerContainerHost |
| : public mojom::ServiceWorkerContainerHost { |
| public: |
| explicit FakeServiceWorkerContainerHost( |
| mojom::ServiceWorkerContainerHostAssociatedRequest request) |
| : associated_binding_(this, std::move(request)) {} |
| ~FakeServiceWorkerContainerHost() override = default; |
| |
| // Implements mojom::ServiceWorkerContainerHost. |
| void Register(const GURL& script_url, |
| blink::mojom::ServiceWorkerRegistrationOptionsPtr options, |
| RegisterCallback callback) override { |
| NOTIMPLEMENTED(); |
| } |
| void GetRegistration(const GURL& client_url, |
| GetRegistrationCallback callback) override { |
| NOTIMPLEMENTED(); |
| } |
| void GetRegistrations(GetRegistrationsCallback callback) override { |
| NOTIMPLEMENTED(); |
| } |
| void GetRegistrationForReady( |
| GetRegistrationForReadyCallback callback) override { |
| NOTIMPLEMENTED(); |
| } |
| void EnsureControllerServiceWorker( |
| mojom::ControllerServiceWorkerRequest request, |
| mojom::ControllerServiceWorkerPurpose purpose) override { |
| NOTIMPLEMENTED(); |
| } |
| void CloneForWorker( |
| mojom::ServiceWorkerContainerHostRequest request) override { |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| void Ping(PingCallback callback) override { NOTIMPLEMENTED(); } |
| void HintToUpdateServiceWorker() override { NOTIMPLEMENTED(); } |
| |
| private: |
| mojo::BindingSet<mojom::ServiceWorkerContainerHost> bindings_; |
| mojo::AssociatedBinding<mojom::ServiceWorkerContainerHost> |
| associated_binding_; |
| DISALLOW_COPY_AND_ASSIGN(FakeServiceWorkerContainerHost); |
| }; |
| |
| class ServiceWorkerProviderContextTest : public testing::Test { |
| public: |
| ServiceWorkerProviderContextTest() = default; |
| |
| void EnableS13nServiceWorker() { |
| scoped_feature_list_.InitAndEnableFeature( |
| network::features::kNetworkService); |
| network::mojom::URLLoaderFactoryPtr fake_loader_factory; |
| fake_loader_factory_.AddBinding(MakeRequest(&fake_loader_factory)); |
| loader_factory_ = |
| base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( |
| std::move(fake_loader_factory)); |
| } |
| |
| void StartRequest(network::mojom::URLLoaderFactory* factory, |
| const GURL& url) { |
| network::ResourceRequest request; |
| request.url = url; |
| request.resource_type = static_cast<int>(RESOURCE_TYPE_SUB_RESOURCE); |
| network::mojom::URLLoaderPtr loader; |
| network::TestURLLoaderClient loader_client; |
| factory->CreateLoaderAndStart( |
| mojo::MakeRequest(&loader), 0, 0, network::mojom::kURLLoadOptionNone, |
| request, loader_client.CreateInterfacePtr(), |
| net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); |
| } |
| |
| bool ContainsRegistration(ServiceWorkerProviderContext* provider_context, |
| int64_t registration_id) { |
| return provider_context->ContainsServiceWorkerRegistrationObjectForTesting( |
| registration_id); |
| } |
| |
| bool ContainsServiceWorker(ServiceWorkerProviderContext* provider_context, |
| int64_t version_id) { |
| return provider_context->ContainsServiceWorkerObjectForTesting(version_id); |
| } |
| |
| void FlushControllerConnector( |
| ServiceWorkerProviderContext* provider_context) { |
| provider_context->state_for_client_->controller_connector.FlushForTesting(); |
| } |
| |
| protected: |
| base::test::ScopedTaskEnvironment task_environment; |
| |
| // S13nServiceWorker: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| FakeURLLoaderFactory fake_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> loader_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderContextTest); |
| }; |
| |
| TEST_F(ServiceWorkerProviderContextTest, SetController) { |
| const int kProviderId = 10; |
| |
| { |
| auto mock_service_worker_object_host = |
| std::make_unique<MockServiceWorkerObjectHost>(200 /* version_id */); |
| ASSERT_EQ(0, mock_service_worker_object_host->GetBindingCount()); |
| blink::mojom::ServiceWorkerObjectInfoPtr object_info = |
| mock_service_worker_object_host->CreateObjectInfo(); |
| EXPECT_EQ(1, mock_service_worker_object_host->GetBindingCount()); |
| |
| // (1) In the case there is no WebSWProviderClient but SWProviderContext for |
| // the provider, the passed reference should be adopted and owned by the |
| // provider context. |
| mojom::ServiceWorkerContainerAssociatedPtr container_ptr; |
| mojom::ServiceWorkerContainerAssociatedRequest container_request = |
| mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr); |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, |
| std::move(container_request), nullptr /* host_ptr_info */, |
| nullptr /* controller_info */, nullptr /* loader_factory*/); |
| |
| auto info = mojom::ControllerServiceWorkerInfo::New(); |
| info->mode = blink::mojom::ControllerServiceWorkerMode::kControlled; |
| info->object_info = std::move(object_info); |
| container_ptr->SetController(std::move(info), |
| std::vector<blink::mojom::WebFeature>(), true); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Destruction of the provider context should release references to the |
| // the controller. |
| provider_context = nullptr; |
| base::RunLoop().RunUntilIdle(); |
| // ServiceWorkerObjectHost Mojo connection got broken. |
| EXPECT_EQ(0, mock_service_worker_object_host->GetBindingCount()); |
| } |
| |
| { |
| auto mock_service_worker_object_host = |
| std::make_unique<MockServiceWorkerObjectHost>(201 /* version_id */); |
| ASSERT_EQ(0, mock_service_worker_object_host->GetBindingCount()); |
| blink::mojom::ServiceWorkerObjectInfoPtr object_info = |
| mock_service_worker_object_host->CreateObjectInfo(); |
| EXPECT_EQ(1, mock_service_worker_object_host->GetBindingCount()); |
| |
| // (2) In the case there are both SWProviderContext and SWProviderClient for |
| // the provider, the passed reference should be adopted by the provider |
| // context and then be transfered ownership to the provider client, after |
| // that due to limitation of the mock implementation, the reference |
| // immediately gets released. |
| mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info; |
| mojom::ServiceWorkerContainerHostAssociatedRequest host_request = |
| mojo::MakeRequest(&host_ptr_info); |
| |
| mojom::ServiceWorkerContainerAssociatedPtr container_ptr; |
| mojom::ServiceWorkerContainerAssociatedRequest container_request = |
| mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr); |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, |
| std::move(container_request), std::move(host_ptr_info), |
| nullptr /* controller_info */, nullptr /* loader_factory*/); |
| auto provider_impl = |
| std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get()); |
| auto client = std::make_unique<MockWebServiceWorkerProviderClientImpl>(); |
| provider_impl->SetClient(client.get()); |
| ASSERT_FALSE(client->was_set_controller_called()); |
| |
| auto info = mojom::ControllerServiceWorkerInfo::New(); |
| info->mode = blink::mojom::ControllerServiceWorkerMode::kControlled; |
| info->object_info = std::move(object_info); |
| container_ptr->SetController(std::move(info), |
| std::vector<blink::mojom::WebFeature>(), true); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(client->was_set_controller_called()); |
| // ServiceWorkerObjectHost Mojo connection got broken. |
| EXPECT_EQ(0, mock_service_worker_object_host->GetBindingCount()); |
| } |
| } |
| |
| // Test that clearing the controller by sending a nullptr object info results in |
| // the provider context having a null controller. |
| TEST_F(ServiceWorkerProviderContextTest, SetController_Null) { |
| const int kProviderId = 10; |
| |
| mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info; |
| mojom::ServiceWorkerContainerHostAssociatedRequest host_request = |
| mojo::MakeRequest(&host_ptr_info); |
| |
| mojom::ServiceWorkerContainerAssociatedPtr container_ptr; |
| mojom::ServiceWorkerContainerAssociatedRequest container_request = |
| mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr); |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, |
| std::move(container_request), std::move(host_ptr_info), |
| nullptr /* controller_info */, nullptr /* loader_factory*/); |
| auto provider_impl = |
| std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get()); |
| auto client = std::make_unique<MockWebServiceWorkerProviderClientImpl>(); |
| provider_impl->SetClient(client.get()); |
| |
| container_ptr->SetController(mojom::ControllerServiceWorkerInfo::New(), |
| std::vector<blink::mojom::WebFeature>(), true); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_FALSE(provider_context->TakeController()); |
| EXPECT_TRUE(client->was_set_controller_called()); |
| } |
| |
| // S13nServiceWorker: Test that SetController correctly sets (or resets) |
| // the controller service worker for clients. |
| TEST_F(ServiceWorkerProviderContextTest, SetControllerServiceWorker) { |
| EnableS13nServiceWorker(); |
| const int kProviderId = 10; |
| |
| // Make the ServiceWorkerContainerHost implementation and |
| // ServiceWorkerContainer request. |
| mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr; |
| FakeServiceWorkerContainerHost host( |
| mojo::MakeRequestAssociatedWithDedicatedPipe(&host_ptr)); |
| mojom::ServiceWorkerContainerAssociatedPtr container_ptr; |
| mojom::ServiceWorkerContainerAssociatedRequest container_request = |
| mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr); |
| |
| // (1) Test if setting the controller via the CTOR works. |
| |
| // Make the object host for .controller. |
| auto object_host1 = |
| std::make_unique<MockServiceWorkerObjectHost>(200 /* version_id */); |
| EXPECT_EQ(0, object_host1->GetBindingCount()); |
| blink::mojom::ServiceWorkerObjectInfoPtr object_info1 = |
| object_host1->CreateObjectInfo(); |
| EXPECT_EQ(1, object_host1->GetBindingCount()); |
| |
| // Make the ControllerServiceWorkerInfo. |
| FakeControllerServiceWorker fake_controller1; |
| auto controller_info1 = mojom::ControllerServiceWorkerInfo::New(); |
| mojom::ControllerServiceWorkerPtr controller_ptr1; |
| fake_controller1.Clone(mojo::MakeRequest(&controller_ptr1)); |
| controller_info1->mode = |
| blink::mojom::ControllerServiceWorkerMode::kControlled; |
| controller_info1->object_info = std::move(object_info1); |
| controller_info1->endpoint = controller_ptr1.PassInterface(); |
| |
| // Make the ServiceWorkerProviderContext, pasing it the controller, container, |
| // and container host. |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, |
| std::move(container_request), host_ptr.PassInterface(), |
| std::move(controller_info1), loader_factory_); |
| |
| // The subresource loader factory must be available. |
| network::mojom::URLLoaderFactory* subresource_loader_factory1 = |
| provider_context->GetSubresourceLoaderFactory(); |
| ASSERT_NE(nullptr, subresource_loader_factory1); |
| |
| // Performing a request should reach the controller. |
| const GURL kURL1("https://www.example.com/foo.png"); |
| base::RunLoop loop1; |
| fake_controller1.set_fetch_callback(loop1.QuitClosure()); |
| StartRequest(subresource_loader_factory1, kURL1); |
| loop1.Run(); |
| EXPECT_EQ(kURL1, fake_controller1.fetch_event_request().url); |
| EXPECT_EQ(1, fake_controller1.fetch_event_count()); |
| |
| // (2) Test if resetting the controller to a new one via SetController |
| // works. |
| |
| // Setup the new controller. |
| auto object_host2 = |
| std::make_unique<MockServiceWorkerObjectHost>(201 /* version_id */); |
| ASSERT_EQ(0, object_host2->GetBindingCount()); |
| blink::mojom::ServiceWorkerObjectInfoPtr object_info2 = |
| object_host2->CreateObjectInfo(); |
| EXPECT_EQ(1, object_host2->GetBindingCount()); |
| FakeControllerServiceWorker fake_controller2; |
| auto controller_info2 = mojom::ControllerServiceWorkerInfo::New(); |
| mojom::ControllerServiceWorkerPtr controller_ptr2; |
| fake_controller2.Clone(mojo::MakeRequest(&controller_ptr2)); |
| controller_info2->mode = |
| blink::mojom::ControllerServiceWorkerMode::kControlled; |
| controller_info2->object_info = std::move(object_info2); |
| controller_info2->endpoint = controller_ptr2.PassInterface(); |
| |
| // Resetting the controller will trigger many things happening, including the |
| // object binding being broken. |
| base::RunLoop drop_binding_loop; |
| object_host1->RunOnConnectionError(drop_binding_loop.QuitClosure()); |
| container_ptr->SetController(std::move(controller_info2), |
| std::vector<blink::mojom::WebFeature>(), true); |
| container_ptr.FlushForTesting(); |
| drop_binding_loop.Run(); |
| EXPECT_EQ(0, object_host1->GetBindingCount()); |
| |
| // Subresource loader factory must be available, and should be the same |
| // one as we got before. |
| network::mojom::URLLoaderFactory* subresource_loader_factory2 = |
| provider_context->GetSubresourceLoaderFactory(); |
| ASSERT_NE(nullptr, subresource_loader_factory2); |
| EXPECT_EQ(subresource_loader_factory1, subresource_loader_factory2); |
| |
| // The SetController() call results in another Mojo call to |
| // ControllerServiceWorkerConnector.UpdateController(). Flush that interface |
| // pointer to ensure the message was received. |
| FlushControllerConnector(provider_context.get()); |
| |
| // Performing a request should reach the new controller. |
| const GURL kURL2("https://www.example.com/foo2.png"); |
| base::RunLoop loop2; |
| fake_controller2.set_fetch_callback(loop2.QuitClosure()); |
| StartRequest(subresource_loader_factory2, kURL2); |
| loop2.Run(); |
| EXPECT_EQ(kURL2, fake_controller2.fetch_event_request().url); |
| EXPECT_EQ(1, fake_controller2.fetch_event_count()); |
| // The request should not go to the previous controller. |
| EXPECT_EQ(1, fake_controller1.fetch_event_count()); |
| |
| // (3) Test if resetting the controller to nullptr works. |
| base::RunLoop drop_binding_loop2; |
| object_host2->RunOnConnectionError(drop_binding_loop2.QuitClosure()); |
| container_ptr->SetController(mojom::ControllerServiceWorkerInfo::New(), |
| std::vector<blink::mojom::WebFeature>(), true); |
| |
| // The controller is reset. References to the old controller must be |
| // released. |
| container_ptr.FlushForTesting(); |
| drop_binding_loop2.Run(); |
| EXPECT_EQ(0, object_host2->GetBindingCount()); |
| |
| // Subresource loader factory must not be available. |
| EXPECT_EQ(nullptr, provider_context->GetSubresourceLoaderFactory()); |
| |
| // The SetController() call results in another Mojo call to |
| // ControllerServiceWorkerConnector.UpdateController(). Flush that interface |
| // pointer to ensure the message was received. |
| FlushControllerConnector(provider_context.get()); |
| |
| // Performing a request using the subresource factory obtained before |
| // falls back to the network. |
| const GURL kURL3("https://www.example.com/foo3.png"); |
| base::RunLoop loop3; |
| fake_loader_factory_.set_start_loader_callback(loop3.QuitClosure()); |
| EXPECT_EQ(0UL, fake_loader_factory_.clients_count()); |
| StartRequest(subresource_loader_factory2, kURL3); |
| loop3.Run(); |
| EXPECT_EQ(kURL3, fake_loader_factory_.last_request_url()); |
| EXPECT_EQ(1UL, fake_loader_factory_.clients_count()); |
| |
| // The request should not go to the previous controllers. |
| EXPECT_EQ(1, fake_controller1.fetch_event_count()); |
| EXPECT_EQ(1, fake_controller2.fetch_event_count()); |
| |
| // (4) Test if resetting the controller to yet another one via SetController |
| // works. |
| auto object_host4 = |
| std::make_unique<MockServiceWorkerObjectHost>(202 /* version_id */); |
| ASSERT_EQ(0, object_host4->GetBindingCount()); |
| blink::mojom::ServiceWorkerObjectInfoPtr object_info4 = |
| object_host4->CreateObjectInfo(); |
| EXPECT_EQ(1, object_host4->GetBindingCount()); |
| FakeControllerServiceWorker fake_controller4; |
| auto controller_info4 = mojom::ControllerServiceWorkerInfo::New(); |
| mojom::ControllerServiceWorkerPtr controller_ptr4; |
| fake_controller4.Clone(mojo::MakeRequest(&controller_ptr4)); |
| controller_info4->mode = |
| blink::mojom::ControllerServiceWorkerMode::kControlled; |
| controller_info4->object_info = std::move(object_info4); |
| controller_info4->endpoint = controller_ptr4.PassInterface(); |
| container_ptr->SetController(std::move(controller_info4), |
| std::vector<blink::mojom::WebFeature>(), true); |
| container_ptr.FlushForTesting(); |
| |
| // Subresource loader factory must be available. |
| auto* subresource_loader_factory4 = |
| provider_context->GetSubresourceLoaderFactory(); |
| ASSERT_NE(nullptr, subresource_loader_factory4); |
| |
| // The SetController() call results in another Mojo call to |
| // ControllerServiceWorkerConnector.UpdateController(). Flush that interface |
| // pointer to ensure the message was received. |
| FlushControllerConnector(provider_context.get()); |
| |
| // Performing a request should reach the new controller. |
| const GURL kURL4("https://www.example.com/foo4.png"); |
| base::RunLoop loop4; |
| fake_controller4.set_fetch_callback(loop4.QuitClosure()); |
| StartRequest(subresource_loader_factory4, kURL4); |
| loop4.Run(); |
| EXPECT_EQ(kURL4, fake_controller4.fetch_event_request().url); |
| EXPECT_EQ(1, fake_controller4.fetch_event_count()); |
| |
| // The request should not go to the previous controllers. |
| EXPECT_EQ(1, fake_controller1.fetch_event_count()); |
| EXPECT_EQ(1, fake_controller2.fetch_event_count()); |
| // The request should not go to the network. |
| EXPECT_EQ(1UL, fake_loader_factory_.clients_count()); |
| |
| // Perform a request again, but then drop the controller connection. |
| // The outcome is not deterministic but should not crash. |
| StartRequest(subresource_loader_factory4, kURL4); |
| fake_controller4.Disconnect(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ServiceWorkerProviderContextTest, ControllerWithoutFetchHandler) { |
| EnableS13nServiceWorker(); |
| const int kProviderId = 10; |
| auto object_host = |
| std::make_unique<MockServiceWorkerObjectHost>(200 /* version_id */); |
| |
| // Set a controller without ControllerServiceWorker ptr to emulate no |
| // fetch event handler. |
| blink::mojom::ServiceWorkerObjectInfoPtr object_info = |
| object_host->CreateObjectInfo(); |
| auto controller_info = mojom::ControllerServiceWorkerInfo::New(); |
| mojom::ControllerServiceWorkerPtr controller_ptr; |
| controller_info->mode = |
| blink::mojom::ControllerServiceWorkerMode::kNoFetchEventHandler; |
| controller_info->object_info = std::move(object_info); |
| |
| mojom::ServiceWorkerContainerAssociatedPtr container_ptr; |
| mojom::ServiceWorkerContainerAssociatedRequest container_request = |
| mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr); |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, |
| std::move(container_request), nullptr /* host_ptr_info */, |
| std::move(controller_info), loader_factory_); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Subresource loader factory must not be available. |
| EXPECT_EQ(nullptr, provider_context->GetSubresourceLoaderFactory()); |
| } |
| |
| TEST_F(ServiceWorkerProviderContextTest, PostMessageToClient) { |
| const int kProviderId = 10; |
| |
| auto mock_service_worker_object_host = |
| std::make_unique<MockServiceWorkerObjectHost>(200 /* version_id */); |
| ASSERT_EQ(0, mock_service_worker_object_host->GetBindingCount()); |
| blink::mojom::ServiceWorkerObjectInfoPtr object_info = |
| mock_service_worker_object_host->CreateObjectInfo(); |
| EXPECT_EQ(1, mock_service_worker_object_host->GetBindingCount()); |
| |
| mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info; |
| mojom::ServiceWorkerContainerHostAssociatedRequest host_request = |
| mojo::MakeRequest(&host_ptr_info); |
| |
| mojom::ServiceWorkerContainerAssociatedPtr container_ptr; |
| mojom::ServiceWorkerContainerAssociatedRequest container_request = |
| mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr); |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, |
| std::move(container_request), std::move(host_ptr_info), |
| nullptr /* controller_info */, nullptr /* loader_factory*/); |
| auto provider_impl = |
| std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get()); |
| auto client = std::make_unique<MockWebServiceWorkerProviderClientImpl>(); |
| provider_impl->SetClient(client.get()); |
| ASSERT_FALSE(client->was_dispatch_message_event_called()); |
| |
| container_ptr->PostMessageToClient(std::move(object_info), |
| blink::TransferableMessage()); |
| base::RunLoop().RunUntilIdle(); |
| |
| // The passed reference should be owned by the provider client (but the |
| // reference is immediately released by the mock provider client). |
| EXPECT_TRUE(client->was_dispatch_message_event_called()); |
| EXPECT_EQ(0, mock_service_worker_object_host->GetBindingCount()); |
| } |
| |
| TEST_F(ServiceWorkerProviderContextTest, CountFeature) { |
| const int kProviderId = 10; |
| |
| mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info; |
| mojom::ServiceWorkerContainerHostAssociatedRequest host_request = |
| mojo::MakeRequest(&host_ptr_info); |
| |
| mojom::ServiceWorkerContainerAssociatedPtr container_ptr; |
| mojom::ServiceWorkerContainerAssociatedRequest container_request = |
| mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr); |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, |
| std::move(container_request), std::move(host_ptr_info), |
| nullptr /* controller_info */, nullptr /* loader_factory*/); |
| auto provider_impl = |
| std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get()); |
| auto client = std::make_unique<MockWebServiceWorkerProviderClientImpl>(); |
| |
| container_ptr->CountFeature(blink::mojom::WebFeature::kWorkerStart); |
| provider_impl->SetClient(client.get()); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Calling CountFeature() before client is set will save the feature usage in |
| // the set, and once SetClient() is called it gets propagated to the client. |
| ASSERT_EQ(1UL, client->used_features().size()); |
| ASSERT_EQ(blink::mojom::WebFeature::kWorkerStart, |
| *(client->used_features().begin())); |
| |
| container_ptr->CountFeature(blink::mojom::WebFeature::kWindowEvent); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_EQ(2UL, client->used_features().size()); |
| ASSERT_EQ(blink::mojom::WebFeature::kWindowEvent, |
| *(++(client->used_features().begin()))); |
| } |
| |
| TEST_F(ServiceWorkerProviderContextTest, GetOrCreateRegistration) { |
| scoped_refptr<WebServiceWorkerRegistrationImpl> registration1; |
| scoped_refptr<WebServiceWorkerRegistrationImpl> registration2; |
| // Set up ServiceWorkerProviderContext for client contexts. |
| const int kProviderId = 10; |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, nullptr, |
| nullptr, nullptr /* controller_info */, nullptr /* loader_factory*/); |
| |
| auto active_host = |
| std::make_unique<MockServiceWorkerObjectHost>(200 /* version_id */); |
| auto waiting_host = |
| std::make_unique<MockServiceWorkerObjectHost>(201 /* version_id */); |
| auto installing_host = |
| std::make_unique<MockServiceWorkerObjectHost>(202 /* version_id */); |
| ASSERT_EQ(0, active_host->GetBindingCount()); |
| ASSERT_EQ(0, waiting_host->GetBindingCount()); |
| ASSERT_EQ(0, installing_host->GetBindingCount()); |
| const int64_t registration_id = 10; |
| auto mock_registration_object_host = |
| std::make_unique<MockServiceWorkerRegistrationObjectHost>( |
| registration_id); |
| ASSERT_EQ(0, mock_registration_object_host->GetBindingCount()); |
| |
| { |
| blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration_info = |
| mock_registration_object_host->CreateObjectInfo( |
| active_host.get(), waiting_host.get(), installing_host.get()); |
| // ServiceWorkerRegistrationObjectHost Mojo connection has been added. |
| EXPECT_EQ(1, mock_registration_object_host->GetBindingCount()); |
| // ServiceWorkerObjectHost Mojo connections have been added. |
| EXPECT_EQ(1, active_host->GetBindingCount()); |
| EXPECT_EQ(1, waiting_host->GetBindingCount()); |
| EXPECT_EQ(1, installing_host->GetBindingCount()); |
| |
| ASSERT_FALSE(ContainsRegistration(provider_context.get(), registration_id)); |
| // Should return a registration object newly created with adopting the |
| // refcounts. |
| registration1 = |
| provider_context->GetOrCreateServiceWorkerRegistrationObject( |
| std::move(registration_info)); |
| EXPECT_TRUE(registration1); |
| EXPECT_TRUE(ContainsRegistration(provider_context.get(), registration_id)); |
| EXPECT_EQ(registration_id, registration1->RegistrationId()); |
| EXPECT_EQ(1, mock_registration_object_host->GetBindingCount()); |
| } |
| |
| { |
| blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration_info = |
| mock_registration_object_host->CreateObjectInfo( |
| active_host.get(), waiting_host.get(), installing_host.get()); |
| // ServiceWorkerRegistrationObjectHost Mojo connection has been added. |
| EXPECT_EQ(2, mock_registration_object_host->GetBindingCount()); |
| // ServiceWorkerObjectHost Mojo connections have been added. |
| EXPECT_EQ(2, active_host->GetBindingCount()); |
| EXPECT_EQ(2, waiting_host->GetBindingCount()); |
| EXPECT_EQ(2, installing_host->GetBindingCount()); |
| |
| // Should return the same registration object without incrementing the |
| // refcounts. |
| registration2 = |
| provider_context->GetOrCreateServiceWorkerRegistrationObject( |
| std::move(registration_info)); |
| EXPECT_TRUE(registration2); |
| EXPECT_EQ(registration1, registration2); |
| base::RunLoop().RunUntilIdle(); |
| // The 2nd ServiceWorkerRegistrationObjectHost Mojo connection has been |
| // dropped. |
| EXPECT_EQ(1, mock_registration_object_host->GetBindingCount()); |
| // The corresponding ServiceWorkerObjectHost Mojo connections have been |
| // dropped. |
| EXPECT_EQ(1, active_host->GetBindingCount()); |
| EXPECT_EQ(1, waiting_host->GetBindingCount()); |
| EXPECT_EQ(1, installing_host->GetBindingCount()); |
| } |
| |
| // The registration dtor decrements the refcounts. |
| registration1 = nullptr; |
| registration2 = nullptr; |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(ContainsRegistration(provider_context.get(), registration_id)); |
| // The 1st ServiceWorkerRegistrationObjectHost Mojo connection got broken. |
| EXPECT_EQ(0, mock_registration_object_host->GetBindingCount()); |
| // The corresponding ServiceWorkerObjectHost Mojo connections got broken. |
| EXPECT_EQ(0, active_host->GetBindingCount()); |
| EXPECT_EQ(0, waiting_host->GetBindingCount()); |
| EXPECT_EQ(0, installing_host->GetBindingCount()); |
| } |
| |
| TEST_F(ServiceWorkerProviderContextTest, GetOrCreateServiceWorker) { |
| scoped_refptr<WebServiceWorkerImpl> worker1; |
| scoped_refptr<WebServiceWorkerImpl> worker2; |
| // Set up ServiceWorkerProviderContext for client contexts. |
| const int kProviderId = 10; |
| auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>( |
| kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow, nullptr, |
| nullptr, nullptr /* controller_info */, nullptr /* loader_factory*/); |
| const int64_t version_id = 200; |
| auto mock_service_worker_object_host = |
| std::make_unique<MockServiceWorkerObjectHost>(version_id); |
| ASSERT_EQ(0, mock_service_worker_object_host->GetBindingCount()); |
| |
| // Should return a worker object newly created with the 1st given |info|. |
| { |
| blink::mojom::ServiceWorkerObjectInfoPtr info = |
| mock_service_worker_object_host->CreateObjectInfo(); |
| // ServiceWorkerObjectHost Mojo connection has been added. |
| EXPECT_EQ(1, mock_service_worker_object_host->GetBindingCount()); |
| ASSERT_FALSE(ContainsServiceWorker(provider_context.get(), version_id)); |
| worker1 = provider_context->GetOrCreateServiceWorkerObject(std::move(info)); |
| EXPECT_TRUE(worker1); |
| EXPECT_TRUE(ContainsServiceWorker(provider_context.get(), version_id)); |
| // |worker1| is holding the 1st blink::mojom::ServiceWorkerObjectHost Mojo |
| // connection to |mock_service_worker_object_host|. |
| EXPECT_EQ(1, mock_service_worker_object_host->GetBindingCount()); |
| } |
| |
| // Should return the same worker object and release the 2nd given |info|. |
| { |
| blink::mojom::ServiceWorkerObjectInfoPtr info = |
| mock_service_worker_object_host->CreateObjectInfo(); |
| EXPECT_EQ(2, mock_service_worker_object_host->GetBindingCount()); |
| worker2 = provider_context->GetOrCreateServiceWorkerObject(std::move(info)); |
| EXPECT_EQ(worker1, worker2); |
| base::RunLoop().RunUntilIdle(); |
| // The 2nd ServiceWorkerObjectHost Mojo connection in |info| has been |
| // dropped. |
| EXPECT_EQ(1, mock_service_worker_object_host->GetBindingCount()); |
| } |
| |
| // The dtor decrements the refcounts. |
| worker1 = nullptr; |
| worker2 = nullptr; |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(ContainsServiceWorker(provider_context.get(), version_id)); |
| // The 1st ServiceWorkerObjectHost Mojo connection got broken. |
| EXPECT_EQ(0, mock_service_worker_object_host->GetBindingCount()); |
| |
| // Should return nullptr when given nullptr. |
| scoped_refptr<WebServiceWorkerImpl> invalid_worker = |
| provider_context->GetOrCreateServiceWorkerObject(nullptr); |
| EXPECT_FALSE(invalid_worker); |
| } |
| |
| } // namespace service_worker_provider_context_unittest |
| } // namespace content |