blob: 86b89f2e48fa3991029125f465dcc520d5f24fe6 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/service_worker/service_worker_provider_host.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/browser/service_worker/embedded_worker_test_helper.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_register_job.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/public/common/origin_util.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/test/test_content_browser_client.h"
#include "content/test/test_content_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
const char kServiceWorkerScheme[] = "i-can-use-service-worker";
class ServiceWorkerTestContentClient : public TestContentClient {
public:
void AddServiceWorkerSchemes(std::set<std::string>* schemes) override {
schemes->insert(kServiceWorkerScheme);
}
};
class ServiceWorkerProviderHostTest : public testing::Test {
protected:
ServiceWorkerProviderHostTest()
: thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {
SetContentClient(&test_content_client_);
}
~ServiceWorkerProviderHostTest() override {}
void SetUp() override {
old_content_browser_client_ =
SetBrowserClientForTesting(&test_content_browser_client_);
helper_.reset(new EmbeddedWorkerTestHelper(base::FilePath()));
context_ = helper_->context();
script_url_ = GURL("http://www.example.com/service_worker.js");
registration1_ = new ServiceWorkerRegistration(
GURL("http://www.example.com/"), 1L, context_->AsWeakPtr());
registration2_ = new ServiceWorkerRegistration(
GURL("http://www.example.com/example"), 2L, context_->AsWeakPtr());
// Prepare provider hosts (for the same process).
std::unique_ptr<ServiceWorkerProviderHost> host1(
new ServiceWorkerProviderHost(
helper_->mock_render_process_id(), MSG_ROUTING_NONE,
1 /* provider_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
context_->AsWeakPtr(), NULL));
host1->SetDocumentUrl(GURL("http://www.example.com/example1.html"));
std::unique_ptr<ServiceWorkerProviderHost> host2(
new ServiceWorkerProviderHost(
helper_->mock_render_process_id(), MSG_ROUTING_NONE,
2 /* provider_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
context_->AsWeakPtr(), NULL));
host2->SetDocumentUrl(GURL("http://www.example.com/example2.html"));
provider_host1_ = host1->AsWeakPtr();
provider_host2_ = host2->AsWeakPtr();
context_->AddProviderHost(base::WrapUnique(host1.release()));
context_->AddProviderHost(base::WrapUnique(host2.release()));
}
void TearDown() override {
registration1_ = 0;
registration2_ = 0;
helper_.reset();
SetBrowserClientForTesting(old_content_browser_client_);
}
bool PatternHasProcessToRun(const GURL& pattern) const {
return context_->process_manager()->PatternHasProcessToRun(pattern);
}
content::TestBrowserThreadBundle thread_bundle_;
std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
ServiceWorkerContextCore* context_;
scoped_refptr<ServiceWorkerRegistration> registration1_;
scoped_refptr<ServiceWorkerRegistration> registration2_;
base::WeakPtr<ServiceWorkerProviderHost> provider_host1_;
base::WeakPtr<ServiceWorkerProviderHost> provider_host2_;
GURL script_url_;
ServiceWorkerTestContentClient test_content_client_;
TestContentBrowserClient test_content_browser_client_;
ContentBrowserClient* old_content_browser_client_;
private:
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostTest);
};
TEST_F(ServiceWorkerProviderHostTest, PotentialRegistration_ProcessStatus) {
provider_host1_->AddMatchingRegistration(registration1_.get());
ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
// Adding the same registration twice has no effect.
provider_host1_->AddMatchingRegistration(registration1_.get());
ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
// Different matching registrations can be added.
provider_host1_->AddMatchingRegistration(registration2_.get());
ASSERT_TRUE(PatternHasProcessToRun(registration2_->pattern()));
// Removing a matching registration will decrease the process refs for its
// pattern.
provider_host1_->RemoveMatchingRegistration(registration1_.get());
ASSERT_FALSE(PatternHasProcessToRun(registration1_->pattern()));
// Multiple provider hosts could add the same matching registration.
// The process refs will become 0 after all provider hosts removed them.
provider_host2_->AddMatchingRegistration(registration2_.get());
provider_host1_->RemoveMatchingRegistration(registration2_.get());
ASSERT_TRUE(PatternHasProcessToRun(registration2_->pattern()));
provider_host2_->RemoveMatchingRegistration(registration2_.get());
ASSERT_FALSE(PatternHasProcessToRun(registration2_->pattern()));
}
TEST_F(ServiceWorkerProviderHostTest, AssociatedRegistration_ProcessStatus) {
// Associating the registration will also increase the process refs for
// the registration's pattern.
provider_host1_->AssociateRegistration(registration1_.get(),
false /* notify_controllerchange */);
ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
// Disassociating the registration shouldn't affect the process refs for
// the registration's pattern.
provider_host1_->DisassociateRegistration();
ASSERT_TRUE(PatternHasProcessToRun(registration1_->pattern()));
}
TEST_F(ServiceWorkerProviderHostTest, MatchRegistration) {
provider_host1_->AddMatchingRegistration(registration1_.get());
provider_host1_->AddMatchingRegistration(registration2_.get());
// Match registration should return the longest matching one.
ASSERT_EQ(provider_host1_->MatchRegistration(), registration2_);
provider_host1_->RemoveMatchingRegistration(registration2_.get());
ASSERT_EQ(provider_host1_->MatchRegistration(), registration1_);
// Should return nullptr after removing all matching registrations.
provider_host1_->RemoveMatchingRegistration(registration1_.get());
ASSERT_EQ(provider_host1_->MatchRegistration(), nullptr);
}
TEST_F(ServiceWorkerProviderHostTest, ContextSecurity) {
using FrameSecurityLevel = ServiceWorkerProviderHost::FrameSecurityLevel;
// Insecure document URL.
provider_host1_->SetDocumentUrl(GURL("http://host"));
provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::SECURE;
EXPECT_FALSE(provider_host1_->IsContextSecureForServiceWorker());
// Insecure parent frame.
provider_host1_->SetDocumentUrl(GURL("https://host"));
provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::INSECURE;
EXPECT_FALSE(provider_host1_->IsContextSecureForServiceWorker());
// Secure URL and parent frame.
provider_host1_->SetDocumentUrl(GURL("https://host"));
provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::SECURE;
EXPECT_TRUE(provider_host1_->IsContextSecureForServiceWorker());
// Exceptional service worker scheme.
GURL url(std::string(kServiceWorkerScheme) + "://host");
EXPECT_TRUE(url.is_valid());
provider_host1_->SetDocumentUrl(url);
provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::SECURE;
EXPECT_FALSE(IsOriginSecure(url));
EXPECT_TRUE(OriginCanAccessServiceWorkers(url));
EXPECT_TRUE(provider_host1_->IsContextSecureForServiceWorker());
// Exceptional service worker scheme with insecure parent frame.
provider_host1_->parent_frame_security_level_ = FrameSecurityLevel::INSECURE;
EXPECT_FALSE(provider_host1_->IsContextSecureForServiceWorker());
}
} // namespace content