0

Remove SetupOnUIThread for EmbeddedWorkerInstance

Thanks to ServiceWorkerOnUI, we can eliminate the class and function to
manage thread-hoppings for starting a service worker. This CL moves
tasks in SetupOnUIThread into EmbeddedWorkerInstance::Start().

Bug: 1138155
Change-Id: I15c72d0d45e792e229a4a8f19972b5d90253fc87
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2578717
Commit-Queue: Makoto Shimazu <shimazu@chromium.org>
Reviewed-by: Asami Doi <asamidoi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835989}
This commit is contained in:
Makoto Shimazu
2020-12-11 04:01:44 +00:00
committed by Chromium LUCI CQ
parent b6f42464a4
commit e38dded5c9
3 changed files with 238 additions and 515 deletions

@ -57,9 +57,6 @@ namespace content {
namespace {
// Used for tracing.
constexpr char kEmbeddedWorkerInstanceScope[] = "EmbeddedWorkerInstance";
// When a service worker version's failure count exceeds
// |kMaxSameProcessFailureCount|, the embedded worker is forced to start in a
// new process.
@ -79,206 +76,6 @@ void NotifyWorkerVersionDoomedOnUI(
worker_process_id, worker_route_id, context_wrapper, version_id);
}
using SetupProcessCallback = base::OnceCallback<void(
blink::ServiceWorkerStatusCode,
blink::mojom::EmbeddedWorkerStartParamsPtr,
std::unique_ptr<ServiceWorkerProcessManager::AllocatedProcessInfo>,
std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy>,
std::unique_ptr<
blink::PendingURLLoaderFactoryBundle> /* factory_bundle_for_new_scripts
*/
,
std::unique_ptr<
blink::PendingURLLoaderFactoryBundle> /* factory_bundle_for_renderer */,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>,
mojo::PendingReceiver<blink::mojom::ReportingObserver>,
const base::Optional<base::TimeDelta>& thread_hop_time,
const base::Optional<base::Time>& ui_post_time)>;
// Allocates a renderer process for starting a worker and does setup like
// registering with DevTools. Called on the UI thread. Calls |callback| on the
// core thread. |context| and |weak_context| are only for passing to DevTools
// and must not be dereferenced here on the UI thread.
//
// This also sets up two URLLoaderFactoryBundles, one for
// ServiceWorkerScriptLoaderFactory and the other is for passing to the
// renderer. |cross_origin_embedder_policy| is respected to make these bundles.
// These bundles include factories for non-network URLs like chrome-extension://
// as needed.
void SetupOnUIThread(
int embedded_worker_id,
base::WeakPtr<ServiceWorkerProcessManager> process_manager,
bool can_use_existing_process,
const base::Optional<network::CrossOriginEmbedderPolicy>&
cross_origin_embedder_policy,
blink::mojom::EmbeddedWorkerStartParamsPtr params,
mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient> receiver,
scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
const base::Optional<base::Time>& io_post_time,
SetupProcessCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
base::Optional<base::TimeDelta> thread_hop_time;
auto process_info =
std::make_unique<ServiceWorkerProcessManager::AllocatedProcessInfo>();
std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy> devtools_proxy;
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
factory_bundle_for_new_scripts;
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
factory_bundle_for_renderer;
mojo::PendingReceiver<blink::mojom::ReportingObserver>
reporting_observer_receiver;
if (!process_manager) {
base::Optional<base::Time> ui_post_time;
ServiceWorkerContextWrapper::RunOrPostTaskOnCoreThread(
FROM_HERE, base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort,
std::move(params), std::move(process_info),
std::move(devtools_proxy),
std::move(factory_bundle_for_new_scripts),
std::move(factory_bundle_for_renderer),
/*coep_reporter=*/mojo::NullRemote(),
std::move(reporting_observer_receiver),
thread_hop_time, ui_post_time));
return;
}
// Get a process.
blink::ServiceWorkerStatusCode status =
process_manager->AllocateWorkerProcess(
embedded_worker_id, params->script_url, cross_origin_embedder_policy,
can_use_existing_process, process_info.get());
if (status != blink::ServiceWorkerStatusCode::kOk) {
base::Optional<base::Time> ui_post_time;
ServiceWorkerContextWrapper::RunOrPostTaskOnCoreThread(
FROM_HERE,
base::BindOnce(std::move(callback), status, std::move(params),
std::move(process_info), std::move(devtools_proxy),
std::move(factory_bundle_for_new_scripts),
std::move(factory_bundle_for_renderer),
/*coep_reporter=*/mojo::NullRemote(),
std::move(reporting_observer_receiver), thread_hop_time,
ui_post_time));
return;
}
const int process_id = process_info->process_id;
RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
// TODO(falken): This CHECK should no longer fail, so turn to a DCHECK it if
// crash reports agree. Consider also checking for
// rph->IsInitializedAndNotDead().
CHECK(rph);
// Bind |receiver|, which is attached to |EmbeddedWorkerInstance::client_|, to
// the process. If the process dies, |client_|'s connection error callback
// will be called on the core thread.
if (receiver.is_valid())
rph->BindReceiver(std::move(receiver));
// Create COEP reporter if COEP value is already available (= this worker is
// not a worker which is going to be newly registered).
mojo::Remote<network::mojom::CrossOriginEmbedderPolicyReporter> coep_reporter;
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_devtools;
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_scripts;
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_subresources;
if (cross_origin_embedder_policy) {
mojo::PendingRemote<blink::mojom::ReportingObserver>
reporting_observer_remote;
reporting_observer_receiver =
reporting_observer_remote.InitWithNewPipeAndPassReceiver();
auto reporter = std::make_unique<CrossOriginEmbedderPolicyReporter>(
rph->GetStoragePartition(), params->script_url,
cross_origin_embedder_policy->reporting_endpoint,
cross_origin_embedder_policy->report_only_reporting_endpoint,
// TODO(https://crbug.com/1147281): This is the NetworkIsolationKey of a
// top-level browsing context, which shouldn't be use for ServiceWorkers
// used in iframes.
net::NetworkIsolationKey::ToDoUseTopFrameOriginAsWell(
url::Origin::Create(params->script_url)));
reporter->BindObserver(std::move(reporting_observer_remote));
mojo::MakeSelfOwnedReceiver(std::move(reporter),
coep_reporter.BindNewPipeAndPassReceiver());
coep_reporter->Clone(
coep_reporter_for_devtools.InitWithNewPipeAndPassReceiver());
coep_reporter->Clone(
coep_reporter_for_scripts.InitWithNewPipeAndPassReceiver());
coep_reporter->Clone(
coep_reporter_for_subresources.InitWithNewPipeAndPassReceiver());
}
// Register to DevTools and update params accordingly.
const int routing_id = rph->GetNextRoutingID();
ServiceWorkerDevToolsManager::GetInstance()->WorkerStarting(
process_id, routing_id, std::move(context_wrapper),
params->service_worker_version_id, params->script_url, params->scope,
params->is_installed, cross_origin_embedder_policy,
std::move(coep_reporter_for_devtools), &params->devtools_worker_token,
&params->wait_for_debugger);
params->service_worker_route_id = routing_id;
// Create DevToolsProxy here to ensure that the WorkerCreated() call is
// balanced by DevToolsProxy's destructor calling WorkerStopped().
devtools_proxy = std::make_unique<EmbeddedWorkerInstance::DevToolsProxy>(
process_id, routing_id);
// Create factory bundles for this worker to do loading. These bundles don't
// support reconnection to the network service, see below comments.
const url::Origin origin = url::Origin::Create(params->script_url);
// The bundle for new scripts is passed to ServiceWorkerScriptLoaderFactory
// and used to request non-installed service worker scripts. It's only needed
// for non-installed workers. It's OK to not support reconnection to the
// network service because it can only used until the service worker reaches
// the 'installed' state.
if (!params->is_installed) {
factory_bundle_for_new_scripts =
EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, routing_id, origin, cross_origin_embedder_policy,
std::move(coep_reporter_for_scripts),
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript);
}
// The bundle for the renderer is passed to the service worker, and
// used for subresource loading from the service worker (i.e., fetch()).
// It's OK to not support reconnection to the network service because the
// service worker terminates itself when the connection breaks, so a new
// instance can be started.
factory_bundle_for_renderer = EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, routing_id, origin, cross_origin_embedder_policy,
std::move(coep_reporter_for_subresources),
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource);
// TODO(crbug.com/862854): Support changes to blink::RendererPreferences while
// the worker is running.
DCHECK(process_manager->browser_context() || process_manager->IsShutdown());
params->renderer_preferences = blink::RendererPreferences();
GetContentClient()->browser()->UpdateRendererPreferencesForWorker(
process_manager->browser_context(), &params->renderer_preferences);
// Create a RendererPreferenceWatcher to observe updates in the preferences.
mojo::PendingRemote<blink::mojom::RendererPreferenceWatcher> watcher_remote;
params->preference_watcher_receiver =
watcher_remote.InitWithNewPipeAndPassReceiver();
GetContentClient()->browser()->RegisterRendererPreferenceWatcher(
process_manager->browser_context(), std::move(watcher_remote));
// Continue to OnSetupCompleted on the core thread.
base::Optional<base::Time> ui_post_time;
RunOrPostTaskOnThread(
FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
base::BindOnce(
std::move(callback), status, std::move(params),
std::move(process_info), std::move(devtools_proxy),
std::move(factory_bundle_for_new_scripts),
std::move(factory_bundle_for_renderer),
coep_reporter ? coep_reporter.Unbind() : mojo::NullRemote(),
std::move(reporting_observer_receiver), thread_hop_time,
ui_post_time));
}
bool HasSentStartWorker(EmbeddedWorkerInstance::StartingPhase phase) {
switch (phase) {
case EmbeddedWorkerInstance::NOT_STARTING:
@ -327,16 +124,17 @@ void BindCacheStorageOnUIThread(
} // namespace
// Created on the UI thread when the worker version is allcated a render process
// and then moved to the core thread. It is destroyed when the worker stops.
// Proxies notifications to DevToolsManager that lives on UI thread.
// Created when a renderer process is allocated for the worker. It is destroyed
// when the worker stops, and this proxies notifications to DevToolsManager.
// Owned by EmbeddedWorkerInstance.
//
// TODO(https://crbug.com/1138155): Remove this because we no longer need
// proxying the notifications becuase there's no thread hopping thanks to
// ServiceWorkerOnUI.
class EmbeddedWorkerInstance::DevToolsProxy {
public:
DevToolsProxy(int process_id, int agent_route_id)
: process_id_(process_id),
agent_route_id_(agent_route_id),
ui_task_runner_(GetUIThreadTaskRunner({})) {}
: process_id_(process_id), agent_route_id_(agent_route_id) {}
~DevToolsProxy() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
@ -370,7 +168,6 @@ class EmbeddedWorkerInstance::DevToolsProxy {
private:
const int process_id_;
const int agent_route_id_;
const scoped_refptr<base::TaskRunner> ui_task_runner_;
bool worker_stop_ignored_notified_ = false;
DISALLOW_COPY_AND_ASSIGN(DevToolsProxy);
@ -398,303 +195,68 @@ class EmbeddedWorkerInstance::ScopedLifetimeTracker {
DISALLOW_COPY_AND_ASSIGN(ScopedLifetimeTracker);
};
// A handle for a renderer process managed by ServiceWorkerProcessManager on the
// UI thread. Lives on the core thread.
// A handle for a renderer process managed by ServiceWorkerProcessManager.
//
// TODO(https://crbug.com/1138155): Remove this as a clean up of
// ServiceWorkerOnUI.
class EmbeddedWorkerInstance::WorkerProcessHandle {
public:
WorkerProcessHandle(
const base::WeakPtr<ServiceWorkerProcessManager>& process_manager,
int embedded_worker_id,
int process_id,
scoped_refptr<base::SequencedTaskRunner> ui_task_runner)
int process_id)
: process_manager_(process_manager),
embedded_worker_id_(embedded_worker_id),
process_id_(process_id),
ui_task_runner_(std::move(ui_task_runner)) {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
process_id_(process_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id_);
}
~WorkerProcessHandle() {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
DCHECK_CURRENTLY_ON(BrowserThread::UI);
process_manager_->ReleaseWorkerProcess(embedded_worker_id_);
}
int process_id() const { return process_id_; }
private:
// Can be dereferenced on the UI thread only.
base::WeakPtr<ServiceWorkerProcessManager> process_manager_;
const int embedded_worker_id_;
const int process_id_;
const scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
DISALLOW_COPY_AND_ASSIGN(WorkerProcessHandle);
};
// A task to allocate a worker process and to send a start worker message. This
// is created on EmbeddedWorkerInstance::Start(), owned by the instance and
// destroyed on EmbeddedWorkerInstance::OnScriptEvaluated().
// We can abort starting worker by destroying this task anytime during the
// sequence.
// Lives on the core thread.
class EmbeddedWorkerInstance::StartTask {
public:
enum class ProcessAllocationState { NOT_ALLOCATED, ALLOCATING, ALLOCATED };
StartTask(EmbeddedWorkerInstance* instance,
const GURL& script_url,
mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient>
receiver,
// Info that is recorded as UMA on OnStarted().
struct EmbeddedWorkerInstance::StartInfo {
StartInfo(bool is_installed,
bool skip_recording_startup_time,
base::TimeTicks start_time)
: instance_(instance),
receiver_(std::move(receiver)),
state_(ProcessAllocationState::NOT_ALLOCATED),
is_installed_(false),
started_during_browser_startup_(false),
skip_recording_startup_time_(instance_->devtools_attached()),
start_time_(start_time) {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
TRACE_EVENT_WITH_FLOW1(
"ServiceWorker", "EmbeddedWorkerInstance::StartTask::StartTask",
TRACE_ID_WITH_SCOPE(kEmbeddedWorkerInstanceScope,
instance_->embedded_worker_id()),
TRACE_EVENT_FLAG_FLOW_OUT, "Script", script_url.spec());
}
~StartTask() {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
TRACE_EVENT_WITH_FLOW0("ServiceWorker",
"EmbeddedWorkerInstance::StartTask::~StartTask",
TRACE_ID_WITH_SCOPE(kEmbeddedWorkerInstanceScope,
instance_->embedded_worker_id()),
TRACE_EVENT_FLAG_FLOW_IN);
if (!instance_->context_)
return;
switch (state_) {
case ProcessAllocationState::NOT_ALLOCATED:
// Not necessary to release a process.
break;
case ProcessAllocationState::ALLOCATING:
// Abort half-baked process allocation on the UI thread.
instance_->ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ServiceWorkerProcessManager::ReleaseWorkerProcess,
instance_->context_->process_manager()->AsWeakPtr(),
instance_->embedded_worker_id()));
break;
case ProcessAllocationState::ALLOCATED:
// Otherwise, the process will be released by EmbeddedWorkerInstance.
break;
}
// Don't have to abort |sent_start_callback_| here. The caller of
// EmbeddedWorkerInstance::Start(), that is, ServiceWorkerVersion does not
// expect it when the start worker sequence is canceled by Stop() because
// the callback, ServiceWorkerVersion::OnStartSentAndScriptEvaluated(),
// could drain valid start requests queued in the version. After the worker
// is stopped, the version attempts to restart the worker if there are
// requests in the queue. See ServiceWorkerVersion::OnStoppedInternal() for
// details.
// TODO(crbug.com/859912): Reconsider this bizarre layering.
}
base::TimeTicks start_time() const { return start_time_; }
void set_start_worker_sent_time(base::TimeTicks time) {
start_worker_sent_time_ = time;
}
base::TimeTicks start_worker_sent_time() const {
return start_worker_sent_time_;
}
base::TimeDelta thread_hop_time() const { return thread_hop_time_; }
void set_skip_recording_startup_time() {
skip_recording_startup_time_ = true;
}
bool skip_recording_startup_time() const {
return skip_recording_startup_time_;
}
void Start(blink::mojom::EmbeddedWorkerStartParamsPtr params,
const base::Optional<network::CrossOriginEmbedderPolicy>&
cross_origin_embedder_policy,
StatusCallback sent_start_callback) {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
DCHECK(instance_->context_);
TRACE_EVENT_WITH_FLOW0(
"ServiceWorker", "EmbeddedWorkerInstance::StartTask::Start",
TRACE_ID_WITH_SCOPE(kEmbeddedWorkerInstanceScope,
instance_->embedded_worker_id()),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
base::WeakPtr<ServiceWorkerContextCore> context = instance_->context_;
state_ = ProcessAllocationState::ALLOCATING;
sent_start_callback_ = std::move(sent_start_callback);
is_installed_ = params->is_installed;
if (!GetContentClient()->browser()->IsBrowserStartupComplete())
started_during_browser_startup_ = true;
bool can_use_existing_process =
context->GetVersionFailureCount(params->service_worker_version_id) <
kMaxSameProcessFailureCount;
base::WeakPtr<ServiceWorkerProcessManager> process_manager =
context->process_manager()->AsWeakPtr();
// Perform process allocation and setup on the UI thread. We will continue
// on the core thread in StartTask::OnSetupCompleted().
SetupOnUIThread(
instance_->embedded_worker_id(), process_manager,
can_use_existing_process, cross_origin_embedder_policy,
std::move(params), std::move(receiver_),
base::WrapRefCounted(context->wrapper()), base::nullopt,
base::BindOnce(&StartTask::OnSetupCompleted, weak_factory_.GetWeakPtr(),
process_manager));
}
bool is_installed() const { return is_installed_; }
private:
void OnSetupCompleted(
base::WeakPtr<ServiceWorkerProcessManager> process_manager,
blink::ServiceWorkerStatusCode status,
blink::mojom::EmbeddedWorkerStartParamsPtr params,
std::unique_ptr<ServiceWorkerProcessManager::AllocatedProcessInfo>
process_info,
std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy> devtools_proxy,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
factory_bundle_for_new_scripts,
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
factory_bundle_for_renderer,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter,
mojo::PendingReceiver<blink::mojom::ReportingObserver>
reporting_observer_receiver,
const base::Optional<base::TimeDelta>& thread_hop_time,
const base::Optional<base::Time>& ui_post_time) {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
if (reporting_observer_receiver) {
instance_->owner_version_->set_reporting_observer_receiver(
std::move(reporting_observer_receiver));
}
std::unique_ptr<WorkerProcessHandle> process_handle;
if (status == blink::ServiceWorkerStatusCode::kOk) {
// If we allocated a process, WorkerProcessHandle has to be created before
// returning to ensure the process is eventually released.
process_handle = std::make_unique<WorkerProcessHandle>(
process_manager, instance_->embedded_worker_id(),
process_info->process_id, instance_->ui_task_runner_);
if (!instance_->context_)
status = blink::ServiceWorkerStatusCode::kErrorAbort;
}
if (status != blink::ServiceWorkerStatusCode::kOk) {
TRACE_EVENT_WITH_FLOW1(
"ServiceWorker",
"EmbeddedWorkerInstance::StartTask::OnSetupCompleted",
TRACE_ID_WITH_SCOPE(kEmbeddedWorkerInstanceScope,
instance_->embedded_worker_id()),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "Error",
blink::ServiceWorkerStatusToString(status));
instance_->OnSetupFailed(std::move(sent_start_callback_), status);
// |this| may be destroyed.
return;
}
ServiceWorkerMetrics::StartSituation start_situation =
process_info->start_situation;
TRACE_EVENT_WITH_FLOW1(
"ServiceWorker", "EmbeddedWorkerInstance::StartTask::OnSetupCompleted",
TRACE_ID_WITH_SCOPE(kEmbeddedWorkerInstanceScope,
instance_->embedded_worker_id()),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "StartSituation",
ServiceWorkerMetrics::StartSituationToString(start_situation));
if (started_during_browser_startup_)
start_situation = ServiceWorkerMetrics::StartSituation::DURING_STARTUP;
// Notify the instance that a process is allocated.
state_ = ProcessAllocationState::ALLOCATED;
instance_->OnProcessAllocated(std::move(process_handle), start_situation);
// Notify the instance that it is registered to the DevTools manager.
instance_->OnRegisteredToDevToolsManager(std::move(devtools_proxy),
params->wait_for_debugger);
// Send the factory bundle for subresource loading from the service worker
// (i.e. fetch()).
DCHECK(factory_bundle_for_renderer);
params->subresource_loader_factories =
std::move(factory_bundle_for_renderer);
// Build the URLLoaderFactory for loading new scripts, it's only needed if
// this is a non-installed service worker.
DCHECK(factory_bundle_for_new_scripts || is_installed_);
if (factory_bundle_for_new_scripts) {
params->provider_info->script_loader_factory_remote =
instance_->MakeScriptLoaderFactoryRemote(
std::move(factory_bundle_for_new_scripts));
}
// Bind COEP reporter created on the UI thread, which has the onwership of
// the instance. The |coep_reporter| might be null when the COEP value is
// not known because the main script has not been loaded yet. In that case,
// COEP reporter will be bound after the main script is loaded.
if (coep_reporter) {
instance_->coep_reporter_.Bind(std::move(coep_reporter));
}
// Create cache storage now as an optimization, so the service worker can
// use the Cache Storage API immediately on startup.
if (base::FeatureList::IsEnabled(
blink::features::kEagerCacheStorageSetupForServiceWorkers)) {
instance_->BindCacheStorage(params->provider_info->cache_storage
.InitWithNewPipeAndPassReceiver());
}
instance_->SendStartWorker(std::move(params));
std::move(sent_start_callback_).Run(blink::ServiceWorkerStatusCode::kOk);
// |this|'s work is done here, but |instance_| still uses its state until
// startup is complete.
}
// |instance_| must outlive |this|.
EmbeddedWorkerInstance* instance_;
// Ownership is transferred by a PostTask() call after process allocation.
mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient> receiver_;
StatusCallback sent_start_callback_;
ProcessAllocationState state_;
: is_installed(is_installed),
skip_recording_startup_time(skip_recording_startup_time),
start_time(start_time) {}
~StartInfo() = default;
// Used for UMA.
bool is_installed_;
bool started_during_browser_startup_;
bool skip_recording_startup_time_;
base::TimeTicks start_time_;
base::TimeTicks start_worker_sent_time_;
base::TimeDelta thread_hop_time_;
base::WeakPtrFactory<StartTask> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(StartTask);
const bool is_installed;
bool skip_recording_startup_time;
const base::TimeTicks start_time;
base::TimeTicks start_worker_sent_time;
};
EmbeddedWorkerInstance::~EmbeddedWorkerInstance() {
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ReleaseProcess();
}
void EmbeddedWorkerInstance::Start(
blink::mojom::EmbeddedWorkerStartParamsPtr params,
StatusCallback callback) {
TRACE_EVENT1("ServiceWorker", "EmbeddedWorkerInstance::Start", "script_url",
params->script_url.spec());
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(context_);
restart_count_++;
DCHECK_EQ(EmbeddedWorkerStatus::STOPPED, status_);
@ -722,15 +284,192 @@ void EmbeddedWorkerInstance::Start(
// check is_bound strictly.
client_.reset();
mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient> receiver =
client_.BindNewPipeAndPassReceiver();
auto process_info =
std::make_unique<ServiceWorkerProcessManager::AllocatedProcessInfo>();
std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy> devtools_proxy;
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
factory_bundle_for_new_scripts;
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
factory_bundle_for_renderer;
mojo::PendingReceiver<blink::mojom::ReportingObserver>
reporting_observer_receiver;
ServiceWorkerProcessManager* process_manager = context_->process_manager();
if (!process_manager) {
OnSetupFailed(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort);
return;
}
// Get a process.
bool can_use_existing_process =
context_->GetVersionFailureCount(params->service_worker_version_id) <
kMaxSameProcessFailureCount;
blink::ServiceWorkerStatusCode status =
process_manager->AllocateWorkerProcess(
embedded_worker_id(), params->script_url,
owner_version_->cross_origin_embedder_policy(),
can_use_existing_process, process_info.get());
if (status != blink::ServiceWorkerStatusCode::kOk) {
OnSetupFailed(std::move(callback), status);
return;
}
const int process_id = process_info->process_id;
RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
// TODO(falken): This CHECK should no longer fail, so turn to a DCHECK it if
// crash reports agree. Consider also checking for
// rph->IsInitializedAndNotDead().
CHECK(rph);
rph->BindReceiver(client_.BindNewPipeAndPassReceiver());
client_.set_disconnect_handler(
base::BindOnce(&EmbeddedWorkerInstance::Detach, base::Unretained(this)));
inflight_start_task_.reset(
new StartTask(this, params->script_url, std::move(receiver), start_time));
inflight_start_task_->Start(std::move(params),
owner_version_->cross_origin_embedder_policy(),
std::move(callback));
{
// Create COEP reporter if COEP value is already available (= this worker is
// not a worker which is going to be newly registered). The Mojo remote
// `coep_reporter_` has the onwership of the instance. The `coep_reporter`
// might be kept null when the COEP value is not known because the main
// script has not been loaded yet. In that case, it will be bound after the
// main script is loaded.
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_devtools;
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_scripts;
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_for_subresources;
if (owner_version_->cross_origin_embedder_policy()) {
mojo::PendingRemote<blink::mojom::ReportingObserver>
reporting_observer_remote;
owner_version_->set_reporting_observer_receiver(
reporting_observer_remote.InitWithNewPipeAndPassReceiver());
auto reporter = std::make_unique<CrossOriginEmbedderPolicyReporter>(
rph->GetStoragePartition(), params->script_url,
owner_version_->cross_origin_embedder_policy()->reporting_endpoint,
owner_version_->cross_origin_embedder_policy()
->report_only_reporting_endpoint,
// TODO(https://crbug.com/1147281): This is the NetworkIsolationKey of
// a top-level browsing context, which shouldn't be use for
// ServiceWorkers used in iframes.
net::NetworkIsolationKey::ToDoUseTopFrameOriginAsWell(
url::Origin::Create(params->script_url)));
reporter->BindObserver(std::move(reporting_observer_remote));
mojo::MakeSelfOwnedReceiver(std::move(reporter),
coep_reporter_.BindNewPipeAndPassReceiver());
coep_reporter_->Clone(
coep_reporter_for_devtools.InitWithNewPipeAndPassReceiver());
coep_reporter_->Clone(
coep_reporter_for_scripts.InitWithNewPipeAndPassReceiver());
coep_reporter_->Clone(
coep_reporter_for_subresources.InitWithNewPipeAndPassReceiver());
}
// Register to DevTools and update params accordingly.
const int routing_id = rph->GetNextRoutingID();
ServiceWorkerDevToolsManager::GetInstance()->WorkerStarting(
process_id, routing_id, context_->wrapper(),
params->service_worker_version_id, params->script_url, params->scope,
params->is_installed, owner_version_->cross_origin_embedder_policy(),
std::move(coep_reporter_for_devtools), &params->devtools_worker_token,
&params->wait_for_debugger);
params->service_worker_route_id = routing_id;
// Create DevToolsProxy here to ensure that the WorkerCreated() call is
// balanced by DevToolsProxy's destructor calling WorkerStopped().
devtools_proxy = std::make_unique<EmbeddedWorkerInstance::DevToolsProxy>(
process_id, routing_id);
// Create factory bundles for this worker to do loading. These bundles don't
// support reconnection to the network service, see below comments.
const url::Origin origin = url::Origin::Create(params->script_url);
// The bundle for new scripts is passed to ServiceWorkerScriptLoaderFactory
// and used to request non-installed service worker scripts. It's only
// needed for non-installed workers. It's OK to not support reconnection to
// the network service because it can only used until the service worker
// reaches the 'installed' state.
if (!params->is_installed) {
factory_bundle_for_new_scripts =
EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, routing_id, origin,
owner_version_->cross_origin_embedder_policy(),
std::move(coep_reporter_for_scripts),
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript);
}
// The bundle for the renderer is passed to the service worker, and
// used for subresource loading from the service worker (i.e., fetch()).
// It's OK to not support reconnection to the network service because the
// service worker terminates itself when the connection breaks, so a new
// instance can be started.
factory_bundle_for_renderer =
EmbeddedWorkerInstance::CreateFactoryBundleOnUI(
rph, routing_id, origin,
owner_version_->cross_origin_embedder_policy(),
std::move(coep_reporter_for_subresources),
ContentBrowserClient::URLLoaderFactoryType::
kServiceWorkerSubResource);
}
// TODO(crbug.com/862854): Support changes to blink::RendererPreferences while
// the worker is running.
DCHECK(process_manager->browser_context() || process_manager->IsShutdown());
params->renderer_preferences = blink::RendererPreferences();
GetContentClient()->browser()->UpdateRendererPreferencesForWorker(
process_manager->browser_context(), &params->renderer_preferences);
{
// Create a RendererPreferenceWatcher to observe updates in the preferences.
mojo::PendingRemote<blink::mojom::RendererPreferenceWatcher> watcher_remote;
params->preference_watcher_receiver =
watcher_remote.InitWithNewPipeAndPassReceiver();
GetContentClient()->browser()->RegisterRendererPreferenceWatcher(
process_manager->browser_context(), std::move(watcher_remote));
}
// If we allocated a process, WorkerProcessHandle has to be created before
// returning to ensure the process is eventually released.
auto process_handle = std::make_unique<WorkerProcessHandle>(
process_manager->AsWeakPtr(), embedded_worker_id(),
process_info->process_id);
ServiceWorkerMetrics::StartSituation start_situation =
process_info->start_situation;
if (!GetContentClient()->browser()->IsBrowserStartupComplete())
start_situation = ServiceWorkerMetrics::StartSituation::DURING_STARTUP;
// Notify the instance that a process is allocated.
OnProcessAllocated(std::move(process_handle), start_situation);
// Notify the instance that it is registered to the DevTools manager.
OnRegisteredToDevToolsManager(std::move(devtools_proxy));
// Send the factory bundle for subresource loading from the service worker
// (i.e. fetch()).
DCHECK(factory_bundle_for_renderer);
params->subresource_loader_factories = std::move(factory_bundle_for_renderer);
// Build the URLLoaderFactory for loading new scripts, it's only needed if
// this is a non-installed service worker.
DCHECK(factory_bundle_for_new_scripts || params->is_installed);
if (factory_bundle_for_new_scripts) {
params->provider_info->script_loader_factory_remote =
MakeScriptLoaderFactoryRemote(
std::move(factory_bundle_for_new_scripts));
}
// Create cache storage now as an optimization, so the service worker can
// use the Cache Storage API immediately on startup.
if (base::FeatureList::IsEnabled(
blink::features::kEagerCacheStorageSetupForServiceWorkers)) {
BindCacheStorage(
params->provider_info->cache_storage.InitWithNewPipeAndPassReceiver());
}
inflight_start_info_ = std::make_unique<StartInfo>(
params->is_installed, params->wait_for_debugger, start_time);
SendStartWorker(std::move(params));
std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk);
}
void EmbeddedWorkerInstance::Stop() {
@ -738,8 +477,9 @@ void EmbeddedWorkerInstance::Stop() {
status_ == EmbeddedWorkerStatus::RUNNING)
<< static_cast<int>(status_);
// Abort an inflight start task.
inflight_start_task_.reset();
// Discard the info for starting a worker because this worker is going to be
// stopped.
inflight_start_info_.reset();
// Don't send the StopWorker message if the StartWorker message hasn't
// been sent.
@ -785,8 +525,7 @@ EmbeddedWorkerInstance::EmbeddedWorkerInstance(
thread_id_(ServiceWorkerConsts::kInvalidEmbeddedWorkerThreadId),
devtools_attached_(false),
network_accessed_for_script_(false),
foreground_notified_(false),
ui_task_runner_(GetUIThreadTaskRunner({})) {
foreground_notified_(false) {
DCHECK(owner_version_);
DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
DCHECK(context_);
@ -808,14 +547,11 @@ void EmbeddedWorkerInstance::OnProcessAllocated(
}
void EmbeddedWorkerInstance::OnRegisteredToDevToolsManager(
std::unique_ptr<DevToolsProxy> devtools_proxy,
bool wait_for_debugger) {
std::unique_ptr<DevToolsProxy> devtools_proxy) {
if (devtools_proxy) {
DCHECK(!devtools_proxy_);
devtools_proxy_ = std::move(devtools_proxy);
}
if (wait_for_debugger)
inflight_start_task_->set_skip_recording_startup_time();
for (auto& observer : listener_list_)
observer.OnRegisteredToDevToolsManager();
}
@ -837,7 +573,7 @@ void EmbeddedWorkerInstance::SendStartWorker(
params->content_settings_proxy.InitWithNewPipeAndPassReceiver());
const bool is_script_streaming = !params->installed_scripts_info.is_null();
inflight_start_task_->set_start_worker_sent_time(base::TimeTicks::Now());
inflight_start_info_->start_worker_sent_time = base::TimeTicks::Now();
// The host must be alive as long as |params->provider_info| is alive.
owner_version_->worker_host()->CompleteStartWorkerPreparation(
@ -889,7 +625,7 @@ void EmbeddedWorkerInstance::OnReadyForInspection(
}
void EmbeddedWorkerInstance::OnScriptLoaded() {
if (!inflight_start_task_)
if (!inflight_start_info_)
return;
// Renderer side has started to launch the worker thread.
@ -909,7 +645,7 @@ void EmbeddedWorkerInstance::OnWorkerVersionDoomed() {
}
void EmbeddedWorkerInstance::OnScriptEvaluationStart() {
if (!inflight_start_task_)
if (!inflight_start_info_)
return;
starting_phase_ = SCRIPT_EVALUATION;
@ -940,12 +676,12 @@ void EmbeddedWorkerInstance::OnStarted(
if (status_ == EmbeddedWorkerStatus::STOPPING)
return;
if (inflight_start_task_->is_installed() &&
!inflight_start_task_->skip_recording_startup_time()) {
if (inflight_start_info_->is_installed &&
!inflight_start_info_->skip_recording_startup_time) {
ServiceWorkerMetrics::StartTimes times;
times.local_start = inflight_start_task_->start_time();
times.local_start = inflight_start_info_->start_time;
times.local_start_worker_sent =
inflight_start_task_->start_worker_sent_time();
inflight_start_info_->start_worker_sent_time;
times.remote_start_worker_received =
start_timing->start_worker_received_time;
times.remote_script_evaluation_start =
@ -953,7 +689,6 @@ void EmbeddedWorkerInstance::OnStarted(
times.remote_script_evaluation_end =
start_timing->script_evaluation_end_time;
times.local_end = base::TimeTicks::Now();
times.thread_hop_time = inflight_start_task_->thread_hop_time();
ServiceWorkerMetrics::RecordStartWorkerTiming(times, start_situation_);
}
@ -961,7 +696,7 @@ void EmbeddedWorkerInstance::OnStarted(
DCHECK_EQ(EmbeddedWorkerStatus::STARTING, status_);
status_ = EmbeddedWorkerStatus::RUNNING;
thread_id_ = thread_id;
inflight_start_task_.reset();
inflight_start_info_.reset();
for (auto& observer : listener_list_) {
observer.OnStarted(start_status, has_fetch_handler);
// |this| may be destroyed here. Fortunately we know there is only one
@ -1242,8 +977,8 @@ void EmbeddedWorkerInstance::SetDevToolsAttached(bool attached) {
devtools_attached_ = attached;
if (!attached)
return;
if (inflight_start_task_)
inflight_start_task_->set_skip_recording_startup_time();
if (inflight_start_info_)
inflight_start_info_->skip_recording_startup_time = true;
AbortLifetimeTracking();
}
@ -1261,7 +996,7 @@ void EmbeddedWorkerInstance::OnNetworkAccessedForScriptLoad() {
void EmbeddedWorkerInstance::ReleaseProcess() {
// Abort an inflight start task.
inflight_start_task_.reset();
inflight_start_info_.reset();
NotifyForegroundServiceWorkerRemoved();

@ -59,7 +59,7 @@ FORWARD_DECLARE_TEST(ServiceWorkerNewScriptLoaderTest, AccessedNetwork);
// may be 'in-waiting' or running in one of the child processes added by
// AddProcessReference().
//
// Owned by ServiceWorkerVersion. Lives on the core thread.
// Owned by ServiceWorkerVersion.
class CONTENT_EXPORT EmbeddedWorkerInstance
: public blink::mojom::EmbeddedWorkerInstanceHost {
public:
@ -264,7 +264,7 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
private:
typedef base::ObserverList<Listener>::Unchecked ListenerList;
class ScopedLifetimeTracker;
class StartTask;
struct StartInfo;
class WorkerProcessHandle;
friend class EmbeddedWorkerInstanceTest;
FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerInstanceTest, StartAndStop);
@ -274,21 +274,14 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
ServiceWorkerNewScriptLoaderTest,
AccessedNetwork);
// Called back from StartTask after a process is allocated on the UI thread.
void OnProcessAllocated(std::unique_ptr<WorkerProcessHandle> handle,
ServiceWorkerMetrics::StartSituation start_situation);
// Called back from StartTask after the worker is registered to
// WorkerDevToolsManager.
void OnRegisteredToDevToolsManager(
std::unique_ptr<DevToolsProxy> devtools_proxy,
bool wait_for_debugger);
std::unique_ptr<DevToolsProxy> devtools_proxy);
// Sends the StartWorker message to the renderer.
void SendStartWorker(blink::mojom::EmbeddedWorkerStartParamsPtr params);
// Implements blink::mojom::EmbeddedWorkerInstanceHost.
// These functions all run on the core thread.
void RequestTermination(RequestTerminationCallback callback) override;
void CountFeature(blink::mojom::WebFeature feature) override;
void OnReadyForInspection(
@ -325,7 +318,6 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
blink::ServiceWorkerStatusCode status);
// Called when a foreground service worker is added/removed in a process.
// Called on the core thread and dispatches task to the UI thread.
void NotifyForegroundServiceWorkerAdded();
void NotifyForegroundServiceWorkerRemoved();
@ -354,7 +346,6 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
// using it. The renderer process will disconnect the pipe when appropriate.
mojo::Remote<blink::mojom::EmbeddedWorkerInstanceClient> client_;
// Receiver for EmbeddedWorkerInstanceHost, runs on core thread.
mojo::AssociatedReceiver<EmbeddedWorkerInstanceHost> instance_host_receiver_{
this};
@ -372,7 +363,9 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
ListenerList listener_list_;
std::unique_ptr<DevToolsProxy> devtools_proxy_;
std::unique_ptr<StartTask> inflight_start_task_;
// Contains info to be recorded on completing StartWorker sequence.
// Set on Start() and cleared on OnStarted().
std::unique_ptr<StartInfo> inflight_start_info_;
std::unique_ptr<ScopedLifetimeTracker> lifetime_tracker_;
// This is valid only after a process is allocated for the worker.
@ -386,8 +379,6 @@ class CONTENT_EXPORT EmbeddedWorkerInstance
mojo::SelfOwnedReceiverRef<network::mojom::URLLoaderFactory>
script_loader_factory_;
const scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
// Remote interface to talk to a running service worker. Used to update
// subresource loader factories in the service worker.
mojo::Remote<blink::mojom::SubresourceLoaderUpdater>

@ -157,9 +157,6 @@ class ServiceWorkerMetrics {
// The browser received the worker started IPC.
base::TimeTicks local_end;
// Counts the time overhead of UI/IO thread hops during startup.
base::TimeDelta thread_hop_time;
};
// Converts an event type to a string. Used for tracing.