diff --git a/content/browser/service_worker/service_worker_main_resource_handle.cc b/content/browser/service_worker/service_worker_main_resource_handle.cc index 4cd3f0044247e..39d924da3c49e 100644 --- a/content/browser/service_worker/service_worker_main_resource_handle.cc +++ b/content/browser/service_worker/service_worker_main_resource_handle.cc @@ -102,10 +102,13 @@ bool ServiceWorkerMainResourceHandle::InitializeForRequest( CHECK(base::FeatureList::IsEnabled(features::kPrefetchServiceWorker)); CHECK(service_worker_client()->IsContainerForWindowClient()); CHECK(client_for_prefetch->IsContainerForWindowClient()); - // If top_frame_origin/key don't match, do not use the prefetch result. + // If `client_for_prefetch` was created for a different + // ServiceWorkerContextCore (i.e. a different StoragePartition), or if its + // top_frame_origin/key don't match, do not use the prefetch result. // `this` remains unmodified, and `InitializeForRequest` will be called // later in the non-prefetch code path. - if (client_for_prefetch->top_frame_origin() != top_frame_origin || + if (client_for_prefetch->context().get() != context_wrapper_->context() || + client_for_prefetch->top_frame_origin() != top_frame_origin || client_for_prefetch->key() != key) { return false; } diff --git a/content/browser/service_worker/service_worker_main_resource_handle_unittest.cc b/content/browser/service_worker/service_worker_main_resource_handle_unittest.cc new file mode 100644 index 0000000000000..d454701eb8160 --- /dev/null +++ b/content/browser/service_worker/service_worker_main_resource_handle_unittest.cc @@ -0,0 +1,148 @@ +// Copyright 2026 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_main_resource_handle.h" + +#include +#include + +#include "base/functional/callback_helpers.h" +#include "content/browser/service_worker/embedded_worker_test_helper.h" +#include "content/browser/service_worker/service_worker_client.h" +#include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "content/browser/service_worker/service_worker_registration.h" +#include "content/browser/service_worker/service_worker_test_utils.h" +#include "content/browser/service_worker/service_worker_version.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/policy_container_utils.h" +#include "net/base/isolation_info.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/storage_key/storage_key.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h" +#include "url/gurl.h" +#include "url/origin.h" + +namespace content { + +class ServiceWorkerMainResourceHandleTest : public testing::Test { + protected: + ServiceWorkerMainResourceHandleTest() + : task_environment_(BrowserTaskEnvironment::IO_MAINLOOP) {} + + void SetUp() override { + helper_ = std::make_unique(base::FilePath()); + } + + void TearDown() override { helper_.reset(); } + + // Creates a handle for `context_wrapper` with a fresh window client and an + // initial isolation info derived from `top_frame_origin`. + std::unique_ptr CreateHandle( + ServiceWorkerContextWrapper* context_wrapper, + const url::Origin& top_frame_origin) { + auto handle = std::make_unique( + context_wrapper, base::DoNothing(), + /*fetch_event_client_id=*/std::string()); + handle->set_service_worker_client( + CreateServiceWorkerClient(context_wrapper->context()), + net::IsolationInfo::CreateForInternalRequest(top_frame_origin)); + return handle; + } + + // Creates a registration with an active version in `context` whose scope is + // the origin of `url`. + scoped_refptr CreateActivatedRegistration( + ServiceWorkerContextCore* context, + const GURL& url) { + blink::mojom::ServiceWorkerRegistrationOptions options; + options.scope = url.DeprecatedGetOriginAsURL(); + auto registration = ServiceWorkerRegistration::Create( + options, blink::StorageKey::CreateFirstParty(url::Origin::Create(url)), + /*registration_id=*/1L, context->AsWeakPtr(), + blink::mojom::AncestorFrameType::kNormalFrame); + auto version = base::MakeRefCounted( + registration.get(), options.scope.Resolve("sw.js"), + blink::mojom::ScriptType::kClassic, + /*version_id=*/1L, + mojo::PendingRemote(), + context->AsWeakPtr(), + /*creator_network_restrictions_id=*/std::nullopt, + /*network_restrictions_id=*/std::nullopt, PolicyContainerPolicies()); + version->set_fetch_handler_type( + ServiceWorkerVersion::FetchHandlerType::kNotSkippable); + version->SetStatus(ServiceWorkerVersion::ACTIVATED); + registration->SetActiveVersion(version); + return registration; + } + + // Creates a window client in `context` for `url` and sets `registration` as + // its controller. + ScopedServiceWorkerClient CreateControlledPrefetchClient( + ServiceWorkerContextCore* context, + const GURL& url, + scoped_refptr registration) { + ScopedServiceWorkerClient client = + CreateServiceWorkerClient(context, url, url::Origin::Create(url)); + client->AddMatchingRegistration(registration.get()); + client->SetControllerRegistration(std::move(registration), + /*notify_controllerchange=*/false); + return client; + } + + BrowserTaskEnvironment task_environment_; + std::unique_ptr helper_; +}; + +TEST_F(ServiceWorkerMainResourceHandleTest, + InitializeForRequestWithPrefetchClientFromSameContext) { + const GURL kUrl("https://www.example.com/page.html"); + const url::Origin kOrigin = url::Origin::Create(kUrl); + + scoped_refptr registration = + CreateActivatedRegistration(helper_->context(), kUrl); + ScopedServiceWorkerClient client_for_prefetch = + CreateControlledPrefetchClient(helper_->context(), kUrl, registration); + ASSERT_EQ(registration.get(), client_for_prefetch->controller_registration()); + + auto handle = CreateHandle(helper_->context_wrapper(), kOrigin); + ASSERT_EQ(handle->service_worker_client()->context().get(), + client_for_prefetch->context().get()); + + EXPECT_TRUE( + handle->InitializeForRequest(kUrl, kOrigin, client_for_prefetch.get())); + EXPECT_EQ(registration.get(), + handle->service_worker_client()->controller_registration()); +} + +TEST_F(ServiceWorkerMainResourceHandleTest, + InitializeForRequestWithPrefetchClientFromDifferentContext) { + // A second ServiceWorkerContextCore, e.g. as for a different + // StoragePartition. + auto other_helper = + std::make_unique(base::FilePath()); + ASSERT_NE(helper_->context(), other_helper->context()); + + const GURL kUrl("https://www.example.com/page.html"); + const url::Origin kOrigin = url::Origin::Create(kUrl); + + scoped_refptr registration = + CreateActivatedRegistration(other_helper->context(), kUrl); + ScopedServiceWorkerClient client_for_prefetch = + CreateControlledPrefetchClient(other_helper->context(), kUrl, + registration); + ASSERT_EQ(registration.get(), client_for_prefetch->controller_registration()); + + auto handle = CreateHandle(helper_->context_wrapper(), kOrigin); + ASSERT_NE(handle->service_worker_client()->context().get(), + client_for_prefetch->context().get()); + + // The prefetch client belongs to a different ServiceWorkerContextCore than + // the navigation handle, so its controller must not be inherited. + EXPECT_FALSE( + handle->InitializeForRequest(kUrl, kOrigin, client_for_prefetch.get())); + EXPECT_FALSE(handle->service_worker_client()->controller_registration()); +} + +} // namespace content diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 4734e1c2db658..6aee34b858657 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn @@ -3006,6 +3006,7 @@ test("content_unittests") { "../browser/service_worker/service_worker_installed_scripts_sender_unittest.cc", "../browser/service_worker/service_worker_job_unittest.cc", "../browser/service_worker/service_worker_loader_helpers_unittest.cc", + "../browser/service_worker/service_worker_main_resource_handle_unittest.cc", "../browser/service_worker/service_worker_main_resource_loader_interceptor_unittest.cc", "../browser/service_worker/service_worker_main_resource_loader_unittest.cc", "../browser/service_worker/service_worker_metrics_unittest.cc",