diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc | 272 |
1 files changed, 246 insertions, 26 deletions
diff --git a/chromium/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc b/chromium/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc index 8153e348947..1ae16b666e3 100644 --- a/chromium/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc +++ b/chromium/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc @@ -6,15 +6,20 @@ #include <memory> +#include "base/feature_list.h" #include "base/synchronization/waitable_event.h" +#include "base/test/scoped_feature_list.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/messaging/message_port_channel.h" +#include "third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom-blink.h" #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom-blink.h" #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom-blink.h" #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_content_settings_client.h" +#include "third_party/blink/public/platform/web_url_loader_client.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" #include "third_party/blink/public/platform/web_url_response.h" #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h" @@ -25,14 +30,17 @@ #include "third_party/blink/renderer/modules/service_worker/thread_safe_script_container.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" namespace blink { namespace { +const char* kNotFoundScriptURL = "https://www.example.com/sw-404.js"; + // Fake network provider for service worker execution contexts. -class FakeServiceWorkerNetworkProvider +class FakeServiceWorkerNetworkProvider final : public WebServiceWorkerNetworkProvider { public: FakeServiceWorkerNetworkProvider() = default; @@ -62,7 +70,85 @@ class FakeServiceWorkerNetworkProvider void DispatchNetworkQuiet() override {} }; -class MockServiceWorkerContextClient : public WebServiceWorkerContextClient { +// A fake WebURLLoader which is used for off-main-thread script fetch tests. +class FakeWebURLLoader final : public WebURLLoader { + public: + FakeWebURLLoader() {} + ~FakeWebURLLoader() override = default; + + void LoadSynchronously(const WebURLRequest&, + WebURLLoaderClient*, + WebURLResponse&, + base::Optional<WebURLError>&, + WebData&, + int64_t& encoded_data_length, + int64_t& encoded_body_length, + WebBlobInfo& downloaded_blob) override { + NOTREACHED(); + } + + void LoadAsynchronously(const WebURLRequest& request, + WebURLLoaderClient* client) override { + if (request.Url().GetString() == kNotFoundScriptURL) { + WebURLResponse response; + response.SetMimeType("text/javascript"); + response.SetHttpStatusCode(404); + client->DidReceiveResponse(response); + client->DidFinishLoading(TimeTicks(), 0, 0, 0, false, {}); + return; + } + // Don't handle other requests intentionally to emulate ongoing load. + } + + void Cancel() override {} + void SetDefersLoading(bool defers) override {} + void DidChangePriority(WebURLRequest::Priority, int) override {} + scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override { + return base::MakeRefCounted<scheduler::FakeTaskRunner>(); + } +}; + +// A fake WebURLLoaderFactory which is used for off-main-thread script fetch +// tests. +class FakeWebURLLoaderFactory final : public WebURLLoaderFactory { + public: + std::unique_ptr<WebURLLoader> CreateURLLoader( + const WebURLRequest&, + std::unique_ptr<scheduler::WebResourceLoadingTaskRunnerHandle>) override { + return std::make_unique<FakeWebURLLoader>(); + } +}; + +// A fake WebWorkerFetchContext which is used for off-main-thread script fetch +// tests. +class FakeWebWorkerFetchContext final : public WebWorkerFetchContext { + public: + void SetTerminateSyncLoadEvent(base::WaitableEvent*) override {} + void InitializeOnWorkerThread(AcceptLanguagesWatcher*) override {} + WebURLLoaderFactory* GetURLLoaderFactory() override { + return &fake_web_url_loader_factory_; + } + std::unique_ptr<WebURLLoaderFactory> WrapURLLoaderFactory( + mojo::ScopedMessagePipeHandle url_loader_factory_handle) override { + return nullptr; + } + void WillSendRequest(WebURLRequest&) override {} + mojom::ControllerServiceWorkerMode IsControlledByServiceWorker() + const override { + return mojom::ControllerServiceWorkerMode::kNoController; + } + WebURL SiteForCookies() const override { return WebURL(); } + base::Optional<WebSecurityOrigin> TopFrameOrigin() const override { + return base::Optional<WebSecurityOrigin>(); + } + WebString GetAcceptLanguages() const override { return WebString(); } + + private: + FakeWebURLLoaderFactory fake_web_url_loader_factory_; +}; + +class MockServiceWorkerContextClient final + : public WebServiceWorkerContextClient { public: MockServiceWorkerContextClient() = default; ~MockServiceWorkerContextClient() override = default; @@ -78,6 +164,14 @@ class MockServiceWorkerContextClient : public WebServiceWorkerContextClient { // message. proxy->ReadyToEvaluateScript(); } + + void FailedToLoadClassicScript() override { + // off-main-script fetch: + // In production code, calling FailedToLoadClassicScript results in + // terminating the worker. + classic_script_load_failure_event_.Signal(); + } + void DidEvaluateScript(bool /* success */) override { script_evaluated_event_.Signal(); } @@ -86,14 +180,26 @@ class MockServiceWorkerContextClient : public WebServiceWorkerContextClient { CreateServiceWorkerNetworkProviderOnMainThread() override { return std::make_unique<FakeServiceWorkerNetworkProvider>(); } + + scoped_refptr<WebWorkerFetchContext> + CreateServiceWorkerFetchContextOnMainThread( + WebServiceWorkerNetworkProvider* network_provider) override { + return base::MakeRefCounted<FakeWebWorkerFetchContext>(); + } + void WorkerContextDestroyed() override { termination_event_.Signal(); } + // These methods must be called on the main thread. void WaitUntilScriptEvaluated() { script_evaluated_event_.Wait(); } void WaitUntilThreadTermination() { termination_event_.Wait(); } + void WaitUntilFailedToLoadClassicScript() { + classic_script_load_failure_event_.Wait(); + } private: base::WaitableEvent script_evaluated_event_; base::WaitableEvent termination_event_; + base::WaitableEvent classic_script_load_failure_event_; }; class MockServiceWorkerInstalledScriptsManager @@ -123,18 +229,17 @@ class MockServiceWorkerInstalledScriptsManager class WebEmbeddedWorkerImplTest : public testing::Test { protected: void SetUp() override { - auto client = std::make_unique<MockServiceWorkerContextClient>(); auto installed_scripts_manager = std::make_unique<MockServiceWorkerInstalledScriptsManager>(); - mock_client_ = client.get(); mock_installed_scripts_manager_ = installed_scripts_manager.get(); + mock_client_ = std::make_unique<MockServiceWorkerContextClient>(); worker_ = WebEmbeddedWorkerImpl::CreateForTesting( - std::move(client), std::move(installed_scripts_manager)); + mock_client_.get(), std::move(installed_scripts_manager)); WebURL script_url = url_test_helpers::ToKURL("https://www.example.com/sw.js"); WebURLResponse response(script_url); - response.SetMIMEType("text/javascript"); + response.SetMimeType("text/javascript"); response.SetHttpStatusCode(200); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(script_url, response, ""); @@ -156,7 +261,7 @@ class WebEmbeddedWorkerImplTest : public testing::Test { } WebEmbeddedWorkerStartData start_data_; - MockServiceWorkerContextClient* mock_client_; + std::unique_ptr<MockServiceWorkerContextClient> mock_client_; MockServiceWorkerInstalledScriptsManager* mock_installed_scripts_manager_; std::unique_ptr<WebEmbeddedWorkerImpl> worker_; }; @@ -164,28 +269,69 @@ class WebEmbeddedWorkerImplTest : public testing::Test { } // namespace TEST_F(WebEmbeddedWorkerImplTest, TerminateSoonAfterStart) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndDisableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); worker_->StartWorkerContext(start_data_); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1); worker_->TerminateWorkerContext(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); +} + +TEST_F(WebEmbeddedWorkerImplTest, TerminateSoonAfterStart_OMT_Fetch) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndEnableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + + EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); + worker_->StartWorkerContext(start_data_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); + + worker_->TerminateWorkerContext(); + // The worker thread was started. Wait for shutdown tasks to finish. + worker_->WaitForShutdownForTesting(); } TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileWaitingForDebugger) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndDisableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); start_data_.wait_for_debugger_mode = WebEmbeddedWorkerStartData::kWaitForDebugger; worker_->StartWorkerContext(start_data_); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1); worker_->TerminateWorkerContext(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); +} + +TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileWaitingForDebugger_OMT_Fetch) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndEnableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + + EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); + start_data_.wait_for_debugger_mode = + WebEmbeddedWorkerStartData::kWaitForDebugger; + worker_->StartWorkerContext(start_data_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); + + worker_->TerminateWorkerContext(); + // The worker thread isn't started yet so we don't have to wait for shutdown. } TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileLoadingScript) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndDisableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + // Load the shadow page. EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); EXPECT_CALL(*mock_installed_scripts_manager_, @@ -193,16 +339,45 @@ TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileLoadingScript) { .Times(testing::AtLeast(1)) .WillRepeatedly(testing::Return(false)); worker_->StartWorkerContext(start_data_); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); // Terminate before loading the script. EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1); worker_->TerminateWorkerContext(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); +} + +TEST_F(WebEmbeddedWorkerImplTest, TerminateWhileLoadingScript_OMT_Fetch) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndEnableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + + // Load the shadow page. + EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); + EXPECT_CALL(*mock_installed_scripts_manager_, + IsScriptInstalled(KURL(start_data_.script_url))) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(false)); + worker_->StartWorkerContext(start_data_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); + testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); + + // Terminate before finishing the script load. + worker_->TerminateWorkerContext(); + // The worker thread was started. Wait for shutdown tasks to finish. + worker_->WaitForShutdownForTesting(); } +// Tests terminating worker context between script download and execution. +// No "OMT_Fetch" variant for this test because "pause after download" doesn't +// have effect for off-main-thread script fetch. When off-main-thread fetch +// is on, pausing/resuming is handled in ServiceWorkerGlobalScope. TEST_F(WebEmbeddedWorkerImplTest, TerminateWhilePausedAfterDownload) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndDisableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + // Load the shadow page. start_data_.pause_after_download_mode = WebEmbeddedWorkerStartData::kPauseAfterDownload; @@ -212,26 +387,29 @@ TEST_F(WebEmbeddedWorkerImplTest, TerminateWhilePausedAfterDownload) { .Times(testing::AtLeast(1)) .WillRepeatedly(testing::Return(false)); worker_->StartWorkerContext(start_data_); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); // Load the script. EXPECT_CALL(*mock_client_, WorkerScriptLoadedOnMainThread()).Times(1); Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); // Terminate before resuming after download. EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1); worker_->TerminateWorkerContext(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); } TEST_F(WebEmbeddedWorkerImplTest, ScriptNotFound) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndDisableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + // Load the shadow page. - WebURL script_url = - url_test_helpers::ToKURL("https://www.example.com/sw-404.js"); + WebURL script_url = url_test_helpers::ToKURL(kNotFoundScriptURL); WebURLResponse response; - response.SetMIMEType("text/javascript"); + response.SetMimeType("text/javascript"); response.SetHttpStatusCode(404); ResourceError error = ResourceError::Failure(script_url); Platform::Current()->GetURLLoaderMockFactory()->RegisterErrorURL( @@ -243,24 +421,61 @@ TEST_F(WebEmbeddedWorkerImplTest, ScriptNotFound) { .Times(testing::AtLeast(1)) .WillRepeatedly(testing::Return(false)); worker_->StartWorkerContext(start_data_); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); // Load the script. EXPECT_CALL(*mock_client_, WorkerScriptLoadedOnMainThread()).Times(0); EXPECT_CALL(*mock_client_, WorkerContextFailedToStartOnMainThread()).Times(1); Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); +} + +TEST_F(WebEmbeddedWorkerImplTest, ScriptNotFound_OMT_Fetch) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndEnableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + + // Load the shadow page. + WebURL script_url = url_test_helpers::ToKURL(kNotFoundScriptURL); + WebURLResponse response; + response.SetMimeType("text/javascript"); + response.SetHttpStatusCode(404); + ResourceError error = ResourceError::Failure(script_url); + Platform::Current()->GetURLLoaderMockFactory()->RegisterErrorURL( + script_url, response, error); + start_data_.script_url = script_url; + EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); + EXPECT_CALL(*mock_installed_scripts_manager_, + IsScriptInstalled(KURL(start_data_.script_url))) + .Times(testing::AtLeast(1)) + .WillRepeatedly(testing::Return(false)); + // Start worker and load the script. + worker_->StartWorkerContext(start_data_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); + testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); + + mock_client_->WaitUntilFailedToLoadClassicScript(); + + // The worker thread was started. Ask to shutdown and wait for shutdown + // tasks to finish. + worker_->TerminateWorkerContext(); + worker_->WaitForShutdownForTesting(); } // The running worker is detected as a memory leak. crbug.com/586897 and // crbug.com/807754. +// No "OMT_Fetch" variant. See comments on TerminateWhilePausedAfterDownload. #if defined(ADDRESS_SANITIZER) #define MAYBE_DontPauseAfterDownload DISABLED_DontPauseAfterDownload #else #define MAYBE_DontPauseAfterDownload DontPauseAfterDownload #endif TEST_F(WebEmbeddedWorkerImplTest, MAYBE_DontPauseAfterDownload) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndDisableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + // Load the shadow page. EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); EXPECT_CALL(*mock_installed_scripts_manager_, @@ -268,7 +483,7 @@ TEST_F(WebEmbeddedWorkerImplTest, MAYBE_DontPauseAfterDownload) { .Times(testing::AtLeast(1)) .WillRepeatedly(testing::Return(false)); worker_->StartWorkerContext(start_data_); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); // Load the script. @@ -280,7 +495,7 @@ TEST_F(WebEmbeddedWorkerImplTest, MAYBE_DontPauseAfterDownload) { .WillRepeatedly(testing::Return(false)); Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); mock_client_->WaitUntilScriptEvaluated(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); // Terminate the running worker thread. @@ -291,12 +506,17 @@ TEST_F(WebEmbeddedWorkerImplTest, MAYBE_DontPauseAfterDownload) { // The running worker is detected as a memory leak. crbug.com/586897 and // crbug.com/807754. +// No "OMT_Fetch" variant. See comments on TerminateWhilePausedAfterDownload. #if defined(ADDRESS_SANITIZER) #define MAYBE_PauseAfterDownload DISABLED_PauseAfterDownload #else #define MAYBE_PauseAfterDownload PauseAfterDownload #endif TEST_F(WebEmbeddedWorkerImplTest, MAYBE_PauseAfterDownload) { + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndDisableFeature( + features::kOffMainThreadServiceWorkerScriptFetch); + // Load the shadow page. EXPECT_CALL(*mock_client_, WorkerReadyForInspectionOnMainThread()).Times(1); EXPECT_CALL(*mock_installed_scripts_manager_, @@ -306,13 +526,13 @@ TEST_F(WebEmbeddedWorkerImplTest, MAYBE_PauseAfterDownload) { start_data_.pause_after_download_mode = WebEmbeddedWorkerStartData::kPauseAfterDownload; worker_->StartWorkerContext(start_data_); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); // Load the script. EXPECT_CALL(*mock_client_, WorkerScriptLoadedOnMainThread()).Times(1); Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); // Resume after download. // This is called on the worker thread. @@ -322,7 +542,7 @@ TEST_F(WebEmbeddedWorkerImplTest, MAYBE_PauseAfterDownload) { .WillRepeatedly(testing::Return(false)); worker_->ResumeAfterDownload(); mock_client_->WaitUntilScriptEvaluated(); - testing::Mock::VerifyAndClearExpectations(mock_client_); + testing::Mock::VerifyAndClearExpectations(mock_client_.get()); testing::Mock::VerifyAndClearExpectations(mock_installed_scripts_manager_); // Terminate the running worker thread. |