blob: 73e524beebb7bc186c15d71cc72685907d76c415 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_SERVICE_WORKER_SERVICE_WORKER_STATE_H_
#define EXTENSIONS_BROWSER_SERVICE_WORKER_SERVICE_WORKER_STATE_H_
#include <optional>
#include "base/auto_reset.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "content/public/browser/service_worker_context.h"
#include "extensions/browser/service_worker/sequenced_context_id.h"
#include "extensions/browser/service_worker/worker_id.h"
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
namespace extensions {
class ProcessManager;
// The current worker related state of an activated extension.
class ServiceWorkerState
: public content::ServiceWorkerContextObserverSynchronous {
public:
// Browser process worker state of an activated extension.
enum class BrowserState {
// Worker has not started or has been stopped/terminated.
kNotActive,
// Worker has started (i.e. has seen DidStartWorkerForScope).
kActive,
// Worker has completed starting (i.e. has seen DidStartWorkerForScope and
// DidStartServiceWorkerContext).
kReady,
};
// Render process worker state of an activated extension.
enum class RendererState {
// Worker thread has not started or has been stopped/terminated.
kNotActive,
// Worker thread has started and it's running.
kActive,
};
ServiceWorkerState(content::ServiceWorkerContext* service_worker_context,
const ProcessManager* process_manager);
~ServiceWorkerState() override;
ServiceWorkerState(const ServiceWorkerState&) = delete;
ServiceWorkerState& operator=(const ServiceWorkerState&) = delete;
class Observer : public base::CheckedObserver {
public:
// Called when an extension service worker has started.
virtual void OnWorkerStart(const SequencedContextId& context_id,
const WorkerId& worker_id) {}
// Called when an extension service worker has failed to start.
virtual void OnWorkerStartFail(const SequencedContextId& context_id,
base::Time start_time,
content::StatusCodeResponse status) {}
// Called when an extension service worker is stopping or has stopped.
virtual void OnWorkerStop(int64_t version_id, const GURL& scope) {}
};
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
void SetBrowserState(BrowserState browser_state);
void SetRendererState(RendererState renderer_state);
void Reset();
// Returns true if a request to start the worker has been made but the worker
// is not ready yet.
bool IsStarting() const;
// Returns true if the worker is running and is ready to execute tasks.
bool IsReady() const;
// Starts the extension service worker. This method should only be called
// if the service worker hasn't started yet. If this method is called while
// the service worker is in the process of starting, it's a no-op.
void StartWorker(const SequencedContextId& context_id);
// Called when a service worker renderer process is running, has executed its
// global JavaScript scope, and all its global event listeners have been
// registered with the //extensions layer. It is considered the
// "renderer-side" signal that the worker is ready.
// NOTE: this can be called before or after `RendererDidStartWorkerForScope`.
void RendererDidStartServiceWorkerContext(
const SequencedContextId& context_id,
const WorkerId& worker_id);
// Called when the render worker thread is preparing to terminate. It is
// considered the "renderer-side" signal that the worker is stopping.
// NOTE: this can be called before or after `OnStoppingSync` and
// `OnStoppedSync`.
void RendererDidStopServiceWorkerContext(const WorkerId& worker_id,
const GURL& scope);
// Called when the worker was requested to start and it verified that a worker
// registration exists at the //content layer. It is considered the
// "browser-side" signal that the worker is ready.
// NOTE: this can be called before or after
// `RendererDidStartServiceWorkerContext`.
void DidStartWorkerForScope(const SequencedContextId& context_id,
base::Time start_time,
int64_t version_id,
int process_id,
int thread_id);
// Called when the worker was requested to start, but failed.
void DidStartWorkerFail(const SequencedContextId& context_id,
base::Time start_time,
content::StatusCodeResponse status);
BrowserState browser_state() const { return browser_state_; }
RendererState renderer_state() const { return renderer_state_; }
const std::optional<WorkerId>& worker_id() const { return worker_id_; }
static base::AutoReset<bool> AllowMultipleWorkersPerExtensionForTesting();
void StopObservingContextForTest();
// content::ServiceWorkerContextObserverSynchronous:
// Called when an extension service worker is stopping.
// It is considered the "browser-side" signal that the worker is stopping.
// NOTE: this can be called before or after
// `RendererDidStopServiceWorkerContext`.
void OnStoppingSync(int64_t version_id, const GURL& scope) override;
// Called when an extension service worker has stopped.
// It is considered the "browser-side" signal that the worker has stopped.
// NOTE: this can be called before or after
// `RendererDidStopServiceWorkerContext`.
void OnStoppedSync(int64_t version_id, const GURL& scope) override;
private:
void SetWorkerId(const WorkerId& worker_id);
void NotifyObserversIfReady(const SequencedContextId& context_id);
void HandleStop(int64_t version_id, const GURL& scope);
BrowserState browser_state_ = BrowserState::kNotActive;
RendererState renderer_state_ = RendererState::kNotActive;
// Whether the service worker is in the process of starting.
bool worker_starting_ = false;
// Contains the worker's WorkerId associated with this ServiceWorkerState,
// once we have discovered info about the worker.
std::optional<WorkerId> worker_id_;
// Holds a pointer to the service worker context associated with this worker.
const raw_ptr<content::ServiceWorkerContext> service_worker_context_ =
nullptr;
// Holds a pointer to the ProcessManager associated with a profile /
// BrowserContext. This ServiceWorkerState is owned by ServiceWorkerTaskQueue,
// ensuring ProcessManager outlives this instance.
const raw_ptr<const ProcessManager> process_manager_ = nullptr;
base::ScopedObservation<content::ServiceWorkerContext,
content::ServiceWorkerContextObserverSynchronous>
service_worker_context_observation_{this};
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<ServiceWorkerState> weak_factory_{this};
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_SERVICE_WORKER_SERVICE_WORKER_STATE_H_