| // Copyright 2014 The Chromium Authors |
| // 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_version.h" |
| |
| #include <stdint.h> |
| #include <memory> |
| #include <tuple> |
| #include <utility> |
| |
| #include "base/containers/span.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/time/time.h" |
| #include "base/uuid.h" |
| #include "content/browser/renderer_host/frame_tree_node.h" |
| #include "content/browser/service_worker/embedded_worker_test_helper.h" |
| #include "content/browser/service_worker/fake_embedded_worker_instance_client.h" |
| #include "content/browser/service_worker/fake_service_worker.h" |
| #include "content/browser/service_worker/service_worker_container_host.h" |
| #include "content/browser/service_worker/service_worker_context_core.h" |
| #include "content/browser/service_worker/service_worker_ping_controller.h" |
| #include "content/browser/service_worker/service_worker_registration.h" |
| #include "content/browser/service_worker/service_worker_test_utils.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/mock_render_process_host.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_service.mojom.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/base/test_completion_callback.h" |
| #include "services/metrics/public/cpp/ukm_recorder.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/service_worker/embedded_worker_status.h" |
| #include "third_party/blink/public/common/service_worker/service_worker_router_rule.h" |
| #include "third_party/blink/public/common/storage_key/storage_key.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h" |
| |
| namespace content { |
| namespace service_worker_version_unittest { |
| |
| constexpr base::TimeDelta kTestTimeoutBeyondRequestTimeout = |
| // Value of kRequestTimeout in service_worker_version.cc |
| base::Minutes(5) + |
| // A little past that. |
| base::Minutes(1); |
| |
| base::OnceCallback<void()> VerifyCalled( |
| bool* called, |
| base::OnceClosure quit_closure = base::OnceClosure()) { |
| return base::BindOnce( |
| [](bool* called, base::OnceClosure quit_closure) { |
| *called = true; |
| if (!quit_closure.is_null()) |
| std::move(quit_closure).Run(); |
| }, |
| called, std::move(quit_closure)); |
| } |
| |
| void ObserveStatusChanges(ServiceWorkerVersion* version, |
| std::vector<ServiceWorkerVersion::Status>* statuses) { |
| statuses->push_back(version->status()); |
| version->RegisterStatusChangeCallback(base::BindOnce( |
| &ObserveStatusChanges, base::Unretained(version), statuses)); |
| } |
| |
| base::Time GetYesterday() { |
| return base::Time::Now() - base::Days(1) - base::Seconds(1); |
| } |
| |
| enum class StorageKeyTestCase { |
| kFirstParty, |
| kThirdParty, |
| }; |
| |
| class ServiceWorkerVersionTest |
| : public testing::Test, |
| public testing::WithParamInterface<StorageKeyTestCase> { |
| public: |
| ServiceWorkerVersionTest(const ServiceWorkerVersionTest&) = delete; |
| ServiceWorkerVersionTest& operator=(const ServiceWorkerVersionTest&) = delete; |
| |
| protected: |
| using FetchHandlerExistence = blink::mojom::FetchHandlerExistence; |
| |
| struct CachedMetadataUpdateListener : public ServiceWorkerVersion::Observer { |
| CachedMetadataUpdateListener() = default; |
| ~CachedMetadataUpdateListener() override = default; |
| void OnCachedMetadataUpdated(ServiceWorkerVersion* version, |
| size_t size) override { |
| ++updated_count; |
| } |
| int updated_count = 0; |
| }; |
| |
| ServiceWorkerVersionTest() |
| : task_environment_(BrowserTaskEnvironment::IO_MAINLOOP, |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} |
| |
| void SetUp() override { |
| helper_ = GetHelper(); |
| |
| scope_ = GURL("https://www.example.com/test/"); |
| blink::mojom::ServiceWorkerRegistrationOptions options; |
| options.scope = scope_; |
| registration_ = CreateNewServiceWorkerRegistration( |
| helper_->context()->registry(), options, GetTestStorageKey(scope_)); |
| version_ = CreateNewServiceWorkerVersion( |
| helper_->context()->registry(), registration_.get(), |
| GURL("https://www.example.com/test/service_worker.js"), |
| blink::mojom::ScriptType::kClassic); |
| EXPECT_EQ(url::Origin::Create(scope_), version_->key().origin()); |
| std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> records; |
| records.push_back(WriteToDiskCacheWithIdSync( |
| helper_->context()->GetStorageControl(), version_->script_url(), 10, |
| {} /* headers */, "I'm a body", "I'm a meta data")); |
| version_->script_cache_map()->SetResources(records); |
| version_->SetMainScriptResponse( |
| EmbeddedWorkerTestHelper::CreateMainScriptResponse()); |
| if (GetFetchHandlerType()) { |
| version_->set_fetch_handler_type(*GetFetchHandlerType()); |
| } |
| |
| // Make the registration findable via storage functions. |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| helper_->context()->registry()->StoreRegistration( |
| registration_.get(), version_.get(), |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| run_loop.Run(); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| } |
| |
| virtual std::unique_ptr<EmbeddedWorkerTestHelper> GetHelper() { |
| return std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath()); |
| } |
| |
| void TearDown() override { |
| client_render_process_hosts_.clear(); |
| version_ = nullptr; |
| registration_ = nullptr; |
| helper_.reset(); |
| } |
| |
| bool IsPingActivated(ServiceWorkerVersion* version) const { |
| return version->ping_controller_.IsActivated(); |
| } |
| |
| void NotifyScriptEvaluationStart(ServiceWorkerVersion* version) { |
| version->OnScriptEvaluationStart(); |
| } |
| |
| void SimulateDispatchEvent(ServiceWorkerMetrics::EventType event_type) { |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| |
| // Make sure worker is running. |
| version_->RunAfterStartWorker( |
| event_type, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, |
| version_->running_status()); |
| |
| // Start request, as if an event is being dispatched. |
| int request_id = version_->StartRequest(event_type, base::DoNothing()); |
| base::RunLoop().RunUntilIdle(); |
| |
| // And finish request, as if a response to the event was received. |
| EXPECT_TRUE(version_->FinishRequest(request_id, /*was_handled=*/true)); |
| } |
| |
| void SetupTestTickClock() { version_->SetTickClockForTesting(&tick_clock_); } |
| |
| virtual std::optional<ServiceWorkerVersion::FetchHandlerType> |
| GetFetchHandlerType() const { |
| return ServiceWorkerVersion::FetchHandlerType::kNotSkippable; |
| } |
| |
| // Make the client in a different process from the service worker when |
| // |in_different_process| is true. |
| ServiceWorkerRemoteContainerEndpoint ActivateWithControllee( |
| bool in_different_process = false) { |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| registration_->SetActiveVersion(version_); |
| ServiceWorkerRemoteContainerEndpoint remote_endpoint; |
| int controllee_process_id = ChildProcessHost::kInvalidUniqueID; |
| |
| if (in_different_process) { |
| auto client_render_process_host = |
| std::make_unique<MockRenderProcessHost>(helper_->browser_context()); |
| controllee_process_id = client_render_process_host->GetID(); |
| client_render_process_hosts_.push_back( |
| std::move(client_render_process_host)); |
| } else { |
| controllee_process_id = version_->embedded_worker()->process_id(); |
| } |
| |
| base::WeakPtr<ServiceWorkerContainerHost> container_host = |
| CreateContainerHostForWindow( |
| GlobalRenderFrameHostId(controllee_process_id, |
| /*mock frame_routing_id=*/1), |
| /*is_parent_frame_secure=*/true, helper_->context()->AsWeakPtr(), |
| &remote_endpoint); |
| container_host->UpdateUrls(registration_->scope(), |
| registration_->key().origin(), |
| registration_->key()); |
| container_host->SetControllerRegistration( |
| registration_, false /* notify_controllerchange */); |
| EXPECT_TRUE(version_->HasControllee()); |
| EXPECT_TRUE(container_host->controller()); |
| return remote_endpoint; |
| } |
| |
| bool UseFirstPartyStorageKey() { |
| return GetParam() == StorageKeyTestCase::kFirstParty; |
| } |
| |
| blink::StorageKey GetTestStorageKey(const GURL& scope_url) { |
| auto scope_origin = url::Origin::Create(scope_url); |
| if (UseFirstPartyStorageKey()) { |
| return blink::StorageKey::CreateFirstParty(std::move(scope_origin)); |
| } else { |
| // For simplicity create a third-party storage key by setting the ancestor |
| // chain bit to kCrossSite. |
| auto storage_key = blink::StorageKey::Create( |
| scope_origin, net::SchemefulSite(scope_origin), |
| blink::mojom::AncestorChainBit::kCrossSite, |
| /*third_party_partitioning_allowed=*/true); |
| EXPECT_TRUE(storage_key.IsThirdPartyContext()); |
| return storage_key; |
| } |
| } |
| |
| BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<EmbeddedWorkerTestHelper> helper_; |
| scoped_refptr<ServiceWorkerRegistration> registration_; |
| scoped_refptr<ServiceWorkerVersion> version_; |
| // Used to hold render process hosts for clients which reside in different |
| // processes from the service worker. |
| std::vector<std::unique_ptr<MockRenderProcessHost>> |
| client_render_process_hosts_; |
| GURL scope_; |
| // Some tests sets a custom tick clock, store it here to ensure that it |
| // outlives `version_`. |
| base::SimpleTestTickClock tick_clock_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ServiceWorkerVersionTest, |
| testing::ValuesIn({StorageKeyTestCase::kFirstParty, |
| StorageKeyTestCase::kThirdParty}), |
| [](const testing::TestParamInfo<StorageKeyTestCase>& info) { |
| switch (info.param) { |
| case (StorageKeyTestCase::kFirstParty): |
| return "FirstPartyStorageKey"; |
| case (StorageKeyTestCase::kThirdParty): |
| return "ThirdPartyStorageKey"; |
| } |
| }); |
| |
| // An instance client that breaks the Mojo connection upon receiving the |
| // Start() message. |
| class FailStartInstanceClient : public FakeEmbeddedWorkerInstanceClient { |
| public: |
| FailStartInstanceClient(EmbeddedWorkerTestHelper* helper) |
| : FakeEmbeddedWorkerInstanceClient(helper) {} |
| |
| FailStartInstanceClient(const FailStartInstanceClient&) = delete; |
| FailStartInstanceClient& operator=(const FailStartInstanceClient&) = delete; |
| |
| void StartWorker(blink::mojom::EmbeddedWorkerStartParamsPtr params) override { |
| // Don't save the Mojo ptrs. The connection breaks. |
| } |
| }; |
| |
| TEST_P(ServiceWorkerVersionTest, ConcurrentStartAndStop) { |
| // Call StartWorker() multiple times. |
| std::optional<blink::ServiceWorkerStatusCode> status1; |
| std::optional<blink::ServiceWorkerStatusCode> status2; |
| std::optional<blink::ServiceWorkerStatusCode> status3; |
| base::RunLoop run_loop_1; |
| base::RunLoop run_loop_2; |
| base::RunLoop run_loop_3; |
| |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status1, run_loop_1.QuitClosure())); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStarting, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveStartingServiceWorker( |
| version_->version_id())); |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status2, run_loop_2.QuitClosure())); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| |
| // Call StartWorker() after it's started. |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status3, run_loop_3.QuitClosure())); |
| |
| run_loop_1.Run(); |
| run_loop_2.Run(); |
| run_loop_3.Run(); |
| |
| // All should just succeed. |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status1.value()); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status2.value()); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status3.value()); |
| |
| { |
| // Call StopWorker() multiple times. |
| bool has_stopped1 = false; |
| bool has_stopped2 = false; |
| base::RunLoop run_loop_4; |
| base::RunLoop run_loop_5; |
| |
| version_->StopWorker(VerifyCalled(&has_stopped1, run_loop_4.QuitClosure())); |
| version_->StopWorker(VerifyCalled(&has_stopped2, run_loop_5.QuitClosure())); |
| |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopping, |
| version_->running_status()); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| run_loop_4.Run(); |
| run_loop_5.Run(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, |
| version_->running_status()); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| |
| // All StopWorker should just succeed. |
| EXPECT_TRUE(has_stopped1); |
| EXPECT_TRUE(has_stopped2); |
| } |
| |
| // Start worker again. |
| status1.reset(); |
| status2.reset(); |
| |
| base::RunLoop run_loop_6; |
| base::RunLoop run_loop_7; |
| |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status1, run_loop_6.QuitClosure())); |
| |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStarting, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveStartingServiceWorker( |
| version_->version_id())); |
| run_loop_6.Run(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| |
| { |
| // Call StopWorker() |
| bool has_stopped = false; |
| version_->StopWorker(VerifyCalled(&has_stopped)); |
| |
| // And try calling StartWorker while StopWorker is in queue. |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status2, run_loop_7.QuitClosure())); |
| |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopping, |
| version_->running_status()); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| run_loop_7.Run(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, |
| version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| |
| // All should just succeed. |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status1.value()); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status2.value()); |
| EXPECT_TRUE(has_stopped); |
| } |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, DispatchEventToStoppedWorker) { |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| |
| // Dispatch an event without starting the worker. |
| version_->SetStatus(ServiceWorkerVersion::INSTALLING); |
| EXPECT_TRUE(version_->HasNoWork()); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::INSTALL); |
| |
| // The worker may still be handling events dispatched directly from |
| // controllees. We cannot say the version doesn't handle any tasks until the |
| // worker reports "No Work" (= ServiceWorkerVersion::OnRequestTermination() |
| // is called). |
| EXPECT_FALSE(version_->HasNoWork()); |
| |
| // The worker should be now started. |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| |
| // Stop the worker, and then dispatch an event immediately after that. |
| bool has_stopped = false; |
| version_->StopWorker(VerifyCalled(&has_stopped)); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::INSTALL); |
| EXPECT_TRUE(has_stopped); |
| |
| // The worker may still be handling events dispatched directly from |
| // controllees. We cannot say the version doesn't handle any tasks until the |
| // worker reports "No Work" (= ServiceWorkerVersion::OnRequestTermination() |
| // is called). |
| EXPECT_FALSE(version_->HasNoWork()); |
| |
| // The worker should be now started again. |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, StartUnregisteredButStillLiveWorker) { |
| // Start the worker. |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| |
| // Delete the registration. |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| helper_->context()->registry()->DeleteRegistration( |
| registration_, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| run_loop.Run(); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| |
| // The live registration is marked as uninstalling, but still exists. |
| ASSERT_TRUE(registration_->is_uninstalling()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| |
| // Stop the worker. |
| StopServiceWorker(version_.get()); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| |
| // Dispatch an event on the unregistered and stopped but still live worker. |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::FETCH_MAIN_FRAME); |
| |
| // The worker should be now started again. |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, InstallAndWaitCompletion) { |
| version_->SetStatus(ServiceWorkerVersion::INSTALLING); |
| |
| // Wait for the completion. |
| bool status_change_called = false; |
| version_->RegisterStatusChangeCallback(VerifyCalled(&status_change_called)); |
| |
| // Dispatch an install event. |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::INSTALL); |
| |
| // Version's status must not have changed during installation. |
| EXPECT_FALSE(status_change_called); |
| EXPECT_EQ(ServiceWorkerVersion::INSTALLING, version_->status()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, ActivateAndWaitCompletion) { |
| // TODO(mek): This test (and the one above for the install event) made more |
| // sense back when ServiceWorkerVersion was responsible for updating the |
| // status. Now a better version of this test should probably be added to |
| // ServiceWorkerRegistrationTest instead. |
| |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATING); |
| |
| // Wait for the completion. |
| bool status_change_called = false; |
| version_->RegisterStatusChangeCallback(VerifyCalled(&status_change_called)); |
| |
| // Dispatch an activate event. |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::ACTIVATE); |
| |
| // Version's status must not have changed during activation. |
| EXPECT_FALSE(status_change_called); |
| EXPECT_EQ(ServiceWorkerVersion::ACTIVATING, version_->status()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, RepeatedlyObserveStatusChanges) { |
| EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status()); |
| |
| // Repeatedly observe status changes (the callback re-registers itself). |
| std::vector<ServiceWorkerVersion::Status> statuses; |
| version_->RegisterStatusChangeCallback(base::BindOnce( |
| &ObserveStatusChanges, base::RetainedRef(version_), &statuses)); |
| |
| version_->SetStatus(ServiceWorkerVersion::INSTALLING); |
| version_->SetStatus(ServiceWorkerVersion::INSTALLED); |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATING); |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| version_->SetStatus(ServiceWorkerVersion::REDUNDANT); |
| |
| // Verify that we could successfully observe repeated status changes. |
| ASSERT_EQ(5U, statuses.size()); |
| ASSERT_EQ(ServiceWorkerVersion::INSTALLING, statuses[0]); |
| ASSERT_EQ(ServiceWorkerVersion::INSTALLED, statuses[1]); |
| ASSERT_EQ(ServiceWorkerVersion::ACTIVATING, statuses[2]); |
| ASSERT_EQ(ServiceWorkerVersion::ACTIVATED, statuses[3]); |
| ASSERT_EQ(ServiceWorkerVersion::REDUNDANT, statuses[4]); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, Doom) { |
| // Add a controllee. |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| registration_->SetActiveVersion(version_); |
| ServiceWorkerRemoteContainerEndpoint remote_endpoint; |
| base::WeakPtr<ServiceWorkerContainerHost> container_host = |
| CreateContainerHostForWindow( |
| GlobalRenderFrameHostId(/*mock process_id=*/33, |
| /*mock frame_routing_id=*/1), |
| /*is_parent_frame_secure=*/true, helper_->context()->AsWeakPtr(), |
| &remote_endpoint); |
| container_host->UpdateUrls(registration_->scope(), |
| registration_->key().origin(), |
| registration_->key()); |
| container_host->SetControllerRegistration(registration_, false); |
| EXPECT_TRUE(version_->HasControllee()); |
| EXPECT_TRUE(container_host->controller()); |
| |
| // Set main_script_load_params_. |
| version_->set_main_script_load_params( |
| blink::mojom::WorkerMainScriptLoadParams::New()); |
| |
| // Doom the version. |
| version_->Doom(); |
| |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| |
| // The controllee should have been removed. |
| EXPECT_EQ(ServiceWorkerVersion::REDUNDANT, version_->status()); |
| EXPECT_FALSE(version_->HasControllee()); |
| EXPECT_FALSE(container_host->controller()); |
| |
| // Ensure that the params are released. |
| EXPECT_TRUE(version_->main_script_load_params_.is_null()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, SetDevToolsAttached) { |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| |
| ASSERT_EQ(blink::EmbeddedWorkerStatus::kStarting, version_->running_status()); |
| |
| ASSERT_TRUE(version_->timeout_timer_.IsRunning()); |
| ASSERT_FALSE(version_->start_time_.is_null()); |
| ASSERT_FALSE(version_->skip_recording_startup_time_); |
| |
| // Simulate DevTools is attached. This should deactivate the timer for start |
| // timeout, but not stop the timer itself. |
| version_->SetDevToolsAttached(true); |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| EXPECT_TRUE(version_->start_time_.is_null()); |
| EXPECT_TRUE(version_->skip_recording_startup_time_); |
| |
| // Simulate DevTools is detached. This should reactivate the timer for start |
| // timeout. |
| version_->SetDevToolsAttached(false); |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| EXPECT_FALSE(version_->start_time_.is_null()); |
| EXPECT_TRUE(version_->skip_recording_startup_time_); |
| |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| } |
| |
| // Tests that a worker containing external request with |
| // ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout does not stop |
| // if devtools is detached (after it is attached). |
| // |
| // Regression test for crbug.com/1152255#c144 |
| TEST_P(ServiceWorkerVersionTest, DevToolsAttachThenDetach) { |
| SetupTestTickClock(); |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| |
| auto start_external_request_test = |
| [&](ServiceWorkerExternalRequestTimeoutType timeout_type, |
| bool expect_running) { |
| SCOPED_TRACE(testing::Message() |
| << std::boolalpha << "expect_running: " << expect_running); |
| { |
| // Start worker. |
| base::RunLoop run_loop; |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| ASSERT_EQ(blink::EmbeddedWorkerStatus::kStarting, |
| version_->running_status()); |
| |
| // Add an external request. |
| EXPECT_EQ(ServiceWorkerExternalRequestResult::kOk, |
| version_->StartExternalRequest( |
| base::Uuid::GenerateRandomV4(), timeout_type)); |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, |
| version_->running_status()); |
| } |
| |
| { |
| // Simulate DevTools is attached. |
| version_->SetDevToolsAttached(true); |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| EXPECT_TRUE(version_->start_time_.is_null()); |
| EXPECT_TRUE(version_->skip_recording_startup_time_); |
| |
| // Simulate DevTools is detached. |
| version_->SetDevToolsAttached(false); |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| EXPECT_TRUE(version_->skip_recording_startup_time_); |
| |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, |
| version_->running_status()); |
| } |
| |
| // Now advance time to check worker's running state. |
| tick_clock_.Advance(kTestTimeoutBeyondRequestTimeout); |
| version_->timeout_timer_.user_task().Run(); |
| base::RunLoop().RunUntilIdle(); |
| |
| const bool worker_stopped_or_stopping = |
| version_->OnRequestTermination(); |
| |
| EXPECT_EQ(!expect_running, worker_stopped_or_stopping); |
| const bool worker_running = |
| version_->running_status() == blink::EmbeddedWorkerStatus::kRunning; |
| EXPECT_EQ(expect_running, worker_running); |
| |
| // Ensure the worker is stopped, so that start_external_request_test() |
| // works next time. |
| { |
| bool has_stopped = false; |
| base::RunLoop run_loop; |
| version_->StopWorker( |
| VerifyCalled(&has_stopped, run_loop.QuitClosure())); |
| run_loop.Run(); |
| EXPECT_TRUE(has_stopped); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, |
| version_->running_status()); |
| } |
| }; |
| |
| // kDoesNotTimeout timeout external request would continue to keep the worker |
| // running. |
| start_external_request_test( |
| ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout, |
| true /* expect_running */); |
| |
| // And ensure that kDefault timeout external request does not keep the worker |
| // running. |
| start_external_request_test(ServiceWorkerExternalRequestTimeoutType::kDefault, |
| false /* expect_running */); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, RequestTerminationWithDevToolsAttached) { |
| auto* service_worker = |
| helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get()); |
| |
| version_->SetDevToolsAttached(true); |
| |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| |
| // Idle delay is not set at this point. The renderer process uses the default |
| // value. |
| EXPECT_FALSE(service_worker->idle_delay().has_value()); |
| |
| // If OnRequestTermination() is called when DevTools is attached, then the |
| // worker's idle timeout is set to the default value forcefully because the |
| // worker needs to be running until DevTools is detached even if there's no |
| // inflight event. |
| version_->OnRequestTermination(); |
| service_worker->FlushForTesting(); |
| EXPECT_EQ(blink::mojom::kServiceWorkerDefaultIdleDelayInSeconds, |
| service_worker->idle_delay()->InSeconds()); |
| } |
| |
| // Test that update isn't triggered for a non-stale worker. |
| TEST_P(ServiceWorkerVersionTest, StaleUpdate_FreshWorker) { |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| registration_->SetActiveVersion(version_); |
| registration_->set_last_update_check(base::Time::Now()); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::PUSH); |
| |
| EXPECT_TRUE(version_->stale_time_.is_null()); |
| EXPECT_FALSE(version_->update_timer_.IsRunning()); |
| } |
| |
| // Test that update isn't triggered for a non-active worker. |
| TEST_P(ServiceWorkerVersionTest, StaleUpdate_NonActiveWorker) { |
| version_->SetStatus(ServiceWorkerVersion::INSTALLING); |
| registration_->SetInstallingVersion(version_); |
| registration_->set_last_update_check(GetYesterday()); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::INSTALL); |
| |
| EXPECT_TRUE(version_->stale_time_.is_null()); |
| EXPECT_FALSE(version_->update_timer_.IsRunning()); |
| } |
| |
| // Test that staleness is detected when starting a worker. |
| TEST_P(ServiceWorkerVersionTest, StaleUpdate_StartWorker) { |
| // Starting the worker marks it as stale. |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| registration_->SetActiveVersion(version_); |
| registration_->set_last_update_check(GetYesterday()); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::PUSH); |
| EXPECT_FALSE(version_->stale_time_.is_null()); |
| EXPECT_FALSE(version_->update_timer_.IsRunning()); |
| |
| // Update is actually scheduled after the worker stops. |
| StopServiceWorker(version_.get()); |
| EXPECT_TRUE(version_->stale_time_.is_null()); |
| EXPECT_TRUE(version_->update_timer_.IsRunning()); |
| } |
| |
| // Test that staleness is detected on a running worker. |
| TEST_P(ServiceWorkerVersionTest, StaleUpdate_RunningWorker) { |
| // Start a fresh worker. |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| registration_->SetActiveVersion(version_); |
| registration_->set_last_update_check(base::Time::Now()); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::PUSH); |
| EXPECT_TRUE(version_->stale_time_.is_null()); |
| |
| // Simulate it running for a day. It will be marked stale. |
| registration_->set_last_update_check(GetYesterday()); |
| version_->OnTimeoutTimer(); |
| EXPECT_FALSE(version_->stale_time_.is_null()); |
| EXPECT_FALSE(version_->update_timer_.IsRunning()); |
| |
| // Simulate it running for past the wait threshold. The update will be |
| // scheduled. |
| version_->stale_time_ = base::TimeTicks::Now() - |
| ServiceWorkerVersion::kStartNewWorkerTimeout - |
| base::Minutes(1); |
| version_->OnTimeoutTimer(); |
| EXPECT_TRUE(version_->stale_time_.is_null()); |
| EXPECT_TRUE(version_->update_timer_.IsRunning()); |
| } |
| |
| // Test that a stream of events doesn't restart the timer. |
| TEST_P(ServiceWorkerVersionTest, StaleUpdate_DoNotDeferTimer) { |
| // Make a stale worker. |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| registration_->SetActiveVersion(version_); |
| registration_->set_last_update_check(GetYesterday()); |
| base::TimeTicks stale_time = base::TimeTicks::Now() - |
| ServiceWorkerVersion::kStartNewWorkerTimeout - |
| base::Minutes(1); |
| version_->stale_time_ = stale_time; |
| |
| // Stale time is not deferred. |
| version_->RunAfterStartWorker(ServiceWorkerMetrics::EventType::UNKNOWN, |
| base::DoNothing()); |
| version_->RunAfterStartWorker(ServiceWorkerMetrics::EventType::UNKNOWN, |
| base::DoNothing()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(stale_time, version_->stale_time_); |
| |
| // Timeout triggers the update. |
| version_->OnTimeoutTimer(); |
| EXPECT_TRUE(version_->stale_time_.is_null()); |
| EXPECT_TRUE(version_->update_timer_.IsRunning()); |
| |
| // Update timer is not deferred. |
| base::TimeTicks run_time = version_->update_timer_.desired_run_time(); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::PUSH); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::PUSH); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::PUSH); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(version_->stale_time_.is_null()); |
| EXPECT_EQ(run_time, version_->update_timer_.desired_run_time()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, StartRequestWithNullContext) { |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| version_->context_ = nullptr; |
| version_->StartRequest(ServiceWorkerMetrics::EventType::PUSH, |
| base::DoNothing()); |
| // Test passes if it doesn't crash. |
| } |
| |
| // Tests the delay mechanism for self-updating service workers, to prevent |
| // them from running forever (see https://crbug.com/805496). |
| TEST_P(ServiceWorkerVersionTest, ResetUpdateDelay) { |
| const base::TimeDelta kMinute = base::Minutes(1); |
| const base::TimeDelta kNoDelay = base::TimeDelta(); |
| |
| // Initialize the delay. |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| registration_->SetActiveVersion(version_); |
| registration_->set_self_update_delay(kMinute); |
| |
| // Events that can be triggered by a worker should not reset the delay. |
| // See the comment in ServiceWorkerVersion::StartRequestWithCustomTimeout. |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::INSTALL); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::ACTIVATE); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::MESSAGE); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(kMinute, registration_->self_update_delay()); |
| |
| // Events that can only be triggered externally reset the delay. |
| // Repeat the test for several such events. |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::SYNC); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(kNoDelay, registration_->self_update_delay()); |
| |
| registration_->set_self_update_delay(kMinute); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::NOTIFICATION_CLICK); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(kNoDelay, registration_->self_update_delay()); |
| |
| registration_->set_self_update_delay(kMinute); |
| SimulateDispatchEvent(ServiceWorkerMetrics::EventType::PUSH); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(kNoDelay, registration_->self_update_delay()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, UpdateCachedMetadata) { |
| CachedMetadataUpdateListener listener; |
| version_->AddObserver(&listener); |
| ASSERT_EQ(0, listener.updated_count); |
| auto* service_worker = |
| helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get()); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| service_worker->RunUntilInitializeGlobalScope(); |
| |
| // Simulate requesting SetCachedMetadata from the service worker global scope. |
| std::vector<uint8_t> data{1, 2, 3}; |
| service_worker->host()->SetCachedMetadata(version_->script_url(), data); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(1, listener.updated_count); |
| |
| // Simulate requesting ClearCachedMetadata from the service worker global |
| // scope. |
| service_worker->host()->ClearCachedMetadata(version_->script_url()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(2, listener.updated_count); |
| version_->RemoveObserver(&listener); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, RestartWorker) { |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| bool has_stopped = false; |
| |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| version_->StartRequest( |
| ServiceWorkerMetrics::EventType::FETCH_MAIN_FRAME, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| |
| // Restart the worker. The inflight event should have been failed. |
| version_->StopWorker(VerifyCalled(&has_stopped)); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopping, version_->running_status()); |
| run_loop.Run(); |
| |
| // Restart the worker. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| // The worker should have been stopped. |
| EXPECT_TRUE(has_stopped); |
| // All inflight events should have been aborted. |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorFailed, status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| |
| // SetAllRequestExpirations() after restarting should not crash since all |
| // events should have been removed at this point: crbug.com/791451. |
| version_->SetAllRequestExpirations(base::TimeTicks()); |
| } |
| |
| class DelayMessageWorker : public FakeServiceWorker { |
| public: |
| explicit DelayMessageWorker(EmbeddedWorkerTestHelper* helper) |
| : FakeServiceWorker(helper) {} |
| |
| DelayMessageWorker(const DelayMessageWorker&) = delete; |
| DelayMessageWorker& operator=(const DelayMessageWorker&) = delete; |
| |
| ~DelayMessageWorker() override = default; |
| |
| void DispatchExtendableMessageEvent( |
| blink::mojom::ExtendableMessageEventPtr event, |
| DispatchExtendableMessageEventCallback callback) override { |
| event_ = std::move(event); |
| callback_ = std::move(callback); |
| if (quit_closure_) |
| std::move(quit_closure_).Run(); |
| } |
| |
| void AbortMessageEvent() { |
| std::move(callback_).Run(blink::mojom::ServiceWorkerEventStatus::ABORTED); |
| } |
| |
| void RunUntilDispatchMessageEvent() { |
| if (event_) |
| return; |
| base::RunLoop loop; |
| quit_closure_ = loop.QuitClosure(); |
| loop.Run(); |
| } |
| |
| private: |
| blink::mojom::ExtendableMessageEventPtr event_; |
| DispatchExtendableMessageEventCallback callback_; |
| base::OnceClosure quit_closure_; |
| }; |
| |
| TEST_P(ServiceWorkerVersionTest, RequestTimeout) { |
| auto* client = helper_->AddNewPendingInstanceClient< |
| DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get()); |
| auto* worker = |
| helper_->AddNewPendingServiceWorker<DelayMessageWorker>(helper_.get()); |
| |
| std::optional<blink::ServiceWorkerStatusCode> error_status; |
| base::RunLoop run_loop; |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| client->UnblockStartWorker(); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| // Create a request. |
| int request_id = version_->StartRequest( |
| ServiceWorkerMetrics::EventType::FETCH_MAIN_FRAME, |
| ReceiveServiceWorkerStatus(&error_status, run_loop.QuitClosure())); |
| |
| // Dispatch a dummy event. |
| auto message_event = blink::mojom::ExtendableMessageEvent::New(); |
| message_event->message.sender_agent_cluster_id = |
| base::UnguessableToken::Create(); |
| version_->endpoint()->DispatchExtendableMessageEvent( |
| std::move(message_event), |
| version_->CreateSimpleEventCallback(request_id)); |
| worker->RunUntilDispatchMessageEvent(); |
| |
| // Request callback has not completed yet. |
| EXPECT_FALSE(error_status); |
| |
| // Simulate timeout. |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| version_->SetAllRequestExpirations(base::TimeTicks::Now()); |
| version_->timeout_timer_.user_task().Run(); |
| |
| // The renderer should have received a StopWorker request. |
| client->RunUntilStopWorker(); |
| |
| // The request should have timed out. |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, |
| error_status.value()); |
| // Calling FinishRequest should be no-op, since the request timed out. |
| EXPECT_FALSE(version_->FinishRequest(request_id, /*was_handled=*/true)); |
| |
| // Simulate the renderer aborting the inflight event. |
| // This should not crash: https://crbug.com/676984. |
| worker->AbortMessageEvent(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Simulate the renderer stopping the worker. |
| client->UnblockStopWorker(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, RequestNowTimeout) { |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| auto* service_worker = |
| helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get()); |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| // Create a request that should expire Now(). |
| int request_id = version_->StartRequestWithCustomTimeout( |
| ServiceWorkerMetrics::EventType::SYNC, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()), |
| base::TimeDelta(), ServiceWorkerVersion::CONTINUE_ON_TIMEOUT); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| version_->timeout_timer_.user_task().Run(); |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, status.value()); |
| |
| service_worker->FlushForTesting(); |
| // Should try to set idle timeout if the last request has expired and is |
| // CONTINUE_ON_TIMEOUT. |
| EXPECT_TRUE(service_worker->idle_delay().has_value()); |
| |
| EXPECT_FALSE(version_->FinishRequest(request_id, /*was_handled=*/true)); |
| |
| // CONTINUE_ON_TIMEOUT timeouts don't stop the service worker. |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, RequestNowTimeoutKill) { |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| // Create a request that should expire Now(). |
| int request_id = version_->StartRequestWithCustomTimeout( |
| ServiceWorkerMetrics::EventType::SYNC, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()), |
| base::TimeDelta(), ServiceWorkerVersion::KILL_ON_TIMEOUT); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| version_->timeout_timer_.user_task().Run(); |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, status.value()); |
| |
| EXPECT_FALSE(version_->FinishRequest(request_id, /*was_handled=*/true)); |
| |
| // KILL_ON_TIMEOUT timeouts should stop the service worker. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, RequestCustomizedTimeout) { |
| std::optional<blink::ServiceWorkerStatusCode> first_status; |
| std::optional<blink::ServiceWorkerStatusCode> second_status; |
| base::RunLoop first_run_loop; |
| base::RunLoop second_run_loop; |
| |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| SetupTestTickClock(); |
| |
| // Create two requests. One which times out in 10 seconds, one in 20 seconds. |
| int timeout_seconds = 10; |
| int first_request_id = version_->StartRequestWithCustomTimeout( |
| ServiceWorkerMetrics::EventType::SYNC, |
| ReceiveServiceWorkerStatus(&first_status, first_run_loop.QuitClosure()), |
| base::Seconds(2 * timeout_seconds), |
| ServiceWorkerVersion::KILL_ON_TIMEOUT); |
| |
| int second_request_id = version_->StartRequestWithCustomTimeout( |
| ServiceWorkerMetrics::EventType::SYNC, |
| ReceiveServiceWorkerStatus(&second_status, second_run_loop.QuitClosure()), |
| base::Seconds(timeout_seconds), |
| ServiceWorkerVersion::CONTINUE_ON_TIMEOUT); |
| |
| // The status should not have changed since neither task has timed out yet. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| version_->timeout_timer_.user_task().Run(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(first_status); |
| EXPECT_FALSE(second_status); |
| |
| // Now advance time until the second task timeout should expire. |
| tick_clock_.Advance(base::Seconds(timeout_seconds + 1)); |
| version_->timeout_timer_.user_task().Run(); |
| second_run_loop.Run(); |
| EXPECT_FALSE(first_status); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, |
| second_status.value()); |
| |
| // CONTINUE_ON_TIMEOUT timeouts don't stop the service worker. |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| |
| // Now advance time until both tasks should be expired. |
| tick_clock_.Advance(base::Seconds(timeout_seconds + 1)); |
| version_->timeout_timer_.user_task().Run(); |
| first_run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, |
| first_status.value()); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, |
| second_status.value()); |
| |
| EXPECT_FALSE(version_->FinishRequest(first_request_id, /*was_handled=*/true)); |
| |
| EXPECT_FALSE( |
| version_->FinishRequest(second_request_id, /*was_handled=*/true)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // KILL_ON_TIMEOUT timeouts should stop the service worker. |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, MixedRequestTimeouts) { |
| std::optional<blink::ServiceWorkerStatusCode> sync_status; |
| std::optional<blink::ServiceWorkerStatusCode> fetch_status; |
| base::RunLoop sync_run_loop; |
| base::RunLoop fetch_run_loop; |
| |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| // Create a fetch request that should expire sometime later. |
| int fetch_request_id = version_->StartRequest( |
| ServiceWorkerMetrics::EventType::FETCH_MAIN_FRAME, |
| ReceiveServiceWorkerStatus(&fetch_status, fetch_run_loop.QuitClosure())); |
| // Create a request that should expire Now(). |
| int sync_request_id = version_->StartRequestWithCustomTimeout( |
| ServiceWorkerMetrics::EventType::SYNC, |
| ReceiveServiceWorkerStatus(&sync_status, sync_run_loop.QuitClosure()), |
| base::TimeDelta(), ServiceWorkerVersion::CONTINUE_ON_TIMEOUT); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(sync_status); |
| |
| // Verify the sync has timed out but not the fetch. |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| version_->timeout_timer_.user_task().Run(); |
| sync_run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, sync_status.value()); |
| EXPECT_FALSE(fetch_status); |
| |
| // Background sync timeouts don't stop the service worker. |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| |
| // Gracefully handle the sync event finishing after the timeout. |
| EXPECT_FALSE(version_->FinishRequest(sync_request_id, /*was_handled=*/true)); |
| |
| // Verify that the fetch times out later. |
| version_->SetAllRequestExpirations(base::TimeTicks::Now()); |
| version_->timeout_timer_.user_task().Run(); |
| fetch_run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, |
| fetch_status.value()); |
| |
| // Fetch request should no longer exist. |
| EXPECT_FALSE(version_->FinishRequest(fetch_request_id, /*was_handled=*/true)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Other timeouts do stop the service worker. |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, FailToStart_RendererCrash) { |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| auto* client = helper_->AddNewPendingInstanceClient< |
| DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get()); |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Callback has not completed yet. |
| EXPECT_FALSE(status); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStarting, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveStartingServiceWorker( |
| version_->version_id())); |
| |
| // Simulate renderer crash: break EmbeddedWorkerInstance's Mojo connection to |
| // the renderer-side client. |
| client->Disconnect(); |
| run_loop.Run(); |
| // Callback completed. |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorStartWorkerFailed, |
| status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, FailToStart_Timeout) { |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| |
| // Start starting the worker. |
| auto* client = helper_->AddNewPendingInstanceClient< |
| DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get()); |
| client->UnblockStopWorker(); |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Callback has not completed yet. |
| EXPECT_FALSE(status); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStarting, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveStartingServiceWorker( |
| version_->version_id())); |
| |
| // Simulate timeout. |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| version_->start_time_ = base::TimeTicks::Now() - |
| ServiceWorkerVersion::kStartNewWorkerTimeout - |
| base::Minutes(1); |
| version_->timeout_timer_.user_task().Run(); |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorTimeout, status.value()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| } |
| |
| // Test that a service worker stalled in stopping will timeout and not get in a |
| // sticky error state. |
| TEST_P(ServiceWorkerVersionTest, StallInStopping_DetachThenStart) { |
| // Start a worker. |
| auto* client = helper_->AddNewPendingInstanceClient< |
| DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get()); |
| client->UnblockStartWorker(); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| // Try to stop the worker. |
| bool has_stopped = false; |
| base::RunLoop run_loop; |
| version_->StopWorker(VerifyCalled(&has_stopped, run_loop.QuitClosure())); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopping, version_->running_status()); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Worker is now stalled in stopping. Verify a fast timeout is in place. |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| EXPECT_EQ(ServiceWorkerVersion::kStopWorkerTimeout, |
| version_->timeout_timer_.GetCurrentDelay()); |
| |
| // Simulate timeout. |
| version_->stop_time_ = base::TimeTicks::Now() - |
| ServiceWorkerVersion::kStopWorkerTimeout - |
| base::Seconds(1); |
| version_->timeout_timer_.user_task().Run(); |
| run_loop.Run(); |
| EXPECT_TRUE(has_stopped); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| |
| // Try to start the worker again. It should work. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| // The timeout interval should be reset to normal. |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| EXPECT_EQ(ServiceWorkerVersion::kTimeoutTimerDelay, |
| version_->timeout_timer_.GetCurrentDelay()); |
| } |
| |
| // Test that a service worker stalled in stopping with a start worker |
| // request queued up will timeout and restart. |
| TEST_P(ServiceWorkerVersionTest, StallInStopping_DetachThenRestart) { |
| // Start a worker. |
| auto* client = helper_->AddNewPendingInstanceClient< |
| DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get()); |
| client->UnblockStartWorker(); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| // Try to stop the worker. |
| bool has_stopped = false; |
| version_->StopWorker(VerifyCalled(&has_stopped)); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopping, version_->running_status()); |
| |
| // Worker is now stalled in stopping. Add a start worker request. |
| std::optional<blink::ServiceWorkerStatusCode> start_status; |
| base::RunLoop run_loop; |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&start_status, run_loop.QuitClosure())); |
| |
| // Simulate timeout. The worker should stop and get restarted. |
| EXPECT_TRUE(version_->timeout_timer_.IsRunning()); |
| version_->stop_time_ = base::TimeTicks::Now() - |
| ServiceWorkerVersion::kStopWorkerTimeout - |
| base::Seconds(1); |
| version_->timeout_timer_.user_task().Run(); |
| run_loop.Run(); |
| EXPECT_TRUE(has_stopped); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, start_status.value()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, RendererCrashDuringEvent) { |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| |
| auto* client = |
| helper_->AddNewPendingInstanceClient<FakeEmbeddedWorkerInstanceClient>( |
| helper_.get()); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| |
| base::RunLoop loop; |
| blink::ServiceWorkerStatusCode status = blink::ServiceWorkerStatusCode::kOk; |
| int request_id = version_->StartRequest( |
| ServiceWorkerMetrics::EventType::SYNC, |
| base::BindOnce( |
| [](base::OnceClosure done, blink::ServiceWorkerStatusCode* out_status, |
| blink::ServiceWorkerStatusCode result_status) { |
| *out_status = result_status; |
| std::move(done).Run(); |
| }, |
| loop.QuitClosure(), &status)); |
| |
| // Simulate renderer crash: break EmbeddedWorkerInstance's Mojo connection to |
| // the renderer-side client. The request callback should be called. |
| client->Disconnect(); |
| loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorFailed, status); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status()); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| |
| // Request already failed, calling finish should return false. |
| EXPECT_FALSE(version_->FinishRequest(request_id, /*was_handled=*/true)); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, PingController) { |
| // Start starting an worker. Ping should not be active. |
| version_->StartWorker(ServiceWorkerMetrics::EventType::UNKNOWN, |
| base::DoNothing()); |
| EXPECT_FALSE(IsPingActivated(version_.get())); |
| |
| // Start script evaluation. Ping should be active. |
| NotifyScriptEvaluationStart(version_.get()); |
| EXPECT_TRUE(IsPingActivated(version_.get())); |
| |
| // Finish starting the worker. Ping should still be active. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| EXPECT_TRUE(IsPingActivated(version_.get())); |
| } |
| |
| // Test starting a service worker from a disallowed origin. |
| TEST_P(ServiceWorkerVersionTest, BadOrigin) { |
| // An https URL would have been allowed but http is not trustworthy. |
| const GURL scope("http://www.example.com/test/"); |
| blink::mojom::ServiceWorkerRegistrationOptions options; |
| options.scope = scope; |
| |
| auto registration = CreateNewServiceWorkerRegistration( |
| helper_->context()->registry(), options, GetTestStorageKey(scope)); |
| auto version = CreateNewServiceWorkerVersion( |
| helper_->context()->registry(), registration_.get(), |
| GURL("http://www.example.com/test/service_worker.js"), |
| blink::mojom::ScriptType::kClassic); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kErrorDisallowed, |
| StartServiceWorker(version.get())); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, |
| ForegroundServiceWorkerCountUpdatedByControllee) { |
| // Start the worker before we have a controllee. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Add a controllee in a different process from the service worker. |
| auto remote_endpoint = ActivateWithControllee(/*in_different_process=*/true); |
| |
| // RenderProcessHost should be notified of foreground worker. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 1, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Remove the controllee. |
| remote_endpoint.host_remote()->reset(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(version_->HasControllee()); |
| |
| // RenderProcessHost should be notified that there are no foreground workers. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, |
| ForegroundServiceWorkerCountNotUpdatedBySameProcessControllee) { |
| // Start the worker before we have a controllee. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Add a controllee in the same process as the service worker. |
| auto remote_endpoint = ActivateWithControllee(); |
| |
| // RenderProcessHost should be notified of foreground worker. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, |
| ForegroundServiceWorkerCountUpdatedByControlleeProcessIdChange) { |
| // Start the worker before we have a controllee. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| registration_->SetActiveVersion(version_); |
| |
| // Add a controllee, but don't begin the navigation commit yet. This will |
| // cause the client to have an invalid process id like we see in real |
| // navigations. |
| ServiceWorkerRemoteContainerEndpoint remote_endpoint; |
| std::unique_ptr<ServiceWorkerContainerHostAndInfo> host_and_info = |
| CreateContainerHostAndInfoForWindow(helper_->context()->AsWeakPtr(), |
| /*are_ancestors_secure=*/true); |
| base::WeakPtr<ServiceWorkerContainerHost> container_host = |
| std::move(host_and_info->host); |
| remote_endpoint.BindForWindow(std::move(host_and_info->info)); |
| container_host->UpdateUrls(registration_->scope(), |
| registration_->key().origin(), |
| registration_->key()); |
| container_host->SetControllerRegistration( |
| registration_, false /* notify_controllerchange */); |
| EXPECT_TRUE(version_->HasControllee()); |
| EXPECT_TRUE(container_host->controller()); |
| |
| // RenderProcessHost should be notified of foreground worker. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 1, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // This is necessary to make OnBeginNavigationCommit() work. |
| auto remote_controller = container_host->GetRemoteControllerServiceWorker(); |
| |
| // Establish a dummy connection to allow sending messages without errors. |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| reporter; |
| auto dummy = reporter.InitWithNewPipeAndPassReceiver(); |
| |
| // Now begin the navigation commit with the same process id used by the |
| // worker. This should cause the worker to stop being considered foreground |
| // priority. |
| container_host->OnBeginNavigationCommit( |
| GlobalRenderFrameHostId(version_->embedded_worker()->process_id(), |
| /*frame_routing_id=*/1), |
| PolicyContainerPolicies(), std::move(reporter), |
| ukm::UkmRecorder::GetNewSourceID()); |
| |
| // RenderProcessHost should be notified of foreground worker. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, |
| ForegroundServiceWorkerCountUpdatedByWorkerStatus) { |
| // Add a controllee in a different process from the service worker. |
| auto remote_endpoint = ActivateWithControllee(/*in_different_process=*/true); |
| |
| // RenderProcessHost should not be notified of foreground worker yet since |
| // there is no worker running. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Starting the worker should notify the RenderProcessHost of the foreground |
| // worker. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ( |
| 1, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Stopping the worker should notify the RenderProcessHost that the foreground |
| // worker has been removed. |
| version_->StopWorker(base::DoNothing()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, |
| ForegroundServiceWorkerCountUpdatedByControlleeForegroundStateChange) { |
| // Start the worker before we have a controllee. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Add a controllee in a different process from the service worker. |
| auto remote_endpoint = ActivateWithControllee(/*in_different_process=*/true); |
| |
| // RenderProcessHost should be notified of foreground worker. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 1, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Set controllee process to background priority. |
| client_render_process_hosts_[0]->set_is_process_backgrounded(true); |
| version_->UpdateForegroundPriority(); |
| |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Set controllee process to foreground priority. |
| client_render_process_hosts_[0]->set_is_process_backgrounded(false); |
| version_->UpdateForegroundPriority(); |
| |
| EXPECT_EQ( |
| 1, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| } |
| |
| // An instance client whose fetch handler type is kNoHandler. |
| class NoFetchHandlerClient : public FakeEmbeddedWorkerInstanceClient { |
| public: |
| explicit NoFetchHandlerClient(EmbeddedWorkerTestHelper* helper) |
| : FakeEmbeddedWorkerInstanceClient(helper) {} |
| |
| NoFetchHandlerClient(const NoFetchHandlerClient&) = delete; |
| NoFetchHandlerClient& operator=(const NoFetchHandlerClient&) = delete; |
| |
| void EvaluateScript() override { |
| host()->OnScriptEvaluationStart(); |
| host()->OnStarted(blink::mojom::ServiceWorkerStartStatus::kNormalCompletion, |
| blink::mojom::ServiceWorkerFetchHandlerType::kNoHandler, |
| /*has_hid_event_handlers=*/false, |
| /*has_usb_event_handlers=*/false, |
| helper()->GetNextThreadId(), |
| blink::mojom::EmbeddedWorkerStartTiming::New()); |
| } |
| }; |
| |
| class ServiceWorkerVersionNoFetchHandlerTest : public ServiceWorkerVersionTest { |
| protected: |
| void SetUp() override { |
| ServiceWorkerVersionTest::SetUp(); |
| // Make the service worker says no handler. |
| helper_->AddPendingInstanceClient( |
| std::make_unique<NoFetchHandlerClient>(helper_.get())); |
| } |
| std::optional<ServiceWorkerVersion::FetchHandlerType> GetFetchHandlerType() |
| const override { |
| return ServiceWorkerVersion::FetchHandlerType::kNoHandler; |
| } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ServiceWorkerVersionNoFetchHandlerTest, |
| testing::ValuesIn({StorageKeyTestCase::kFirstParty, |
| StorageKeyTestCase::kThirdParty}), |
| [](const testing::TestParamInfo<StorageKeyTestCase>& info) { |
| switch (info.param) { |
| case (StorageKeyTestCase::kFirstParty): |
| return "FirstPartyStorageKey"; |
| case (StorageKeyTestCase::kThirdParty): |
| return "ThirdPartyStorageKey"; |
| } |
| }); |
| |
| TEST_P(ServiceWorkerVersionNoFetchHandlerTest, |
| ForegroundServiceWorkerCountNotUpdated) { |
| // Start the worker before we have a controllee. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| |
| // Add a controllee in a different process from the service worker. |
| auto remote_endpoint = ActivateWithControllee(/*in_different_process=*/true); |
| |
| // RenderProcessHost should not be notified if the service worker does not |
| // have a FetchEvent handler. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| 0, |
| helper_->mock_render_process_host()->foreground_service_worker_count()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, FailToStart_UseNewRendererProcess) { |
| ServiceWorkerContextCore* context = helper_->context(); |
| int64_t id = version_->version_id(); |
| version_->SetStatus(ServiceWorkerVersion::ACTIVATED); |
| |
| // Start once. It should choose the "existing process". |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ(helper_->mock_render_process_id(), |
| version_->embedded_worker()->process_id()); |
| |
| StopServiceWorker(version_.get()); |
| |
| // Fail once. |
| helper_->AddPendingInstanceClient( |
| std::make_unique<FailStartInstanceClient>(helper_.get())); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kErrorStartWorkerFailed, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ(1, context->GetVersionFailureCount(id)); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker(id)); |
| |
| // Fail again. |
| helper_->AddPendingInstanceClient( |
| std::make_unique<FailStartInstanceClient>(helper_.get())); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kErrorStartWorkerFailed, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ(2, context->GetVersionFailureCount(id)); |
| EXPECT_FALSE(helper_->context_wrapper()->IsLiveRunningServiceWorker(id)); |
| |
| // Succeed. It should choose the "new process". |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ(helper_->new_render_process_id(), |
| version_->embedded_worker()->process_id()); |
| EXPECT_EQ(0, context->GetVersionFailureCount(id)); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker(id)); |
| version_->StopWorker(base::DoNothing()); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Start again. It should choose the "existing process" again as we no longer |
| // force creation of a new process. |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| EXPECT_EQ(helper_->mock_render_process_id(), |
| version_->embedded_worker()->process_id()); |
| version_->StopWorker(base::DoNothing()); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, FailToStart_RestartStalledWorker) { |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| // Stall in starting. |
| auto* client = helper_->AddNewPendingInstanceClient< |
| DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get()); |
| client->UnblockStopWorker(); |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::FETCH_MAIN_FRAME, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(status); |
| |
| // The restart logic is triggered because OnStopped is called before |
| // OnStarted. So the Start message is sent again. The delayed instance client |
| // was already consumed, so a default fake instance client will be created, |
| // which starts normally. |
| bool has_stopped = false; |
| version_->StopWorker(VerifyCalled(&has_stopped)); |
| run_loop.Run(); |
| |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| EXPECT_TRUE(has_stopped); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| EXPECT_TRUE(helper_->context_wrapper()->IsLiveRunningServiceWorker( |
| version_->version_id())); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, InstalledFetchEventHandlerExists) { |
| auto* service_worker = |
| helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get()); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| service_worker->RunUntilInitializeGlobalScope(); |
| EXPECT_EQ(FetchHandlerExistence::EXISTS, |
| service_worker->fetch_handler_existence()); |
| } |
| |
| TEST_P(ServiceWorkerVersionNoFetchHandlerTest, |
| InstalledFetchEventHandlerDoesNotExist) { |
| auto* service_worker = |
| helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get()); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| service_worker->RunUntilInitializeGlobalScope(); |
| EXPECT_EQ(FetchHandlerExistence::DOES_NOT_EXIST, |
| service_worker->fetch_handler_existence()); |
| } |
| |
| class StoreMessageServiceWorker : public FakeServiceWorker { |
| public: |
| explicit StoreMessageServiceWorker(EmbeddedWorkerTestHelper* helper) |
| : FakeServiceWorker(helper) {} |
| ~StoreMessageServiceWorker() override = default; |
| |
| // Returns messages from AddMessageToConsole. |
| const std::vector<std::pair<blink::mojom::ConsoleMessageLevel, std::string>>& |
| console_messages() { |
| return console_messages_; |
| } |
| |
| void SetAddMessageToConsoleReceivedCallback( |
| const base::RepeatingClosure& closure) { |
| add_message_to_console_callback_ = closure; |
| } |
| |
| private: |
| void AddMessageToConsole(blink::mojom::ConsoleMessageLevel level, |
| const std::string& message) override { |
| console_messages_.emplace_back(level, message); |
| if (add_message_to_console_callback_) |
| add_message_to_console_callback_.Run(); |
| } |
| |
| std::vector<std::pair<blink::mojom::ConsoleMessageLevel, std::string>> |
| console_messages_; |
| base::RepeatingClosure add_message_to_console_callback_; |
| }; |
| |
| TEST_P(ServiceWorkerVersionTest, AddMessageToConsole) { |
| auto* service_worker = |
| helper_->AddNewPendingServiceWorker<StoreMessageServiceWorker>( |
| helper_.get()); |
| |
| // Attempt to start the worker and immediate AddMessageToConsole should not |
| // cause a crash. |
| std::pair<blink::mojom::ConsoleMessageLevel, std::string> test_message = |
| std::make_pair(blink::mojom::ConsoleMessageLevel::kVerbose, ""); |
| ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, |
| StartServiceWorker(version_.get())); |
| version_->AddMessageToConsole(test_message.first, test_message.second); |
| service_worker->RunUntilInitializeGlobalScope(); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| |
| // Messages sent before sending StartWorker message won't be dispatched. |
| ASSERT_EQ(0UL, service_worker->console_messages().size()); |
| |
| // Messages sent after sending StartWorker message should be reached to |
| // the renderer. |
| base::RunLoop loop; |
| service_worker->SetAddMessageToConsoleReceivedCallback(loop.QuitClosure()); |
| version_->AddMessageToConsole(test_message.first, test_message.second); |
| loop.Run(); |
| ASSERT_EQ(1UL, service_worker->console_messages().size()); |
| EXPECT_EQ(test_message, service_worker->console_messages()[0]); |
| } |
| |
| // Test that writing metadata aborts gracefully when a remote connection to |
| // the Storage Service is disconnected. |
| TEST_P(ServiceWorkerVersionTest, WriteMetadata_RemoteStorageDisconnection) { |
| const std::string kMetadata("Test metadata"); |
| |
| net::TestCompletionCallback completion; |
| version_->script_cache_map()->WriteMetadata( |
| version_->script_url(), base::as_bytes(base::make_span(kMetadata)), |
| completion.callback()); |
| |
| helper_->SimulateStorageRestartForTesting(); |
| |
| ASSERT_EQ(completion.WaitForResult(), net::ERR_FAILED); |
| } |
| |
| // Test that writing metadata aborts gracefully when the storage is disabled. |
| TEST_P(ServiceWorkerVersionTest, WriteMetadata_StorageDisabled) { |
| const std::string kMetadata("Test metadata"); |
| |
| base::RunLoop loop; |
| helper_->context()->registry()->DisableStorageForTesting(loop.QuitClosure()); |
| loop.Run(); |
| |
| net::TestCompletionCallback completion; |
| version_->script_cache_map()->WriteMetadata( |
| version_->script_url(), base::as_bytes(base::make_span(kMetadata)), |
| completion.callback()); |
| |
| ASSERT_EQ(completion.WaitForResult(), net::ERR_FAILED); |
| } |
| |
| // Test that writing metadata twice at the same time finishes successfully. |
| TEST_P(ServiceWorkerVersionTest, WriteMetadata_MultipleWrites) { |
| const std::string kMetadata("Test metadata"); |
| |
| net::TestCompletionCallback completion1; |
| version_->script_cache_map()->WriteMetadata( |
| version_->script_url(), base::as_bytes(base::make_span(kMetadata)), |
| completion1.callback()); |
| net::TestCompletionCallback completion2; |
| version_->script_cache_map()->WriteMetadata( |
| version_->script_url(), base::as_bytes(base::make_span(kMetadata)), |
| completion2.callback()); |
| |
| ASSERT_EQ(completion1.WaitForResult(), static_cast<int>(kMetadata.size())); |
| ASSERT_EQ(completion2.WaitForResult(), static_cast<int>(kMetadata.size())); |
| } |
| |
| // Tests that adding pending external requests with different |
| // ServiceWorkerExternalRequestTimeoutType is handled correctly within |
| // ServiceWorkerVersion::pending_external_requests_. |
| TEST_P(ServiceWorkerVersionTest, PendingExternalRequest) { |
| using TimeoutType = ServiceWorkerExternalRequestTimeoutType; |
| using Result = ServiceWorkerExternalRequestResult; |
| auto get_pending_request_size = [&]() -> size_t { |
| return version_->pending_external_requests_.size(); |
| }; |
| |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| base::RunLoop run_loop; |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| ASSERT_EQ(blink::EmbeddedWorkerStatus::kStarting, version_->running_status()); |
| |
| base::Uuid uuid1 = base::Uuid::GenerateRandomV4(); |
| base::Uuid uuid2 = base::Uuid::GenerateRandomV4(); |
| |
| // Test adding request with |uuid1| and different TimeoutType-s. |
| EXPECT_EQ(Result::kOk, |
| version_->StartExternalRequest(uuid1, TimeoutType::kDefault)); |
| EXPECT_EQ(1u, get_pending_request_size()); |
| // |uuid1| already exists, with same or different TimeoutType. |
| EXPECT_EQ(Result::kBadRequestId, version_->StartExternalRequest( |
| uuid1, TimeoutType::kDoesNotTimeout)); |
| EXPECT_EQ(Result::kBadRequestId, |
| version_->StartExternalRequest(uuid1, TimeoutType::kDefault)); |
| EXPECT_EQ(1u, get_pending_request_size()); |
| // |uuid2| does not exist yet. |
| EXPECT_EQ(Result::kBadRequestId, version_->FinishExternalRequest(uuid2)); |
| |
| // Test adding request with |uuid2|. |
| EXPECT_EQ(Result::kOk, version_->StartExternalRequest( |
| uuid2, TimeoutType::kDoesNotTimeout)); |
| EXPECT_EQ(2u, get_pending_request_size()); |
| EXPECT_EQ(Result::kOk, version_->FinishExternalRequest(uuid1)); |
| EXPECT_EQ(Result::kBadRequestId, version_->FinishExternalRequest(uuid1)); |
| |
| run_loop.Run(); |
| } |
| |
| // Tests worker lifetime with ServiceWorkerVersion::StartExternalRequest. |
| TEST_P(ServiceWorkerVersionTest, WorkerLifetimeWithExternalRequest) { |
| SetupTestTickClock(); |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| |
| auto start_external_request_test = |
| [&](ServiceWorkerExternalRequestTimeoutType timeout_type, |
| bool expect_running) { |
| SCOPED_TRACE(testing::Message() |
| << std::boolalpha << "expect_running: " << expect_running); |
| { |
| // Start worker. |
| base::RunLoop run_loop; |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| ASSERT_EQ(blink::EmbeddedWorkerStatus::kStarting, |
| version_->running_status()); |
| |
| // Add an external request. |
| EXPECT_EQ(ServiceWorkerExternalRequestResult::kOk, |
| version_->StartExternalRequest( |
| base::Uuid::GenerateRandomV4(), timeout_type)); |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, |
| version_->running_status()); |
| } |
| |
| // Now advance time to check worker's running state. |
| tick_clock_.Advance(kTestTimeoutBeyondRequestTimeout); |
| version_->timeout_timer_.user_task().Run(); |
| base::RunLoop().RunUntilIdle(); |
| |
| version_->OnPongFromWorker(); // Avoids ping timeout. |
| const bool worker_stopped_or_stopping = |
| version_->OnRequestTermination(); |
| |
| EXPECT_EQ(!expect_running, worker_stopped_or_stopping); |
| const bool worker_running = |
| version_->running_status() == blink::EmbeddedWorkerStatus::kRunning; |
| EXPECT_EQ(expect_running, worker_running); |
| |
| // Ensure the worker is stopped, so that start_external_request_test() |
| // works next time. |
| { |
| bool has_stopped = false; |
| base::RunLoop run_loop; |
| version_->StopWorker( |
| VerifyCalled(&has_stopped, run_loop.QuitClosure())); |
| run_loop.Run(); |
| EXPECT_TRUE(has_stopped); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, |
| version_->running_status()); |
| } |
| }; |
| |
| start_external_request_test(ServiceWorkerExternalRequestTimeoutType::kDefault, |
| // External request with kDefault timeout stops |
| // the worker after default timeout. |
| false /* expect_running */); |
| start_external_request_test( |
| ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout, |
| // External request with kDoesNotTimeout timeout keeps the worker running |
| // beyond the default timeout. |
| true /* expect_running */); |
| } |
| |
| // Tests that a worker containing external request with |
| // ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout is not |
| // stopped by an external request with "default" timeout. |
| // |
| // Regression test for https://crbug.com/1189678 |
| TEST_P(ServiceWorkerVersionTest, |
| DefaultTimeoutRequestDoesNotAffectMaxTimeoutRequest) { |
| SetupTestTickClock(); |
| std::optional<blink::ServiceWorkerStatusCode> status; |
| |
| using ReqTimeoutType = ServiceWorkerExternalRequestTimeoutType; |
| { |
| // Start worker. |
| base::RunLoop run_loop; |
| version_->StartWorker( |
| ServiceWorkerMetrics::EventType::UNKNOWN, |
| ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure())); |
| ASSERT_EQ(blink::EmbeddedWorkerStatus::kStarting, |
| version_->running_status()); |
| |
| // Add an external request, with kDoesNotTimeout timeout. |
| EXPECT_EQ(ServiceWorkerExternalRequestResult::kOk, |
| version_->StartExternalRequest(base::Uuid::GenerateRandomV4(), |
| ReqTimeoutType::kDoesNotTimeout)); |
| run_loop.Run(); |
| EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value()); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, |
| version_->running_status()); |
| |
| // Add another external request with kDefault timeout. |
| EXPECT_EQ(ServiceWorkerExternalRequestResult::kOk, |
| version_->StartExternalRequest(base::Uuid::GenerateRandomV4(), |
| ReqTimeoutType::kDefault)); |
| } |
| |
| // Now advance time to check worker's running state. |
| tick_clock_.Advance(kTestTimeoutBeyondRequestTimeout); |
| version_->timeout_timer_.user_task().Run(); |
| version_->OnPongFromWorker(); // Avoids ping timeout. |
| base::RunLoop().RunUntilIdle(); |
| |
| // Expect the worker to be still running. |
| const bool worker_stopped_or_stopping = version_->OnRequestTermination(); |
| EXPECT_FALSE(worker_stopped_or_stopping); |
| EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, SetResources) { |
| // Create a new version |
| scoped_refptr<ServiceWorkerVersion> version = CreateNewServiceWorkerVersion( |
| helper_->context()->registry(), registration_.get(), |
| GURL("https://www.example.com/test/service_worker.js"), |
| blink::mojom::ScriptType::kClassic); |
| |
| // The checksum is empty because still no resource records. |
| EXPECT_FALSE(version->sha256_script_checksum()); |
| |
| // Set resource records. |
| std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> records; |
| records.push_back(WriteToDiskCacheWithIdSync( |
| helper_->context()->GetStorageControl(), version->script_url(), 10, |
| {} /* headers */, "I'm a body", "I'm a meta data")); |
| |
| // Set fetch_handler_type, which is refereed in SetResources(). |
| version->set_fetch_handler_type( |
| ServiceWorkerVersion::FetchHandlerType::kNotSkippable); |
| version->SetResources(records); |
| |
| // The checksum has been calculated after the SetResources. |
| EXPECT_EQ("CBE5CFDF7C2118A9C3D78EF1D684F3AFA089201352886449A06A6511CFEF74A7", |
| version->sha256_script_checksum()); |
| } |
| |
| class ServiceWorkerVersionStaticRouterTest : public ServiceWorkerVersionTest { |
| public: |
| ServiceWorkerVersionStaticRouterTest() { |
| feature_list_.InitWithFeatures({features::kServiceWorkerStaticRouter}, {}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ServiceWorkerVersionStaticRouterTest, |
| testing::ValuesIn({StorageKeyTestCase::kFirstParty, |
| StorageKeyTestCase::kThirdParty}), |
| [](const testing::TestParamInfo<StorageKeyTestCase>& info) { |
| switch (info.param) { |
| case (StorageKeyTestCase::kFirstParty): |
| return "FirstPartyStorageKey"; |
| case (StorageKeyTestCase::kThirdParty): |
| return "ThirdPartyStorageKey"; |
| } |
| }); |
| |
| TEST_P(ServiceWorkerVersionStaticRouterTest, SetRouterEvaluator) { |
| // Create a new version |
| scoped_refptr<ServiceWorkerVersion> version = CreateNewServiceWorkerVersion( |
| helper_->context()->registry(), registration_.get(), |
| GURL("https://www.example.com/test/service_worker.js"), |
| blink::mojom::ScriptType::kClassic); |
| version->set_fetch_handler_type( |
| ServiceWorkerVersion::FetchHandlerType::kNotSkippable); |
| |
| // The router_evaluator should be unset on setup. |
| EXPECT_FALSE(version->router_evaluator()); |
| |
| // Leave the router_evaluator unset for invalid rules. |
| { |
| // No condition & source rule is invalid. |
| blink::ServiceWorkerRouterRules rules; |
| blink::ServiceWorkerRouterRule rule; |
| rules.rules.emplace_back(rule); |
| EXPECT_FALSE(version->SetupRouterEvaluator(rules)); |
| EXPECT_FALSE(version->router_evaluator()); |
| } |
| |
| // Set correct rules will make the router_evaluator() return non-null. |
| { |
| blink::ServiceWorkerRouterRules rules; |
| blink::ServiceWorkerRouterRule rule; |
| rule.condition = blink::ServiceWorkerRouterCondition::WithRunningStatus( |
| {blink::ServiceWorkerRouterRunningStatusCondition::RunningStatusEnum:: |
| kRunning}); |
| blink::ServiceWorkerRouterSource source; |
| source.type = blink::ServiceWorkerRouterSource::Type::kNetwork; |
| source.network_source = blink::ServiceWorkerRouterNetworkSource{}; |
| rule.sources.emplace_back(source); |
| rules.rules.emplace_back(rule); |
| EXPECT_TRUE(version->SetupRouterEvaluator(rules)); |
| EXPECT_TRUE(version->router_evaluator()); |
| } |
| |
| // SetupRouterEvaluator() will merge existing rules if router_evaluator() |
| // already exists. |
| { |
| EXPECT_TRUE(version->router_evaluator()); |
| EXPECT_EQ(version->router_evaluator()->rules().rules.size(), 1UL); |
| blink::ServiceWorkerRouterRules rules; |
| blink::ServiceWorkerRouterRule rule; |
| rule.condition = blink::ServiceWorkerRouterCondition::WithRunningStatus( |
| {blink::ServiceWorkerRouterRunningStatusCondition::RunningStatusEnum:: |
| kNotRunning}); |
| blink::ServiceWorkerRouterSource source; |
| source.type = blink::ServiceWorkerRouterSource::Type::kFetchEvent; |
| source.fetch_event_source = blink::ServiceWorkerRouterFetchEventSource{}; |
| rule.sources.emplace_back(source); |
| rules.rules.emplace_back(rule); |
| EXPECT_TRUE(version->SetupRouterEvaluator(rules)); |
| EXPECT_TRUE(version->router_evaluator()); |
| EXPECT_EQ(version->router_evaluator()->rules().rules.size(), 2UL); |
| auto first_rule = version->router_evaluator()->rules().rules[0]; |
| auto second_rule = version->router_evaluator()->rules().rules[1]; |
| auto&& [first_url_pattern, first_request, first_running_status, |
| first_or_condition] = first_rule.condition.get(); |
| EXPECT_EQ(first_running_status->status, |
| blink::ServiceWorkerRouterRunningStatusCondition:: |
| RunningStatusEnum::kRunning); |
| EXPECT_EQ(first_rule.sources.begin()->type, |
| blink::ServiceWorkerRouterSource::Type::kNetwork); |
| auto&& [second_url_pattern, second_request, second_running_status, |
| second_or_condition] = second_rule.condition.get(); |
| EXPECT_EQ(second_running_status->status, |
| blink::ServiceWorkerRouterRunningStatusCondition:: |
| RunningStatusEnum::kNotRunning); |
| EXPECT_EQ(second_rule.sources.begin()->type, |
| blink::ServiceWorkerRouterSource::Type::kFetchEvent); |
| } |
| } |
| |
| // An instance client for controlling whether it has hid event handlers or not. |
| class HidEventHandlerClient : public FakeEmbeddedWorkerInstanceClient { |
| public: |
| HidEventHandlerClient(EmbeddedWorkerTestHelper* helper, |
| bool has_hid_event_handlers) |
| : FakeEmbeddedWorkerInstanceClient(helper), |
| has_hid_event_handlers_(has_hid_event_handlers) {} |
| |
| HidEventHandlerClient(const NoFetchHandlerClient&) = delete; |
| HidEventHandlerClient& operator=(const NoFetchHandlerClient&) = delete; |
| |
| void EvaluateScript() override { |
| host()->OnScriptEvaluationStart(); |
| host()->OnStarted( |
| blink::mojom::ServiceWorkerStartStatus::kNormalCompletion, |
| blink::mojom::ServiceWorkerFetchHandlerType::kNotSkippable, |
| has_hid_event_handlers_, /*has_usb_event_handlers_=*/false, |
| helper()->GetNextThreadId(), |
| blink::mojom::EmbeddedWorkerStartTiming::New()); |
| } |
| |
| private: |
| bool has_hid_event_handlers_; |
| }; |
| |
| TEST_P(ServiceWorkerVersionTest, HasHidEventHandler) { |
| helper_->AddNewPendingInstanceClient<HidEventHandlerClient>( |
| helper_.get(), /*has_hid_event_handlers*/ true); |
| StartServiceWorker(version_.get()); |
| EXPECT_TRUE(version_->has_hid_event_handlers()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, NoHidEventHandler) { |
| helper_->AddNewPendingInstanceClient<HidEventHandlerClient>( |
| helper_.get(), /*has_hid_event_handlers*/ false); |
| StartServiceWorker(version_.get()); |
| EXPECT_FALSE(version_->has_hid_event_handlers()); |
| } |
| |
| // An instance client for controlling whether it has hid event handlers or not. |
| class UsbEventHandlerClient : public FakeEmbeddedWorkerInstanceClient { |
| public: |
| UsbEventHandlerClient(EmbeddedWorkerTestHelper* helper, |
| bool has_usb_event_handlers) |
| : FakeEmbeddedWorkerInstanceClient(helper), |
| has_usb_event_handlers_(has_usb_event_handlers) {} |
| |
| UsbEventHandlerClient(const NoFetchHandlerClient&) = delete; |
| UsbEventHandlerClient& operator=(const NoFetchHandlerClient&) = delete; |
| |
| void EvaluateScript() override { |
| host()->OnScriptEvaluationStart(); |
| host()->OnStarted( |
| blink::mojom::ServiceWorkerStartStatus::kNormalCompletion, |
| blink::mojom::ServiceWorkerFetchHandlerType::kNotSkippable, |
| /*has_hid_event_handlers_=*/false, has_usb_event_handlers_, |
| helper()->GetNextThreadId(), |
| blink::mojom::EmbeddedWorkerStartTiming::New()); |
| } |
| |
| private: |
| bool has_usb_event_handlers_; |
| }; |
| |
| TEST_P(ServiceWorkerVersionTest, HasUsbEventHandler) { |
| helper_->AddNewPendingInstanceClient<UsbEventHandlerClient>( |
| helper_.get(), /*has_usb_event_handlers*/ true); |
| StartServiceWorker(version_.get()); |
| EXPECT_TRUE(version_->has_usb_event_handlers()); |
| } |
| |
| TEST_P(ServiceWorkerVersionTest, NoUsbEventHandler) { |
| helper_->AddNewPendingInstanceClient<UsbEventHandlerClient>( |
| helper_.get(), /*has_usb_event_handlers*/ false); |
| StartServiceWorker(version_.get()); |
| EXPECT_FALSE(version_->has_usb_event_handlers()); |
| } |
| |
| } // namespace service_worker_version_unittest |
| } // namespace content |