mirror of
https://github.com/electron/electron.git
synced 2026-01-06 22:24:03 -05:00
* chore: bump chromium in DEPS to 144.0.7507.0
* chore: bump chromium in DEPS to 144.0.7508.0
* chore: update patches
* 7101838: [pathbuilder] Enforce immutable SkPath APIs globally
https://chromium-review.googlesource.com/c/chromium/src/+/7101838
* chore: update filenames.libcxx.gni
* [pathbuilder] Enforce immutable SkPath APIs globally
https://chromium-review.googlesource.com/c/chromium/src/+/7101838
* Reduce service_worker_info.h includes in headers
https://chromium-review.googlesource.com/c/chromium/src/+/7108401
* chore: bump chromium in DEPS to 144.0.7510.0
* chore: update patches
* Use internal popup menus for tabs in actor-controlled states
https://chromium-review.googlesource.com/c/chromium/src/+/7074751
* [api] Delete deprecated fields on v8::Isolate
https://chromium-review.googlesource.com/c/v8/v8/+/7081397
xref: 98d243aea0
* Fixup Reduce service_worker_info.h includes in headers
* Promote deprecation of v8::Context and v8::Object API methods
https://chromium-review.googlesource.com/c/v8/v8/+/7087956
* fixup Promote deprecation of v8::Context and v8::Object API methods
* chore: bump chromium in DEPS to 144.0.7512.1
* chore: update patches
* fixup [pathbuilder] Enforce immutable SkPath APIs global
* chore: update filenames.hunspell.gni
* fix deprecation of v8::Context and v8::Object API methods for nan
https://chromium-review.googlesource.com/c/v8/v8/+/7087956
* [PDF] Implement PdfHelpBubbleHandlerFactory
https://chromium-review.googlesource.com/c/chromium/src/+/7056325
also: [PDF Ink Signatures] Hook up IPH
https://chromium-review.googlesource.com/c/chromium/src/+/7056207
* Remove base/hash/md5.h
https://chromium-review.googlesource.com/c/chromium/src/+/7113738
* fixup for lint
* Remove deprecated interceptor callback types and AccessControl enum
https://chromium-review.googlesource.com/c/v8/v8/+/7112747
* fixup for lint
* fixup [PDF] Implement PdfHelpBubbleHandlerFactory
* use base::SHA1HashString instead of std::hash
---------
Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
368 lines
12 KiB
C++
368 lines
12 KiB
C++
// Copyright (c) 2025 Salesforce, Inc.
|
|
// Use of this source code is governed by the MIT license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "shell/browser/api/electron_api_service_worker_main.h"
|
|
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "base/logging.h"
|
|
#include "base/no_destructor.h"
|
|
#include "content/browser/service_worker/service_worker_context_wrapper.h" // nogncheck
|
|
#include "content/browser/service_worker/service_worker_info.h" // nogncheck
|
|
#include "content/browser/service_worker/service_worker_version.h" // nogncheck
|
|
#include "gin/object_template_builder.h"
|
|
#include "services/service_manager/public/cpp/interface_provider.h"
|
|
#include "shell/browser/api/message_port.h"
|
|
#include "shell/browser/browser.h"
|
|
#include "shell/browser/javascript_environment.h"
|
|
#include "shell/common/api/api.mojom.h"
|
|
#include "shell/common/gin_converters/blink_converter.h"
|
|
#include "shell/common/gin_converters/gurl_converter.h"
|
|
#include "shell/common/gin_converters/value_converter.h"
|
|
#include "shell/common/gin_helper/dictionary.h"
|
|
#include "shell/common/gin_helper/handle.h"
|
|
#include "shell/common/gin_helper/object_template_builder.h"
|
|
#include "shell/common/gin_helper/promise.h"
|
|
#include "shell/common/node_includes.h"
|
|
#include "shell/common/v8_util.h"
|
|
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
|
|
|
|
namespace {
|
|
|
|
// Use private API to get the live version of the service worker. This will
|
|
// exist while in starting, stopping, or stopped running status.
|
|
content::ServiceWorkerVersion* GetLiveVersion(
|
|
content::ServiceWorkerContext* service_worker_context,
|
|
int64_t version_id) {
|
|
auto* wrapper = static_cast<content::ServiceWorkerContextWrapper*>(
|
|
service_worker_context);
|
|
return wrapper->GetLiveVersion(version_id);
|
|
}
|
|
|
|
// Get a public ServiceWorkerVersionBaseInfo object directly from the service
|
|
// worker.
|
|
std::optional<content::ServiceWorkerVersionBaseInfo> GetLiveVersionInfo(
|
|
content::ServiceWorkerContext* service_worker_context,
|
|
int64_t version_id) {
|
|
auto* version = GetLiveVersion(service_worker_context, version_id);
|
|
if (version) {
|
|
return version->GetInfo();
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace electron::api {
|
|
|
|
// ServiceWorkerKey -> ServiceWorkerMain*
|
|
using VersionIdMap = absl::flat_hash_map<ServiceWorkerKey,
|
|
ServiceWorkerMain*,
|
|
ServiceWorkerKey::Hasher>;
|
|
|
|
VersionIdMap& GetVersionIdMap() {
|
|
static base::NoDestructor<VersionIdMap> instance;
|
|
return *instance;
|
|
}
|
|
|
|
ServiceWorkerMain* FromServiceWorkerKey(const ServiceWorkerKey& key) {
|
|
VersionIdMap& version_map = GetVersionIdMap();
|
|
auto iter = version_map.find(key);
|
|
auto* service_worker = iter == version_map.end() ? nullptr : iter->second;
|
|
return service_worker;
|
|
}
|
|
|
|
// static
|
|
ServiceWorkerMain* ServiceWorkerMain::FromVersionID(
|
|
int64_t version_id,
|
|
const content::StoragePartition* storage_partition) {
|
|
ServiceWorkerKey key(version_id, storage_partition);
|
|
return FromServiceWorkerKey(key);
|
|
}
|
|
|
|
gin::DeprecatedWrapperInfo ServiceWorkerMain::kWrapperInfo = {
|
|
gin::kEmbedderNativeGin};
|
|
|
|
ServiceWorkerMain::ServiceWorkerMain(content::ServiceWorkerContext* sw_context,
|
|
int64_t version_id,
|
|
const ServiceWorkerKey& key)
|
|
: version_id_(version_id), key_(key), service_worker_context_(sw_context) {
|
|
GetVersionIdMap().emplace(key_, this);
|
|
InvalidateVersionInfo();
|
|
}
|
|
|
|
ServiceWorkerMain::~ServiceWorkerMain() {
|
|
Destroy();
|
|
}
|
|
|
|
void ServiceWorkerMain::Destroy() {
|
|
version_destroyed_ = true;
|
|
InvalidateVersionInfo();
|
|
MaybeDisconnectRemote();
|
|
GetVersionIdMap().erase(key_);
|
|
Unpin();
|
|
}
|
|
|
|
void ServiceWorkerMain::MaybeDisconnectRemote() {
|
|
if (remote_.is_bound() &&
|
|
(version_destroyed_ ||
|
|
(!service_worker_context_->IsLiveStartingServiceWorker(version_id_) &&
|
|
!service_worker_context_->IsLiveRunningServiceWorker(version_id_)))) {
|
|
remote_.reset();
|
|
}
|
|
}
|
|
|
|
mojom::ElectronRenderer* ServiceWorkerMain::GetRendererApi() {
|
|
if (!remote_.is_bound()) {
|
|
if (!service_worker_context_->IsLiveRunningServiceWorker(version_id_)) {
|
|
return nullptr;
|
|
}
|
|
|
|
service_worker_context_->GetRemoteAssociatedInterfaces(version_id_)
|
|
.GetInterface(&remote_);
|
|
}
|
|
return remote_.get();
|
|
}
|
|
|
|
void ServiceWorkerMain::Send(v8::Isolate* isolate,
|
|
bool internal,
|
|
const std::string& channel,
|
|
v8::Local<v8::Value> args) {
|
|
blink::CloneableMessage message;
|
|
if (!gin::ConvertFromV8(isolate, args, &message)) {
|
|
isolate->ThrowException(v8::Exception::Error(
|
|
gin::StringToV8(isolate, "Failed to serialize arguments")));
|
|
return;
|
|
}
|
|
|
|
auto* renderer_api_remote = GetRendererApi();
|
|
if (!renderer_api_remote) {
|
|
return;
|
|
}
|
|
|
|
renderer_api_remote->Message(internal, channel, std::move(message));
|
|
}
|
|
|
|
void ServiceWorkerMain::InvalidateVersionInfo() {
|
|
if (version_info_ != nullptr) {
|
|
version_info_.reset();
|
|
}
|
|
|
|
if (version_destroyed_)
|
|
return;
|
|
|
|
auto version_info = GetLiveVersionInfo(service_worker_context_, version_id_);
|
|
if (version_info) {
|
|
version_info_ =
|
|
std::make_unique<content::ServiceWorkerVersionBaseInfo>(*version_info);
|
|
} else {
|
|
// When ServiceWorkerContextCore::RemoveLiveVersion is called, it posts a
|
|
// task to notify that the service worker has stopped. At this point, the
|
|
// live version will no longer exist.
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
void ServiceWorkerMain::OnRunningStatusChanged(
|
|
blink::EmbeddedWorkerStatus running_status) {
|
|
// Disconnect remote when content::ServiceWorkerHost has terminated.
|
|
MaybeDisconnectRemote();
|
|
|
|
InvalidateVersionInfo();
|
|
|
|
// Redundant worker has been marked for deletion. Now that it's stopped, let's
|
|
// destroy our wrapper.
|
|
if (redundant_ && running_status == blink::EmbeddedWorkerStatus::kStopped) {
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
void ServiceWorkerMain::OnVersionRedundant() {
|
|
// Redundant service workers have been either unregistered or replaced. A new
|
|
// ServiceWorkerMain will need to be created.
|
|
// Set internal state to mark it for deletion once it has fully stopped.
|
|
redundant_ = true;
|
|
}
|
|
|
|
bool ServiceWorkerMain::IsDestroyed() const {
|
|
return version_destroyed_;
|
|
}
|
|
|
|
const blink::StorageKey ServiceWorkerMain::GetStorageKey() {
|
|
const GURL& scope = version_info_ ? version_info()->scope : GURL::EmptyGURL();
|
|
return blink::StorageKey::CreateFirstParty(url::Origin::Create(scope));
|
|
}
|
|
|
|
gin_helper::Dictionary ServiceWorkerMain::StartExternalRequest(
|
|
v8::Isolate* isolate,
|
|
bool has_timeout) {
|
|
auto details = gin_helper::Dictionary::CreateEmpty(isolate);
|
|
|
|
if (version_destroyed_) {
|
|
isolate->ThrowException(v8::Exception::TypeError(
|
|
gin::StringToV8(isolate, "ServiceWorkerMain is destroyed")));
|
|
return details;
|
|
}
|
|
|
|
auto request_uuid = base::Uuid::GenerateRandomV4();
|
|
auto timeout_type =
|
|
has_timeout
|
|
? content::ServiceWorkerExternalRequestTimeoutType::kDefault
|
|
: content::ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout;
|
|
|
|
content::ServiceWorkerExternalRequestResult start_result =
|
|
service_worker_context_->StartingExternalRequest(
|
|
version_id_, timeout_type, request_uuid);
|
|
|
|
details.Set("id", request_uuid.AsLowercaseString());
|
|
details.Set("ok",
|
|
start_result == content::ServiceWorkerExternalRequestResult::kOk);
|
|
|
|
return details;
|
|
}
|
|
|
|
void ServiceWorkerMain::FinishExternalRequest(v8::Isolate* isolate,
|
|
std::string uuid) {
|
|
base::Uuid request_uuid = base::Uuid::ParseLowercase(uuid);
|
|
if (!request_uuid.is_valid()) {
|
|
isolate->ThrowException(v8::Exception::TypeError(
|
|
gin::StringToV8(isolate, "Invalid external request UUID")));
|
|
return;
|
|
}
|
|
|
|
DCHECK(service_worker_context_);
|
|
if (!service_worker_context_)
|
|
return;
|
|
|
|
content::ServiceWorkerExternalRequestResult result =
|
|
service_worker_context_->FinishedExternalRequest(version_id_,
|
|
request_uuid);
|
|
|
|
std::string error;
|
|
switch (result) {
|
|
case content::ServiceWorkerExternalRequestResult::kOk:
|
|
break;
|
|
case content::ServiceWorkerExternalRequestResult::kBadRequestId:
|
|
error = "Unknown external request UUID";
|
|
break;
|
|
case content::ServiceWorkerExternalRequestResult::kWorkerNotRunning:
|
|
error = "Service worker is no longer running";
|
|
break;
|
|
case content::ServiceWorkerExternalRequestResult::kWorkerNotFound:
|
|
error = "Service worker was not found";
|
|
break;
|
|
case content::ServiceWorkerExternalRequestResult::kNullContext:
|
|
default:
|
|
error = "Service worker context is unavailable and may be shutting down";
|
|
break;
|
|
}
|
|
|
|
if (!error.empty()) {
|
|
isolate->ThrowException(
|
|
v8::Exception::TypeError(gin::StringToV8(isolate, error)));
|
|
}
|
|
}
|
|
|
|
size_t ServiceWorkerMain::CountExternalRequestsForTest() {
|
|
if (version_destroyed_)
|
|
return 0;
|
|
auto& storage_key = GetStorageKey();
|
|
return service_worker_context_->CountExternalRequestsForTest(storage_key);
|
|
}
|
|
|
|
int64_t ServiceWorkerMain::VersionID() const {
|
|
return version_id_;
|
|
}
|
|
|
|
GURL ServiceWorkerMain::ScopeURL() const {
|
|
if (version_destroyed_)
|
|
return {};
|
|
return version_info()->scope;
|
|
}
|
|
|
|
GURL ServiceWorkerMain::ScriptURL() const {
|
|
if (version_destroyed_)
|
|
return {};
|
|
return version_info()->script_url;
|
|
}
|
|
|
|
// static
|
|
gin_helper::Handle<ServiceWorkerMain> ServiceWorkerMain::New(
|
|
v8::Isolate* isolate) {
|
|
return gin_helper::Handle<ServiceWorkerMain>();
|
|
}
|
|
|
|
// static
|
|
gin_helper::Handle<ServiceWorkerMain> ServiceWorkerMain::From(
|
|
v8::Isolate* isolate,
|
|
content::ServiceWorkerContext* sw_context,
|
|
const content::StoragePartition* storage_partition,
|
|
int64_t version_id) {
|
|
ServiceWorkerKey service_worker_key(version_id, storage_partition);
|
|
|
|
auto* service_worker = FromServiceWorkerKey(service_worker_key);
|
|
if (service_worker)
|
|
return gin_helper::CreateHandle(isolate, service_worker);
|
|
|
|
// Ensure ServiceWorkerVersion exists and is not redundant (pending deletion)
|
|
auto* live_version = GetLiveVersion(sw_context, version_id);
|
|
if (!live_version || live_version->is_redundant()) {
|
|
return gin_helper::Handle<ServiceWorkerMain>();
|
|
}
|
|
|
|
auto handle = gin_helper::CreateHandle(
|
|
isolate,
|
|
new ServiceWorkerMain(sw_context, version_id, service_worker_key));
|
|
|
|
// Prevent garbage collection of worker until it has been deleted internally.
|
|
handle->Pin(isolate);
|
|
|
|
return handle;
|
|
}
|
|
|
|
// static
|
|
void ServiceWorkerMain::FillObjectTemplate(
|
|
v8::Isolate* isolate,
|
|
v8::Local<v8::ObjectTemplate> templ) {
|
|
gin_helper::ObjectTemplateBuilder(isolate, templ)
|
|
.SetMethod("_send", &ServiceWorkerMain::Send)
|
|
.SetMethod("isDestroyed", &ServiceWorkerMain::IsDestroyed)
|
|
.SetMethod("_startExternalRequest",
|
|
&ServiceWorkerMain::StartExternalRequest)
|
|
.SetMethod("_finishExternalRequest",
|
|
&ServiceWorkerMain::FinishExternalRequest)
|
|
.SetMethod("_countExternalRequests",
|
|
&ServiceWorkerMain::CountExternalRequestsForTest)
|
|
.SetProperty("versionId", &ServiceWorkerMain::VersionID)
|
|
.SetProperty("scope", &ServiceWorkerMain::ScopeURL)
|
|
.SetProperty("scriptURL", &ServiceWorkerMain::ScriptURL)
|
|
.Build();
|
|
}
|
|
|
|
const char* ServiceWorkerMain::GetTypeName() {
|
|
return GetClassName();
|
|
}
|
|
|
|
} // namespace electron::api
|
|
|
|
namespace {
|
|
|
|
using electron::api::ServiceWorkerMain;
|
|
|
|
void Initialize(v8::Local<v8::Object> exports,
|
|
v8::Local<v8::Value> unused,
|
|
v8::Local<v8::Context> context,
|
|
void* priv) {
|
|
v8::Isolate* const isolate = electron::JavascriptEnvironment::GetIsolate();
|
|
gin_helper::Dictionary dict{isolate, exports};
|
|
dict.Set("ServiceWorkerMain",
|
|
ServiceWorkerMain::GetConstructor(isolate, context));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_service_worker_main,
|
|
Initialize)
|