electron_api_service_worker_main.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // Copyright (c) 2025 Salesforce, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/api/electron_api_service_worker_main.h"
  5. #include <string>
  6. #include <utility>
  7. #include "base/logging.h"
  8. #include "base/no_destructor.h"
  9. #include "content/browser/service_worker/service_worker_context_wrapper.h" // nogncheck
  10. #include "content/browser/service_worker/service_worker_version.h" // nogncheck
  11. #include "gin/handle.h"
  12. #include "gin/object_template_builder.h"
  13. #include "services/service_manager/public/cpp/interface_provider.h"
  14. #include "shell/browser/api/message_port.h"
  15. #include "shell/browser/browser.h"
  16. #include "shell/browser/javascript_environment.h"
  17. #include "shell/common/api/api.mojom.h"
  18. #include "shell/common/gin_converters/blink_converter.h"
  19. #include "shell/common/gin_converters/gurl_converter.h"
  20. #include "shell/common/gin_converters/value_converter.h"
  21. #include "shell/common/gin_helper/dictionary.h"
  22. #include "shell/common/gin_helper/error_thrower.h"
  23. #include "shell/common/gin_helper/object_template_builder.h"
  24. #include "shell/common/gin_helper/promise.h"
  25. #include "shell/common/node_includes.h"
  26. #include "shell/common/v8_util.h"
  27. #include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
  28. namespace {
  29. // Use private API to get the live version of the service worker. This will
  30. // exist while in starting, stopping, or stopped running status.
  31. content::ServiceWorkerVersion* GetLiveVersion(
  32. content::ServiceWorkerContext* service_worker_context,
  33. int64_t version_id) {
  34. auto* wrapper = static_cast<content::ServiceWorkerContextWrapper*>(
  35. service_worker_context);
  36. return wrapper->GetLiveVersion(version_id);
  37. }
  38. // Get a public ServiceWorkerVersionBaseInfo object directly from the service
  39. // worker.
  40. std::optional<content::ServiceWorkerVersionBaseInfo> GetLiveVersionInfo(
  41. content::ServiceWorkerContext* service_worker_context,
  42. int64_t version_id) {
  43. auto* version = GetLiveVersion(service_worker_context, version_id);
  44. if (version) {
  45. return version->GetInfo();
  46. }
  47. return std::nullopt;
  48. }
  49. } // namespace
  50. namespace electron::api {
  51. // ServiceWorkerKey -> ServiceWorkerMain*
  52. using VersionIdMap = absl::flat_hash_map<ServiceWorkerKey,
  53. ServiceWorkerMain*,
  54. ServiceWorkerKey::Hasher>;
  55. VersionIdMap& GetVersionIdMap() {
  56. static base::NoDestructor<VersionIdMap> instance;
  57. return *instance;
  58. }
  59. ServiceWorkerMain* FromServiceWorkerKey(const ServiceWorkerKey& key) {
  60. VersionIdMap& version_map = GetVersionIdMap();
  61. auto iter = version_map.find(key);
  62. auto* service_worker = iter == version_map.end() ? nullptr : iter->second;
  63. return service_worker;
  64. }
  65. // static
  66. ServiceWorkerMain* ServiceWorkerMain::FromVersionID(
  67. int64_t version_id,
  68. const content::StoragePartition* storage_partition) {
  69. ServiceWorkerKey key(version_id, storage_partition);
  70. return FromServiceWorkerKey(key);
  71. }
  72. gin::WrapperInfo ServiceWorkerMain::kWrapperInfo = {gin::kEmbedderNativeGin};
  73. ServiceWorkerMain::ServiceWorkerMain(content::ServiceWorkerContext* sw_context,
  74. int64_t version_id,
  75. const ServiceWorkerKey& key)
  76. : version_id_(version_id), key_(key), service_worker_context_(sw_context) {
  77. GetVersionIdMap().emplace(key_, this);
  78. InvalidateVersionInfo();
  79. }
  80. ServiceWorkerMain::~ServiceWorkerMain() {
  81. Destroy();
  82. }
  83. void ServiceWorkerMain::Destroy() {
  84. version_destroyed_ = true;
  85. InvalidateVersionInfo();
  86. MaybeDisconnectRemote();
  87. GetVersionIdMap().erase(key_);
  88. Unpin();
  89. }
  90. void ServiceWorkerMain::MaybeDisconnectRemote() {
  91. if (remote_.is_bound() &&
  92. (version_destroyed_ ||
  93. (!service_worker_context_->IsLiveStartingServiceWorker(version_id_) &&
  94. !service_worker_context_->IsLiveRunningServiceWorker(version_id_)))) {
  95. remote_.reset();
  96. }
  97. }
  98. mojom::ElectronRenderer* ServiceWorkerMain::GetRendererApi() {
  99. if (!remote_.is_bound()) {
  100. if (!service_worker_context_->IsLiveRunningServiceWorker(version_id_)) {
  101. return nullptr;
  102. }
  103. service_worker_context_->GetRemoteAssociatedInterfaces(version_id_)
  104. .GetInterface(&remote_);
  105. }
  106. return remote_.get();
  107. }
  108. void ServiceWorkerMain::Send(v8::Isolate* isolate,
  109. bool internal,
  110. const std::string& channel,
  111. v8::Local<v8::Value> args) {
  112. blink::CloneableMessage message;
  113. if (!gin::ConvertFromV8(isolate, args, &message)) {
  114. isolate->ThrowException(v8::Exception::Error(
  115. gin::StringToV8(isolate, "Failed to serialize arguments")));
  116. return;
  117. }
  118. auto* renderer_api_remote = GetRendererApi();
  119. if (!renderer_api_remote) {
  120. return;
  121. }
  122. renderer_api_remote->Message(internal, channel, std::move(message));
  123. }
  124. void ServiceWorkerMain::InvalidateVersionInfo() {
  125. if (version_info_ != nullptr) {
  126. version_info_.reset();
  127. }
  128. if (version_destroyed_)
  129. return;
  130. auto version_info = GetLiveVersionInfo(service_worker_context_, version_id_);
  131. if (version_info) {
  132. version_info_ =
  133. std::make_unique<content::ServiceWorkerVersionBaseInfo>(*version_info);
  134. } else {
  135. // When ServiceWorkerContextCore::RemoveLiveVersion is called, it posts a
  136. // task to notify that the service worker has stopped. At this point, the
  137. // live version will no longer exist.
  138. Destroy();
  139. }
  140. }
  141. void ServiceWorkerMain::OnRunningStatusChanged(
  142. blink::EmbeddedWorkerStatus running_status) {
  143. // Disconnect remote when content::ServiceWorkerHost has terminated.
  144. MaybeDisconnectRemote();
  145. InvalidateVersionInfo();
  146. // Redundant worker has been marked for deletion. Now that it's stopped, let's
  147. // destroy our wrapper.
  148. if (redundant_ && running_status == blink::EmbeddedWorkerStatus::kStopped) {
  149. Destroy();
  150. }
  151. }
  152. void ServiceWorkerMain::OnVersionRedundant() {
  153. // Redundant service workers have been either unregistered or replaced. A new
  154. // ServiceWorkerMain will need to be created.
  155. // Set internal state to mark it for deletion once it has fully stopped.
  156. redundant_ = true;
  157. }
  158. bool ServiceWorkerMain::IsDestroyed() const {
  159. return version_destroyed_;
  160. }
  161. const blink::StorageKey ServiceWorkerMain::GetStorageKey() {
  162. const GURL& scope = version_info_ ? version_info()->scope : GURL::EmptyGURL();
  163. return blink::StorageKey::CreateFirstParty(url::Origin::Create(scope));
  164. }
  165. gin_helper::Dictionary ServiceWorkerMain::StartExternalRequest(
  166. v8::Isolate* isolate,
  167. bool has_timeout) {
  168. auto details = gin_helper::Dictionary::CreateEmpty(isolate);
  169. if (version_destroyed_) {
  170. isolate->ThrowException(v8::Exception::TypeError(
  171. gin::StringToV8(isolate, "ServiceWorkerMain is destroyed")));
  172. return details;
  173. }
  174. auto request_uuid = base::Uuid::GenerateRandomV4();
  175. auto timeout_type =
  176. has_timeout
  177. ? content::ServiceWorkerExternalRequestTimeoutType::kDefault
  178. : content::ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout;
  179. content::ServiceWorkerExternalRequestResult start_result =
  180. service_worker_context_->StartingExternalRequest(
  181. version_id_, timeout_type, request_uuid);
  182. details.Set("id", request_uuid.AsLowercaseString());
  183. details.Set("ok",
  184. start_result == content::ServiceWorkerExternalRequestResult::kOk);
  185. return details;
  186. }
  187. void ServiceWorkerMain::FinishExternalRequest(v8::Isolate* isolate,
  188. std::string uuid) {
  189. base::Uuid request_uuid = base::Uuid::ParseLowercase(uuid);
  190. if (!request_uuid.is_valid()) {
  191. isolate->ThrowException(v8::Exception::TypeError(
  192. gin::StringToV8(isolate, "Invalid external request UUID")));
  193. return;
  194. }
  195. DCHECK(service_worker_context_);
  196. if (!service_worker_context_)
  197. return;
  198. content::ServiceWorkerExternalRequestResult result =
  199. service_worker_context_->FinishedExternalRequest(version_id_,
  200. request_uuid);
  201. std::string error;
  202. switch (result) {
  203. case content::ServiceWorkerExternalRequestResult::kOk:
  204. break;
  205. case content::ServiceWorkerExternalRequestResult::kBadRequestId:
  206. error = "Unknown external request UUID";
  207. break;
  208. case content::ServiceWorkerExternalRequestResult::kWorkerNotRunning:
  209. error = "Service worker is no longer running";
  210. break;
  211. case content::ServiceWorkerExternalRequestResult::kWorkerNotFound:
  212. error = "Service worker was not found";
  213. break;
  214. case content::ServiceWorkerExternalRequestResult::kNullContext:
  215. default:
  216. error = "Service worker context is unavailable and may be shutting down";
  217. break;
  218. }
  219. if (!error.empty()) {
  220. isolate->ThrowException(
  221. v8::Exception::TypeError(gin::StringToV8(isolate, error)));
  222. }
  223. }
  224. size_t ServiceWorkerMain::CountExternalRequestsForTest() {
  225. if (version_destroyed_)
  226. return 0;
  227. auto& storage_key = GetStorageKey();
  228. return service_worker_context_->CountExternalRequestsForTest(storage_key);
  229. }
  230. int64_t ServiceWorkerMain::VersionID() const {
  231. return version_id_;
  232. }
  233. GURL ServiceWorkerMain::ScopeURL() const {
  234. if (version_destroyed_)
  235. return {};
  236. return version_info()->scope;
  237. }
  238. GURL ServiceWorkerMain::ScriptURL() const {
  239. if (version_destroyed_)
  240. return {};
  241. return version_info()->script_url;
  242. }
  243. // static
  244. gin::Handle<ServiceWorkerMain> ServiceWorkerMain::New(v8::Isolate* isolate) {
  245. return gin::Handle<ServiceWorkerMain>();
  246. }
  247. // static
  248. gin::Handle<ServiceWorkerMain> ServiceWorkerMain::From(
  249. v8::Isolate* isolate,
  250. content::ServiceWorkerContext* sw_context,
  251. const content::StoragePartition* storage_partition,
  252. int64_t version_id) {
  253. ServiceWorkerKey service_worker_key(version_id, storage_partition);
  254. auto* service_worker = FromServiceWorkerKey(service_worker_key);
  255. if (service_worker)
  256. return gin::CreateHandle(isolate, service_worker);
  257. // Ensure ServiceWorkerVersion exists and is not redundant (pending deletion)
  258. auto* live_version = GetLiveVersion(sw_context, version_id);
  259. if (!live_version || live_version->is_redundant()) {
  260. return gin::Handle<ServiceWorkerMain>();
  261. }
  262. auto handle = gin::CreateHandle(
  263. isolate,
  264. new ServiceWorkerMain(sw_context, version_id, service_worker_key));
  265. // Prevent garbage collection of worker until it has been deleted internally.
  266. handle->Pin(isolate);
  267. return handle;
  268. }
  269. // static
  270. void ServiceWorkerMain::FillObjectTemplate(
  271. v8::Isolate* isolate,
  272. v8::Local<v8::ObjectTemplate> templ) {
  273. gin_helper::ObjectTemplateBuilder(isolate, templ)
  274. .SetMethod("_send", &ServiceWorkerMain::Send)
  275. .SetMethod("isDestroyed", &ServiceWorkerMain::IsDestroyed)
  276. .SetMethod("_startExternalRequest",
  277. &ServiceWorkerMain::StartExternalRequest)
  278. .SetMethod("_finishExternalRequest",
  279. &ServiceWorkerMain::FinishExternalRequest)
  280. .SetMethod("_countExternalRequests",
  281. &ServiceWorkerMain::CountExternalRequestsForTest)
  282. .SetProperty("versionId", &ServiceWorkerMain::VersionID)
  283. .SetProperty("scope", &ServiceWorkerMain::ScopeURL)
  284. .SetProperty("scriptURL", &ServiceWorkerMain::ScriptURL)
  285. .Build();
  286. }
  287. const char* ServiceWorkerMain::GetTypeName() {
  288. return GetClassName();
  289. }
  290. } // namespace electron::api
  291. namespace {
  292. using electron::api::ServiceWorkerMain;
  293. void Initialize(v8::Local<v8::Object> exports,
  294. v8::Local<v8::Value> unused,
  295. v8::Local<v8::Context> context,
  296. void* priv) {
  297. v8::Isolate* isolate = context->GetIsolate();
  298. gin_helper::Dictionary dict(isolate, exports);
  299. dict.Set("ServiceWorkerMain", ServiceWorkerMain::GetConstructor(context));
  300. }
  301. } // namespace
  302. NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_service_worker_main,
  303. Initialize)