Commit 986c8c34 authored by Patrick Monette's avatar Patrick Monette Committed by Commit Bot

Create an observer interface for SharedWorkerService

With this interface, a class will be able to observe shared workers as
they start and stop running, and be notified when clients connect to
running instances.

Bug: 993029
Change-Id: I469e937eb04cd7948e9888ba406c93c1b8cebbd6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1762921Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Cr-Commit-Position: refs/heads/master@{#693022}
parent f83e39c3
......@@ -1949,8 +1949,6 @@ jumbo_source_set("browser") {
"worker_host/shared_worker_content_settings_proxy_impl.h",
"worker_host/shared_worker_host.cc",
"worker_host/shared_worker_host.h",
"worker_host/shared_worker_instance.cc",
"worker_host/shared_worker_instance.h",
"worker_host/shared_worker_service_impl.cc",
"worker_host/shared_worker_service_impl.h",
"worker_host/worker_script_fetch_initiator.cc",
......
......@@ -11,8 +11,8 @@
#include "content/browser/service_worker/service_worker_provider_host.h"
#include "content/browser/worker_host/dedicated_worker_host.h"
#include "content/browser/worker_host/shared_worker_host.h"
#include "content/browser/worker_host/shared_worker_instance.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/shared_worker_instance.h"
#include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
#include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom.h"
......
......@@ -11,7 +11,7 @@
#include "base/macros.h"
#include "base/unguessable_token.h"
#include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/worker_host/shared_worker_instance.h"
#include "content/public/browser/shared_worker_instance.h"
namespace content {
......
......@@ -102,6 +102,14 @@ SharedWorkerHost::~SharedWorkerHost() {
// Attempt to notify the worker before disconnecting.
if (worker_)
worker_->Terminate();
// Notify the service that each client still connected will be removed and
// that the worker will terminate.
for (const auto& client : clients_) {
service_->NotifyClientRemoved(instance_, client.client_process_id,
client.frame_id);
}
service_->NotifyWorkerTerminating(instance_);
} else {
// Tell clients that this worker failed to start.
for (const ClientInfo& info : clients_)
......@@ -237,6 +245,15 @@ void SharedWorkerHost::Start(
// Monitor the lifetime of the worker.
worker_.set_disconnect_handler(base::BindOnce(
&SharedWorkerHost::OnWorkerConnectionLost, weak_factory_.GetWeakPtr()));
// Notify the service that the worker was started and that some clients were
// already connected.
service_->NotifyWorkerStarted(instance_, worker_process_id_,
devtools_worker_token);
for (const auto& client : clients_) {
service_->NotifyClientAdded(instance_, client.client_process_id,
client.frame_id);
}
}
// This is similar to
......@@ -425,6 +442,12 @@ void SharedWorkerHost::AddClient(
&SharedWorkerHost::OnClientConnectionLost, weak_factory_.GetWeakPtr()));
worker_->Connect(info.connection_request_id, port.ReleaseHandle());
// Notify that a new client was added now. If the worker is not started, the
// Start() function will handle sending a notification for each existing
// client.
if (started_)
service_->NotifyClientAdded(instance_, client_process_id, frame_id);
}
void SharedWorkerHost::SetAppCacheHandle(
......@@ -456,6 +479,12 @@ void SharedWorkerHost::OnClientConnectionLost() {
// We'll get a notification for each dropped connection.
for (auto it = clients_.begin(); it != clients_.end(); ++it) {
if (!it->client.is_connected()) {
// Notify the service that a client was removed while the worker was
// running.
if (started_) {
service_->NotifyClientRemoved(instance_, it->client_process_id,
it->frame_id);
}
clients_.erase(it);
break;
}
......
......@@ -16,10 +16,10 @@
#include "base/strings/string16.h"
#include "base/unguessable_token.h"
#include "content/browser/browser_interface_broker_impl.h"
#include "content/browser/worker_host/shared_worker_instance.h"
#include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/shared_worker_instance.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
......
......@@ -17,8 +17,8 @@
#include "content/browser/service_worker/service_worker_navigation_handle.h"
#include "content/browser/worker_host/mock_shared_worker.h"
#include "content/browser/worker_host/shared_worker_connector_impl.h"
#include "content/browser/worker_host/shared_worker_instance.h"
#include "content/browser/worker_host/shared_worker_service_impl.h"
#include "content/public/browser/shared_worker_instance.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"
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/worker_host/shared_worker_instance.h"
#include "content/public/browser/shared_worker_instance.h"
#include <memory>
......
......@@ -23,7 +23,6 @@
#include "content/browser/url_loader_factory_getter.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/worker_host/shared_worker_host.h"
#include "content/browser/worker_host/shared_worker_instance.h"
#include "content/browser/worker_host/worker_script_fetch_initiator.h"
#include "content/common/content_constants_internal.h"
#include "content/public/browser/browser_task_traits.h"
......@@ -31,6 +30,7 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/shared_worker_instance.h"
#include "content/public/common/bind_interface_helpers.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
......@@ -67,6 +67,14 @@ SharedWorkerServiceImpl::~SharedWorkerServiceImpl() {
worker_hosts_.clear();
}
void SharedWorkerServiceImpl::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void SharedWorkerServiceImpl::RemoveObserver(const Observer* observer) {
observers_.RemoveObserver(observer);
}
bool SharedWorkerServiceImpl::TerminateWorker(
const GURL& url,
const std::string& name,
......@@ -79,6 +87,7 @@ bool SharedWorkerServiceImpl::TerminateWorker(
DestroyHost(worker_host);
return true;
}
return false;
}
......@@ -169,9 +178,40 @@ void SharedWorkerServiceImpl::ConnectToWorker(
void SharedWorkerServiceImpl::DestroyHost(SharedWorkerHost* host) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
worker_hosts_.erase(worker_hosts_.find(host));
}
void SharedWorkerServiceImpl::NotifyWorkerStarted(
const SharedWorkerInstance& instance,
int worker_process_id,
const base::UnguessableToken& dev_tools_token) {
for (Observer& observer : observers_)
observer.OnWorkerStarted(instance, worker_process_id, dev_tools_token);
}
void SharedWorkerServiceImpl::NotifyWorkerTerminating(
const SharedWorkerInstance& instance) {
for (Observer& observer : observers_)
observer.OnBeforeWorkerTerminated(instance);
}
void SharedWorkerServiceImpl::NotifyClientAdded(
const SharedWorkerInstance& instance,
int client_process_id,
int frame_id) {
for (Observer& observer : observers_)
observer.OnClientAdded(instance, client_process_id, frame_id);
}
void SharedWorkerServiceImpl::NotifyClientRemoved(
const SharedWorkerInstance& instance,
int client_process_id,
int frame_id) {
for (Observer& observer : observers_)
observer.OnClientRemoved(instance, client_process_id, frame_id);
}
void SharedWorkerServiceImpl::CreateWorker(
const SharedWorkerInstance& instance,
blink::mojom::FetchClientSettingsObjectPtr
......
......@@ -13,6 +13,7 @@
#include "base/compiler_specific.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/worker_host/shared_worker_host.h"
#include "content/public/browser/shared_worker_service.h"
......@@ -48,6 +49,8 @@ class CONTENT_EXPORT SharedWorkerServiceImpl : public SharedWorkerService {
~SharedWorkerServiceImpl() override;
// SharedWorkerService implementation.
void AddObserver(Observer* observer) override;
void RemoveObserver(const Observer* observer) override;
bool TerminateWorker(const GURL& url,
const std::string& name,
const url::Origin& constructor_origin) override;
......@@ -72,6 +75,17 @@ class CONTENT_EXPORT SharedWorkerServiceImpl : public SharedWorkerService {
// Virtual for testing.
virtual void DestroyHost(SharedWorkerHost* host);
void NotifyWorkerStarted(const SharedWorkerInstance& instance,
int worker_process_id,
const base::UnguessableToken& dev_tools_token);
void NotifyWorkerTerminating(const SharedWorkerInstance& instance);
void NotifyClientAdded(const SharedWorkerInstance& instance,
int client_process_id,
int frame_id);
void NotifyClientRemoved(const SharedWorkerInstance& instance,
int client_process_id,
int frame_id);
StoragePartitionImpl* storage_partition() { return storage_partition_; }
private:
......@@ -137,6 +151,8 @@ class CONTENT_EXPORT SharedWorkerServiceImpl : public SharedWorkerService {
scoped_refptr<ChromeAppCacheService> appcache_service_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_override_;
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<SharedWorkerServiceImpl> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(SharedWorkerServiceImpl);
......
......@@ -13,6 +13,7 @@
#include "base/containers/queue.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/scoped_observer.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/worker_host/mock_shared_worker.h"
#include "content/browser/worker_host/shared_worker_connector_impl.h"
......@@ -1227,4 +1228,108 @@ TEST_F(SharedWorkerServiceImplTest, CreateWorkerRaceTest3) {
EXPECT_TRUE(worker.CheckReceivedTerminate());
}
class TestSharedWorkerServiceObserver : public SharedWorkerService::Observer {
public:
TestSharedWorkerServiceObserver() = default;
~TestSharedWorkerServiceObserver() override = default;
// SharedWorkerService::Observer:
void OnWorkerStarted(const SharedWorkerInstance& instance,
int worker_process_id,
const base::UnguessableToken& dev_tools_token) override {
EXPECT_TRUE(running_workers_.insert({instance, {}}).second);
}
void OnBeforeWorkerTerminated(const SharedWorkerInstance& instance) override {
EXPECT_EQ(1u, running_workers_.erase(instance));
}
void OnClientAdded(const SharedWorkerInstance& instance,
int client_process_id,
int frame_id) override {
auto it = running_workers_.find(instance);
EXPECT_TRUE(it != running_workers_.end());
std::set<ClientInfo>& clients = it->second;
EXPECT_TRUE(clients.insert({client_process_id, frame_id}).second);
}
void OnClientRemoved(const SharedWorkerInstance& instance,
int client_process_id,
int frame_id) override {
auto it = running_workers_.find(instance);
EXPECT_TRUE(it != running_workers_.end());
std::set<ClientInfo>& clients = it->second;
EXPECT_EQ(1u, clients.erase({client_process_id, frame_id}));
}
size_t GetWorkerCount() { return running_workers_.size(); }
size_t GetClientCount() {
size_t client_count = 0;
for (const auto& worker : running_workers_)
client_count += worker.second.size();
return client_count;
}
private:
using ClientInfo = std::pair<int, int>;
base::flat_map<SharedWorkerInstance, std::set<ClientInfo>> running_workers_;
DISALLOW_COPY_AND_ASSIGN(TestSharedWorkerServiceObserver);
};
TEST_F(SharedWorkerServiceImplTest, Observer) {
TestSharedWorkerServiceObserver observer;
ScopedObserver<SharedWorkerService, SharedWorkerService::Observer>
scoped_observer(&observer);
scoped_observer.Add(content::BrowserContext::GetDefaultStoragePartition(
browser_context_.get())
->GetSharedWorkerService());
std::unique_ptr<TestWebContents> web_contents =
CreateWebContents(GURL("http://example.com/"));
TestRenderFrameHost* render_frame_host = web_contents->GetMainFrame();
MockRenderProcessHost* renderer_host = render_frame_host->GetProcess();
const int process_id = renderer_host->GetID();
renderer_host->OverrideBinderForTesting(
blink::mojom::SharedWorkerFactory::Name_,
base::BindRepeating(&SharedWorkerServiceImplTest::BindSharedWorkerFactory,
base::Unretained(this), process_id));
MockSharedWorkerClient client;
MessagePortChannel local_port;
const GURL kUrl("http://example.com/w.js");
ConnectToSharedWorker(MakeSharedWorkerConnector(
renderer_host, render_frame_host->GetRoutingID()),
kUrl, "name", &client, &local_port);
mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver =
WaitForFactoryReceiver(process_id);
MockSharedWorkerFactory factory(std::move(factory_receiver));
base::RunLoop().RunUntilIdle();
mojo::Remote<blink::mojom::SharedWorkerHost> worker_host;
mojo::PendingReceiver<blink::mojom::SharedWorker> worker_receiver;
EXPECT_TRUE(factory.CheckReceivedCreateSharedWorker(
kUrl, "name", blink::mojom::ContentSecurityPolicyType::kReport,
&worker_host, &worker_receiver));
MockSharedWorker worker(std::move(worker_receiver));
base::RunLoop().RunUntilIdle();
int connection_request_id;
MessagePortChannel port;
EXPECT_TRUE(worker.CheckReceivedConnect(&connection_request_id, &port));
EXPECT_TRUE(client.CheckReceivedOnCreated());
EXPECT_EQ(1u, observer.GetWorkerCount());
EXPECT_EQ(1u, observer.GetClientCount());
// Tear down the worker host.
worker_host->OnContextClosed();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, observer.GetWorkerCount());
EXPECT_EQ(0u, observer.GetClientCount());
}
} // namespace content
......@@ -304,6 +304,8 @@ jumbo_source_set("browser_sources") {
"session_storage_namespace.h",
"session_storage_usage_info.h",
"shared_cors_origin_access_list.h",
"shared_worker_instance.cc",
"shared_worker_instance.h",
"shared_worker_service.h",
"site_instance.h",
"site_isolation_policy.cc",
......
......@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/worker_host/shared_worker_instance.h"
#include "content/public/browser/shared_worker_instance.h"
#include <tuple>
#include "base/logging.h"
......@@ -27,6 +29,15 @@ SharedWorkerInstance::SharedWorkerInstance(
SharedWorkerInstance::SharedWorkerInstance(const SharedWorkerInstance& other) =
default;
SharedWorkerInstance::SharedWorkerInstance(SharedWorkerInstance&& other) =
default;
SharedWorkerInstance& SharedWorkerInstance::operator=(
const SharedWorkerInstance& other) = default;
SharedWorkerInstance& SharedWorkerInstance::operator=(
SharedWorkerInstance&& other) = default;
SharedWorkerInstance::~SharedWorkerInstance() = default;
bool SharedWorkerInstance::Matches(
......@@ -59,4 +70,13 @@ bool SharedWorkerInstance::Matches(const SharedWorkerInstance& other) const {
return Matches(other.url(), other.name(), other.constructor_origin());
}
bool operator<(const SharedWorkerInstance& lhs,
const SharedWorkerInstance& rhs) {
if (lhs.Matches(rhs))
return false;
return std::tie(lhs.url(), lhs.name(), lhs.constructor_origin()) <
std::tie(rhs.url(), rhs.name(), rhs.constructor_origin());
}
} // namespace content
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_INSTANCE_H_
#define CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_INSTANCE_H_
#ifndef CONTENT_PUBLIC_BROWSER_SHARED_WORKER_INSTANCE_H_
#define CONTENT_PUBLIC_BROWSER_SHARED_WORKER_INSTANCE_H_
#include <string>
......@@ -16,8 +16,8 @@
namespace content {
// SharedWorkerInstance is copyable value-type data type. It could be passed to
// the UI thread and be used for comparison in SharedWorkerDevToolsManager.
// SharedWorkerInstance is a value-type class that is the browser-side
// representation of one instance of a shared worker.
class CONTENT_EXPORT SharedWorkerInstance {
public:
SharedWorkerInstance(
......@@ -29,6 +29,9 @@ class CONTENT_EXPORT SharedWorkerInstance {
network::mojom::IPAddressSpace creation_address_space,
blink::mojom::SharedWorkerCreationContextType creation_context_type);
SharedWorkerInstance(const SharedWorkerInstance& other);
SharedWorkerInstance(SharedWorkerInstance&& other);
SharedWorkerInstance& operator=(const SharedWorkerInstance& other);
SharedWorkerInstance& operator=(SharedWorkerInstance&& other);
~SharedWorkerInstance();
// Checks if this SharedWorkerInstance matches the passed url, name, and
......@@ -58,20 +61,23 @@ class CONTENT_EXPORT SharedWorkerInstance {
}
private:
const GURL url_;
const std::string name_;
GURL url_;
std::string name_;
// The origin of the document that created this shared worker instance. Used
// for security checks. See Matches() for details.
// https://html.spec.whatwg.org/multipage/workers.html#concept-sharedworkerglobalscope-constructor-origin
const url::Origin constructor_origin_;
url::Origin constructor_origin_;
const std::string content_security_policy_;
const blink::mojom::ContentSecurityPolicyType content_security_policy_type_;
const network::mojom::IPAddressSpace creation_address_space_;
const blink::mojom::SharedWorkerCreationContextType creation_context_type_;
std::string content_security_policy_;
blink::mojom::ContentSecurityPolicyType content_security_policy_type_;
network::mojom::IPAddressSpace creation_address_space_;
blink::mojom::SharedWorkerCreationContextType creation_context_type_;
};
CONTENT_EXPORT bool operator<(const SharedWorkerInstance& lhs,
const SharedWorkerInstance& rhs);
} // namespace content
#endif // CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_INSTANCE_H_
#endif // CONTENT_PUBLIC_BROWSER_SHARED_WORKER_INSTANCE_H_
......@@ -7,17 +7,53 @@
#include <string>
#include "base/observer_list_types.h"
#include "content/common/content_export.h"
class GURL;
namespace url {
class Origin;
} // namespace url
namespace content {
class SharedWorkerInstance;
// An interface for managing shared workers. These may be run in a separate
// process, since multiple renderer processes can be talking to a single shared
// worker. All the methods below can only be called on the UI thread.
class CONTENT_EXPORT SharedWorkerService {
public:
class Observer : public base::CheckedObserver {
public:
// Called when a shared worker has started/stopped. This means that Start()
// was called for that worker and it got assigned its DevTools token. Note
// that it may still be evaluating the script and thus it could not be yet
// running in the renderer. This differs a bit from the "started" state of
// the embedded worker.
virtual void OnWorkerStarted(
const SharedWorkerInstance& instance,
int worker_process_id,
const base::UnguessableToken& dev_tools_token) = 0;
virtual void OnBeforeWorkerTerminated(
const SharedWorkerInstance& instance) = 0;
// Called when a frame starts/stop being a client of a shared worker. It is
// guaranteed that OnWorkerStarted() is called before receiving these
// notifications.
virtual void OnClientAdded(const SharedWorkerInstance& instance,
int client_process_id,
int frame_id) = 0;
virtual void OnClientRemoved(const SharedWorkerInstance& instance,
int client_process_id,
int frame_id) = 0;
};
// Adds/removes an observer.
virtual void AddObserver(Observer* observer) = 0;
virtual void RemoveObserver(const Observer* observer) = 0;
// Terminates the given shared worker identified by its name, the URL of
// its main script resource, and the constructor origin. Returns true on
// success.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment