Compare commits

..

1 Commits

Author SHA1 Message Date
Sam Attard
b8f6459e44 refactor: attach translator holder via v8::Function data slot 2026-04-10 04:15:35 +00:00
24 changed files with 77 additions and 301 deletions

View File

@@ -15,7 +15,7 @@ runs:
git config --global core.preloadindex true
git config --global core.longpaths true
fi
export BUILD_TOOLS_SHA=1b7bd25dae4a780bb3170fff56c9327b53aaf7eb
export BUILD_TOOLS_SHA=a0cc95a1884a631559bcca0c948465b725d9295a
npm i -g @electron/build-tools
# Update depot_tools to ensure python
e d update_depot_tools
@@ -29,4 +29,4 @@ runs:
else
echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH
echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH
fi
fi

View File

@@ -50,7 +50,7 @@ jobs:
field-value: ✅ Reviewed
pull-request-labeled-ai-pr:
name: ai-pr label added
if: github.event.label.name == 'ai-pr' && github.event.pull_request.state != 'closed'
if: github.event.label.name == 'ai-pr'
runs-on: ubuntu-latest
permissions: {}
steps:

View File

@@ -63,17 +63,10 @@ See [`Menu`](menu.md) for examples.
* `afterGroupContaining` string[] (optional) - Provides a means for a single context menu to declare
the placement of their containing group after the containing group of the item
with the specified id.
* `badge` Object (optional) _macOS_ - Only available on macOS 14 and up.
* `type` string (optional) - Can be one of `alerts`, `updates`, `new-items` or `none`. Default is `none`.
* `count` number (optional) - The number of items the badge displays. Cannot be used with `type: 'none'`.
* `content` string (optional) - A custom string to display in the badge. Only usable with `type: 'none'`.
> [!NOTE]
> `acceleratorWorksWhenHidden` is specified as being macOS-only because accelerators always work when items are hidden on Windows and Linux. The option is exposed to users to give them the option to turn it off, as this is possible in native macOS development.
> [!NOTE]
> If you use one of the predefined badge types on macOS (not 'none'), the system localizes and pluralizes the string for you. If you create your own custom badge string, you need to localize and pluralize that string yourself.
### Instance Properties
The following properties are available on instances of `MenuItem`:
@@ -188,9 +181,3 @@ A `number` indicating an item's sequential unique id.
#### `menuItem.menu`
A [`Menu`](menu.md) that the item is a part of.
#### `menuItem.badge` _macOS_
An [`MenuItemBadge`](structures/menu-item-badge.md) indicating the badge for the menu item.
This property can be dynamically changed. Only available on macOS 14 and up.

View File

@@ -1,5 +0,0 @@
# MenuItemBadge Object
* `type` string (optional) - Can be one of `alerts`, `updates`, `new-items` or `none`. Default is `none`.
* `count` number (optional) - The number of items the badge displays. Cannot be used with `type: 'none'`.
* `content` string (optional) - A custom string to display in the badge. Only usable with `type: 'none'`.

View File

@@ -111,7 +111,6 @@ auto_filenames = {
"docs/api/structures/media-access-permission-request.md",
"docs/api/structures/memory-info.md",
"docs/api/structures/memory-usage-details.md",
"docs/api/structures/menu-item-badge.md",
"docs/api/structures/mime-typed-buffer.md",
"docs/api/structures/mouse-input-event.md",
"docs/api/structures/mouse-wheel-input-event.md",

View File

@@ -38,24 +38,6 @@ const MenuItem = function (this: any, options: any) {
this.overrideProperty('acceleratorWorksWhenHidden', true);
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role));
if (process.platform === 'darwin') {
let badgeValue = options.badge;
Object.defineProperty(this, 'badge', {
get: () => badgeValue,
set: (newValue) => {
badgeValue = newValue;
// Update native badge if this item is already in a menu
if (this.menu) {
const index = this.menu.getIndexOfCommandId(this.commandId);
if (index !== -1 && badgeValue) {
this.menu.setBadge(index, badgeValue);
}
}
},
enumerable: true
});
}
if (!MenuItem.types.includes(this.type)) {
throw new Error(`Unknown menu item type: ${this.type}`);
}

View File

@@ -176,9 +176,6 @@ Menu.prototype.insert = function (pos, item) {
if (item.type === 'palette' || item.type === 'header') {
this.setCustomType(pos, item.type);
}
if (process.platform === 'darwin' && item.badge) {
this.setBadge(pos, item.badge);
}
// Make menu accessible to items.
item.overrideReadOnlyProperty('menu', this);

View File

@@ -18,6 +18,7 @@
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/image_converter.h"
#include "shell/common/gin_converters/optional_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/object_template_builder.h"
#include "shell/common/node_includes.h"
#include "ui/base/models/image_model.h"
@@ -27,7 +28,6 @@
namespace gin {
using SharingItem = electron::ElectronMenuModel::SharingItem;
using Badge = electron::ElectronMenuModel::Badge;
template <>
struct Converter<SharingItem> {
@@ -44,28 +44,6 @@ struct Converter<SharingItem> {
}
};
template <>
struct Converter<Badge> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
Badge* out) {
gin_helper::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
std::string type_str;
if (dict.Get("type", &type_str)) {
out->type = base::UTF8ToUTF16(type_str);
} else {
out->type = u"none";
}
dict.GetOptional("count", &(out->count));
dict.GetOptional("content", &(out->content));
return true;
}
};
} // namespace gin
#endif
@@ -274,21 +252,6 @@ void Menu::SetCustomType(int index, const std::u16string& customType) {
model_->SetCustomType(index, customType);
}
#if BUILDFLAG(IS_MAC)
void Menu::SetBadge(int index, const gin_helper::Dictionary& badge_dict) {
ElectronMenuModel::Badge badge;
std::string type_str;
if (badge_dict.Get("type", &type_str)) {
badge.type = base::UTF8ToUTF16(type_str);
} else {
badge.type = u"none";
}
badge_dict.GetOptional("count", &badge.count);
badge_dict.GetOptional("content", &badge.content);
model_->SetBadge(index, std::move(badge));
}
#endif
void Menu::Clear() {
model_->Clear();
}
@@ -329,12 +292,8 @@ void Menu::FillObjectTemplate(v8::Isolate* isolate,
.SetMethod("setToolTip", &Menu::SetToolTip)
.SetMethod("setRole", &Menu::SetRole)
.SetMethod("setCustomType", &Menu::SetCustomType)
#if BUILDFLAG(IS_MAC)
.SetMethod("setBadge", &Menu::SetBadge)
#endif
.SetMethod("clear", &Menu::Clear)
.SetMethod("getItemCount", &Menu::GetItemCount)
.SetMethod("getIndexOfCommandId", &Menu::GetIndexOfCommandId)
.SetMethod("popupAt", &Menu::PopupAt)
.SetMethod("closePopupAt", &Menu::ClosePopupAt)
.SetMethod("_getAcceleratorTextAt", &Menu::GetAcceleratorTextAtForTesting)

View File

@@ -13,7 +13,6 @@
#include "shell/browser/event_emitter_mixin.h"
#include "shell/browser/ui/electron_menu_model.h"
#include "shell/common/gin_helper/constructible.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/self_keep_alive.h"
#include "ui/base/mojom/menu_source_type.mojom-forward.h"
@@ -129,9 +128,6 @@ class Menu : public gin::Wrappable<Menu>,
void SetToolTip(int index, const std::u16string& toolTip);
void SetRole(int index, const std::u16string& role);
void SetCustomType(int index, const std::u16string& customType);
#if BUILDFLAG(IS_MAC)
void SetBadge(int index, const gin_helper::Dictionary& badge);
#endif
void Clear();
int GetIndexOfCommandId(int command_id) const;
int GetItemCount() const;

View File

@@ -550,7 +550,8 @@ gin::WrapperInfo Session::kWrapperInfo = {{gin::kEmbedderNativeGin},
Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
: isolate_(isolate),
network_emulation_token_(base::UnguessableToken::Create()),
browser_context_{browser_context} {
browser_context_{
raw_ref<ElectronBrowserContext>::from_ptr(browser_context)} {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
data->AddDisposeObserver(this);
// Observe DownloadManager to get download notifications.
@@ -583,21 +584,16 @@ Session::~Session() {
}
void Session::Dispose() {
if (!keep_alive_)
return;
ElectronBrowserContext* const browser_context = this->browser_context();
if (!browser_context)
return;
browser_context->GetDownloadManager()->RemoveObserver(this);
if (keep_alive_) {
browser_context()->GetDownloadManager()->RemoveObserver(this);
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
if (auto* service =
SpellcheckServiceFactory::GetForContext(browser_context)) {
service->SetHunspellObserver(nullptr);
}
if (auto* service =
SpellcheckServiceFactory::GetForContext(browser_context())) {
service->SetHunspellObserver(nullptr);
}
#endif
}
}
void Session::OnDownloadCreated(content::DownloadManager* manager,
@@ -1879,7 +1875,6 @@ void Session::OnBeforeMicrotasksRunnerDispose(v8::Isolate* isolate) {
data->RemoveDisposeObserver(this);
Dispose();
weak_factory_.Invalidate();
browser_context_ = nullptr;
keep_alive_.Clear();
}

View File

@@ -10,6 +10,7 @@
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "content/public/browser/download_manager.h"
@@ -102,8 +103,8 @@ class Session final : public gin::Wrappable<Session>,
Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context);
~Session() override;
[[nodiscard]] ElectronBrowserContext* browser_context() const {
return browser_context_;
ElectronBrowserContext* browser_context() const {
return &browser_context_.get();
}
// gin::Wrappable
@@ -224,7 +225,7 @@ class Session final : public gin::Wrappable<Session>,
// The client id to enable the network throttler.
base::UnguessableToken network_emulation_token_;
raw_ptr<ElectronBrowserContext> browser_context_;
const raw_ref<ElectronBrowserContext> browser_context_;
gin::WeakCellFactory<Session> weak_factory_{this};

View File

@@ -910,15 +910,14 @@ WebContents::WebContents(v8::Isolate* isolate,
session = Session::FromPartition(isolate, "");
}
session_ = session;
ElectronBrowserContext* const browser_context = session->browser_context();
DCHECK(browser_context != nullptr);
std::unique_ptr<content::WebContents> web_contents;
if (is_guest()) {
scoped_refptr<content::SiteInstance> site_instance =
content::SiteInstance::CreateForURL(browser_context,
content::SiteInstance::CreateForURL(session->browser_context(),
GURL("chrome-guest://fake-host"));
content::WebContents::CreateParams params{browser_context, site_instance};
content::WebContents::CreateParams params(session->browser_context(),
site_instance);
guest_delegate_ =
std::make_unique<WebViewGuestDelegate>(embedder_->web_contents(), this);
params.guest_delegate = guest_delegate_.get();
@@ -946,7 +945,7 @@ WebContents::WebContents(v8::Isolate* isolate,
SkColor bc = ParseCSSColor(background_color_str).value_or(SK_ColorWHITE);
bool transparent = bc == SK_ColorTRANSPARENT;
content::WebContents::CreateParams params{browser_context};
content::WebContents::CreateParams params(session->browser_context());
auto* view = new OffScreenWebContentsView(
transparent, offscreen_use_shared_texture_,
offscreen_shared_texture_pixel_format_, offscreen_device_scale_factor_,
@@ -957,13 +956,13 @@ WebContents::WebContents(v8::Isolate* isolate,
web_contents = content::WebContents::Create(params);
view->SetWebContents(web_contents.get());
} else {
content::WebContents::CreateParams params{browser_context};
content::WebContents::CreateParams params(session->browser_context());
params.initially_hidden = !initially_shown;
web_contents = content::WebContents::Create(params);
}
InitWithSessionAndOptions(isolate, std::move(web_contents), browser_context,
options);
InitWithSessionAndOptions(isolate, std::move(web_contents),
session->browser_context(), options);
}
void WebContents::InitZoomController(content::WebContents* web_contents,

View File

@@ -626,9 +626,6 @@ void ElectronBrowserMainParts::PostMainMessageLoopRun() {
#if BUILDFLAG(IS_LINUX)
ui::OzonePlatform::GetInstance()->PostMainMessageLoopRun();
#endif
browser_.reset();
js_env_.reset();
}
#if !BUILDFLAG(IS_MAC)

View File

@@ -116,6 +116,10 @@ class FileSystemAccessPermissionContext
content::GlobalRenderFrameHostId frame_id,
EntriesAllowedByEnterprisePolicyCallback callback) override;
enum class Access { kRead, kWrite, kReadWrite };
enum class RequestType { kNewPermission, kRestorePermissions };
void RevokeActiveGrants(const url::Origin& origin,
const base::FilePath& file_path = base::FilePath());

View File

@@ -117,30 +117,6 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
return result;
}
// Convert a Badge to an NSMenuItemBadge.
NSMenuItemBadge* CreateBadge(const electron::ElectronMenuModel::Badge& badge)
API_AVAILABLE(macos(14.0)) {
NSString* badgeType = base::SysUTF16ToNSString(badge.type);
if ([badgeType isEqualToString:@"alerts"]) {
if (badge.count.has_value())
return [NSMenuItemBadge alertsWithCount:badge.count.value()];
} else if ([badgeType isEqualToString:@"updates"]) {
if (badge.count.has_value())
return [NSMenuItemBadge updatesWithCount:badge.count.value()];
} else if ([badgeType isEqualToString:@"new-items"]) {
if (badge.count.has_value())
return [NSMenuItemBadge newItemsWithCount:badge.count.value()];
} else if ([badgeType isEqualToString:@"none"]) {
if (badge.content.has_value()) {
NSString* content = base::SysUTF8ToNSString(badge.content.value());
return [[NSMenuItemBadge alloc] initWithString:content];
}
}
return nil;
}
} // namespace
// This class stores a base::WeakPtr<electron::ElectronMenuModel> as an
@@ -365,12 +341,14 @@ NSMenuItemBadge* CreateBadge(const electron::ElectronMenuModel::Badge& badge)
electron::ElectronMenuModel::ItemType type = model->GetTypeAt(index);
std::u16string customType = model->GetCustomTypeAt(index);
// The sectionHeaderWithTitle menu item is only available in macOS 14.0+.
if (@available(macOS 14, *)) {
if (customType == u"header") {
item = [NSMenuItem sectionHeaderWithTitle:label];
}
}
// If the menu item has an icon, set it.
ui::ImageModel icon = model->GetIconAt(index);
if (icon.IsImage())
item.image = icon.GetImage().ToNSImage();
@@ -378,15 +356,6 @@ NSMenuItemBadge* CreateBadge(const electron::ElectronMenuModel::Badge& badge)
std::u16string toolTip = model->GetToolTipAt(index);
item.toolTip = base::SysUTF16ToNSString(toolTip);
if (@available(macOS 14, *)) {
electron::ElectronMenuModel::Badge badge;
if (model->GetBadgeAt(index, &badge)) {
NSMenuItemBadge* nsBadge = CreateBadge(badge);
if (nsBadge)
item.badge = nsBadge;
}
}
if (role == u"services") {
std::u16string title = u"Services";
NSString* sub_label = l10n_util::FixUpWindowsStyleLabel(title);
@@ -548,15 +517,6 @@ NSMenuItemBadge* CreateBadge(const electron::ElectronMenuModel::Badge& badge)
} else {
item.image = nil;
}
if (@available(macOS 14, *)) {
electron::ElectronMenuModel::Badge badge;
if (model->GetBadgeAt(index, &badge)) {
item.badge = CreateBadge(badge);
} else {
item.badge = nil;
}
}
}
- (void)refreshMenuTree:(NSMenu*)menu {

View File

@@ -12,15 +12,6 @@ namespace electron {
ElectronMenuModel::SharingItem::SharingItem() = default;
ElectronMenuModel::SharingItem::SharingItem(SharingItem&&) = default;
ElectronMenuModel::SharingItem::~SharingItem() = default;
ElectronMenuModel::Badge::Badge() = default;
ElectronMenuModel::Badge::Badge(Badge&&) = default;
ElectronMenuModel::Badge::Badge(const Badge&) = default;
ElectronMenuModel::Badge& ElectronMenuModel::Badge::operator=(const Badge&) =
default;
ElectronMenuModel::Badge& ElectronMenuModel::Badge::operator=(Badge&&) =
default;
ElectronMenuModel::Badge::~Badge() = default;
#endif
bool ElectronMenuModel::Delegate::GetAcceleratorForCommandId(
@@ -124,21 +115,6 @@ bool ElectronMenuModel::GetSharingItemAt(size_t index,
void ElectronMenuModel::SetSharingItem(SharingItem item) {
sharing_item_.emplace(std::move(item));
}
void ElectronMenuModel::SetBadge(size_t index, Badge badge) {
int command_id = GetCommandIdAt(index);
badges_[command_id] = std::move(badge);
}
bool ElectronMenuModel::GetBadgeAt(size_t index, Badge* badge) const {
int command_id = GetCommandIdAt(index);
const auto iter = badges_.find(command_id);
if (iter != badges_.end()) {
*badge = iter->second;
return true;
}
return false;
}
#endif
void ElectronMenuModel::MenuWillClose() {

View File

@@ -33,19 +33,6 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
std::optional<std::vector<GURL>> urls;
std::optional<std::vector<base::FilePath>> file_paths;
};
struct Badge {
Badge();
Badge(Badge&&);
Badge(const Badge&);
Badge& operator=(const Badge&);
Badge& operator=(Badge&&);
~Badge();
std::u16string type; // "alerts", "updates", "new-items", or "none"
std::optional<int> count;
std::optional<std::string> content;
};
#endif
class Delegate : public ui::SimpleMenuModel::Delegate {
@@ -118,9 +105,6 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
return sharing_item_;
}
// Set/Get the Badge of a menu item.
void SetBadge(size_t index, Badge badge);
bool GetBadgeAt(size_t index, Badge* badge) const;
#endif
// ui::SimpleMenuModel:
@@ -139,7 +123,6 @@ class ElectronMenuModel : public ui::SimpleMenuModel {
#if BUILDFLAG(IS_MAC)
std::optional<SharingItem> sharing_item_;
base::flat_map<int, Badge> badges_; // command id -> badge
#endif
base::flat_map<int, std::u16string> toolTips_; // command id -> tooltip

View File

@@ -705,10 +705,8 @@ gin_helper::Handle<SimpleURLLoaderWrapper> SimpleURLLoaderWrapper::Create(
else // default session
session = Session::FromPartition(args->isolate(), "");
}
if (session) {
if (session)
browser_context = session->browser_context();
DCHECK(browser_context != nullptr);
}
}
auto ret = gin_helper::CreateHandle(

View File

@@ -4,7 +4,7 @@
#include "shell/common/gin_helper/callback.h"
#include "gin/dictionary.h"
#include "gin/arguments.h"
#include "gin/persistent.h"
#include "v8/include/cppgc/allocation.h"
#include "v8/include/v8-cppgc.h"
@@ -51,42 +51,28 @@ struct TranslatorHolder {
delete data.GetParameter();
}
static gin::DeprecatedWrapperInfo kWrapperInfo;
v8::Global<v8::External> handle;
Translator translator;
bool one_time = false;
bool called = false;
};
gin::DeprecatedWrapperInfo TranslatorHolder::kWrapperInfo = {
gin::kEmbedderNativeGin};
void CallTranslator(const v8::FunctionCallbackInfo<v8::Value>& info) {
gin::Arguments args(info);
auto* holder =
static_cast<TranslatorHolder*>(info.Data().As<v8::External>()->Value(
v8::kExternalPointerTypeTagDefault));
void CallTranslator(v8::Local<v8::External> external,
v8::Local<v8::Object> state,
gin::Arguments* args) {
// Whether the callback should only be called once.
v8::Isolate* isolate = args->isolate();
auto context = isolate->GetCurrentContext();
bool one_time =
state->Has(context, gin::StringToSymbol(isolate, "oneTime")).ToChecked();
// Check if the callback has already been called.
if (one_time) {
auto called_symbol = gin::StringToSymbol(isolate, "called");
if (state->Has(context, called_symbol).ToChecked()) {
args->ThrowTypeError("One-time callback was called more than once");
return;
} else {
state->Set(context, called_symbol, v8::True(isolate)).ToChecked();
}
if (holder->one_time && holder->called) {
args.ThrowTypeError("One-time callback was called more than once");
return;
}
holder->called = true;
auto* holder = static_cast<TranslatorHolder*>(
external->Value(v8::kExternalPointerTypeTagDefault));
holder->translator.Run(args);
holder->translator.Run(&args);
// Free immediately for one-time callback.
if (one_time)
delete holder;
if (holder->one_time)
holder->translator.Reset();
}
} // namespace
@@ -113,41 +99,11 @@ v8::Local<v8::Function> SafeV8Function::NewHandle(v8::Isolate* isolate) const {
v8::Local<v8::Value> CreateFunctionFromTranslator(v8::Isolate* isolate,
const Translator& translator,
bool one_time) {
gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
auto* wrapper_info = &TranslatorHolder::kWrapperInfo;
v8::Local<v8::FunctionTemplate> constructor =
data->DeprecatedGetFunctionTemplate(wrapper_info);
// The FunctionTemplate is cached.
if (constructor.IsEmpty()) {
constructor =
CreateFunctionTemplate(isolate, base::BindRepeating(&CallTranslator));
data->DeprecatedSetFunctionTemplate(wrapper_info, constructor);
}
auto* holder = new TranslatorHolder(isolate);
holder->translator = translator;
auto state = gin::Dictionary::CreateEmpty(isolate);
if (one_time)
state.Set("oneTime", true);
holder->one_time = one_time;
auto context = isolate->GetCurrentContext();
return BindFunctionWith(
isolate, context, constructor->GetFunction(context).ToLocalChecked(),
holder->handle.Get(isolate), gin::ConvertToV8(isolate, state));
}
// func.bind(func, arg1).
// NB(zcbenz): Using C++11 version crashes VS.
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
v8::Local<v8::Value> arg1,
v8::Local<v8::Value> arg2) {
v8::MaybeLocal<v8::Value> bind =
func->Get(context, gin::StringToV8(isolate, "bind"));
CHECK(!bind.IsEmpty());
v8::Local<v8::Function> bind_func = bind.ToLocalChecked().As<v8::Function>();
v8::Local<v8::Value> converted[] = {func, arg1, arg2};
return bind_func->Call(context, func, std::size(converted), converted)
return v8::Function::New(context, CallTranslator, holder->handle.Get(isolate))
.ToLocalChecked();
}

View File

@@ -118,11 +118,6 @@ using Translator = base::RepeatingCallback<void(gin::Arguments* args)>;
v8::Local<v8::Value> CreateFunctionFromTranslator(v8::Isolate* isolate,
const Translator& translator,
bool one_time);
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
v8::Local<v8::Value> arg1,
v8::Local<v8::Value> arg2);
// Calls callback with Arguments.
template <typename Sig>

View File

@@ -10,7 +10,7 @@
#include <vector>
#include "base/containers/span.h"
#include "base/memory/memory_pressure_listener_registry.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
@@ -810,7 +810,7 @@ class WebFrameRenderer final
void ClearCache(v8::Isolate* isolate) {
blink::WebCache::Clear();
base::MemoryPressureListenerRegistry::NotifyMemoryPressure(
base::MemoryPressureListener::NotifyMemoryPressure(
base::MEMORY_PRESSURE_LEVEL_CRITICAL);
}

View File

@@ -372,6 +372,33 @@ describe('contextBridge', () => {
expect(result).to.equal(123);
});
it('should proxy promises correctly when Function.prototype has been overridden in the main world', async () => {
await makeBindingWindow(() => {
contextBridge.exposeInMainWorld('example', {
getPromise: () => Promise.resolve('proxied-ok')
});
});
const result = await callWithBindings((root: any) => {
return new Promise(resolve => {
let observed = false;
const original = Function.prototype.bind;
// eslint-disable-next-line no-extend-native
Function.prototype.bind = new Proxy(original, {
apply (target, thisArg, args) {
observed = true;
return Reflect.apply(target, thisArg, args);
}
});
root.example.getPromise().then((v: string) => {
// eslint-disable-next-line no-extend-native
Function.prototype.bind = original;
resolve({ observed, value: v });
});
});
});
expect(result).to.deep.equal({ observed: false, value: 'proxied-ok' });
});
it('should proxy methods', async () => {
await makeBindingWindow(() => {
contextBridge.exposeInMainWorld('example', {

View File

@@ -1,6 +1,5 @@
import { expect } from 'chai';
import { once } from 'node:events';
import * as path from 'node:path';
import { ifdescribe, isTestingBindingAvailable, startRemoteControlApp } from './lib/spec-helpers';
@@ -40,33 +39,6 @@ describe('cpp heap', () => {
});
describe('session module', () => {
it('does not crash on exit with live session wrappers', async () => {
const rc = await startRemoteControlApp();
await rc.remotely(async () => {
const { app, session } = require('electron');
const sessions = [
session.defaultSession,
session.fromPartition('cppheap-exit'),
session.fromPartition('persist:cppheap-exit-persist')
];
// We want to test GC on shutdown, so add a global reference
// to these sessions to prevent pre-shutdown GC.
(globalThis as any).sessionRefs = sessions;
// We want to test CppGC-traced references during shutdown.
// The CppGC-managed cookies will do that; but since they're
// lazy-created, access them here to ensure they're live.
sessions.forEach(ses => ses.cookies);
setTimeout(() => app.quit());
});
const [code] = await once(rc.process, 'exit');
expect(code).to.equal(0);
});
it('should record as node in heap snapshot', async () => {
const { remotely } = await startRemoteControlApp(['--expose-internals']);
const result = await remotely(async (heap: string, snapshotHelper: string) => {

View File

@@ -166,7 +166,6 @@ declare namespace Electron {
commandsMap: Record<string, MenuItem>;
groupsMap: Record<string, MenuItem[]>;
getItemCount(): number;
getIndexOfCommandId(commandId: number): number;
popupAt(window: BaseWindow, frame: WebFrameMain | undefined, x: number, y: number, positioning: number, sourceType: Required<Electron.PopupOptions>['sourceType'], callback: () => void): void;
closePopupAt(id: number): void;
setSublabel(index: number, label: string): void;
@@ -174,7 +173,6 @@ declare namespace Electron {
setIcon(index: number, image: string | NativeImage): void;
setRole(index: number, role: string): void;
setCustomType(index: number, customType: string): void;
setBadge(index: number, badge: MenuItemBadge): void;
insertItem(index: number, commandId: number, label: string): void;
insertCheckItem(index: number, commandId: number, label: string): void;
insertRadioItem(index: number, commandId: number, label: string, groupId: number): void;