// Copyright (c) 2025 Salesforce, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_MAIN_H_ #define ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_MAIN_H_ #include #include "base/memory/raw_ptr.h" #include "base/process/process.h" #include "content/public/browser/global_routing_id.h" #include "content/public/browser/service_worker_context.h" #include "content/public/browser/service_worker_version_base_info.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "shell/common/api/api.mojom.h" #include "shell/common/gin_helper/constructible.h" #include "shell/common/gin_helper/pinnable.h" #include "shell/common/gin_helper/wrappable.h" #include "third_party/blink/public/common/service_worker/embedded_worker_status.h" class GURL; namespace content { class StoragePartition; } namespace gin { class Arguments; } // namespace gin namespace gin_helper { class Dictionary; template class Handle; template class Promise; } // namespace gin_helper namespace electron::api { // Key to uniquely identify a ServiceWorkerMain by its Version ID within the // associated StoragePartition. struct ServiceWorkerKey { int64_t version_id; raw_ptr storage_partition; ServiceWorkerKey(int64_t id, const content::StoragePartition* partition) : version_id(id), storage_partition(partition) {} bool operator<(const ServiceWorkerKey& other) const { return std::tie(version_id, storage_partition) < std::tie(other.version_id, other.storage_partition); } bool operator==(const ServiceWorkerKey& other) const { return version_id == other.version_id && storage_partition == other.storage_partition; } struct Hasher { std::size_t operator()(const ServiceWorkerKey& key) const { return std::hash()( key.storage_partition) ^ std::hash()(key.version_id); } }; }; // Creates a wrapper to align with the lifecycle of the non-public // content::ServiceWorkerVersion. Object instances are pinned for the lifetime // of the underlying SW such that registered IPC handlers continue to dispatch. // // Instances are uniquely identified by pairing their version ID and the // StoragePartition in which they're registered. In Electron, this is always // the default StoragePartition for the associated BrowserContext. class ServiceWorkerMain final : public gin_helper::DeprecatedWrappable, public gin_helper::Pinnable, public gin_helper::Constructible { public: // Create a new ServiceWorkerMain and return the V8 wrapper of it. static gin_helper::Handle New(v8::Isolate* isolate); static gin_helper::Handle From( v8::Isolate* isolate, content::ServiceWorkerContext* sw_context, const content::StoragePartition* storage_partition, int64_t version_id); static ServiceWorkerMain* FromVersionID( int64_t version_id, const content::StoragePartition* storage_partition); // gin_helper::Constructible static void FillObjectTemplate(v8::Isolate*, v8::Local); static const char* GetClassName() { return "ServiceWorkerMain"; } // gin_helper::Wrappable static gin::DeprecatedWrapperInfo kWrapperInfo; const char* GetTypeName() override; // disable copy ServiceWorkerMain(const ServiceWorkerMain&) = delete; ServiceWorkerMain& operator=(const ServiceWorkerMain&) = delete; void OnRunningStatusChanged(blink::EmbeddedWorkerStatus running_status); void OnVersionRedundant(); protected: explicit ServiceWorkerMain(content::ServiceWorkerContext* sw_context, int64_t version_id, const ServiceWorkerKey& key); ~ServiceWorkerMain() override; private: void Destroy(); void MaybeDisconnectRemote(); const blink::StorageKey GetStorageKey(); // Increments external requests for the service worker to keep it alive. gin_helper::Dictionary StartExternalRequest(v8::Isolate* isolate, bool has_timeout); void FinishExternalRequest(v8::Isolate* isolate, std::string uuid); size_t CountExternalRequestsForTest(); // Get or create a Mojo connection to the renderer process. mojom::ElectronRenderer* GetRendererApi(); // Send a message to the renderer process. void Send(v8::Isolate* isolate, bool internal, const std::string& channel, v8::Local args); void InvalidateVersionInfo(); const content::ServiceWorkerVersionBaseInfo* version_info() const { return version_info_.get(); } bool IsDestroyed() const; int64_t VersionID() const; GURL ScopeURL() const; GURL ScriptURL() const; // Version ID unique only to the StoragePartition. int64_t version_id_; // Unique identifier pairing the Version ID and StoragePartition. ServiceWorkerKey key_; // Whether the Service Worker version has been destroyed. bool version_destroyed_ = false; // Whether the Service Worker version's state is redundant. bool redundant_ = false; // Store copy of version info so it's accessible when not running. std::unique_ptr version_info_; raw_ptr service_worker_context_; mojo::AssociatedRemote remote_; std::unique_ptr> start_worker_promise_; }; } // namespace electron::api #endif // ELECTRON_SHELL_BROWSER_API_ELECTRON_API_SERVICE_WORKER_MAIN_H_