Compare commits

...

6 Commits

Author SHA1 Message Date
John Beutner
2051696e1d fix: ensure WebContents::WasShown runs when window is shown
Avoids a freeze when failing to enter fullscreen on macOS.
2026-02-24 10:01:51 -05:00
Mitchell Cohen
acd01e15e2 fix: prevent crash on Windows when closing child windows (#49901)
* guard against window destruction in min/max size checks

* use weakptr to prevent hit test crash on teardown

* revove web contents views during teardown

* fix test failure

* fix other tests
2026-02-24 15:48:04 +01:00
David Sanders
fc3a0abf19 build: fix roller branch detection in CI (#49920) 2026-02-23 18:10:22 -08:00
David Sanders
fa2b9ac466 ci: fix checking latest release for website docs update (#49918) 2026-02-23 15:22:01 -08:00
Andre Hora
481e224992 docs: remove documentation link to discontinued Touch Bar Simulator (#49899)
Remove link to discontinued Touch Bar Simulator

https://github.com/sindresorhus/touch-bar-simulator is archived.

> This app is discontinued as it no longer works because of changes in macOS.
2026-02-23 15:04:31 -05:00
Shashwat Raj
90c9de70ac fix: updated Alt detection to explicitly exclude AltGraph/AltGr (#49778)
Updated Alt detection to explicitly exclude AltGraph/AltGr
2026-02-23 13:00:27 -05:00
12 changed files with 87 additions and 33 deletions

View File

@@ -31,7 +31,7 @@ jobs:
echo "isLatestRelease=false" >> $GITHUB_OUTPUT
fi
- name: Trigger website docs update
if: ${{ steps.check-if-latest-release.outputs.isLatestRelease }}
if: ${{ steps.check-if-latest-release.outputs.isLatestRelease == 'true' }}
env:
GH_REPO: electron/website
GH_TOKEN: ${{ fromJSON(steps.secret-service.outputs.secrets).WEBSITE_DOCS_UPDATER_APP_TOKEN }}

View File

@@ -23,11 +23,6 @@ Creates a new touch bar with the specified items. Use
> The TouchBar API is currently experimental and may change or be
> removed in future Electron releases.
> [!TIP]
> If you don't have a MacBook with Touch Bar, you can use
> [Touch Bar Simulator](https://github.com/sindresorhus/touch-bar-simulator)
> to test Touch Bar usage in your app.
### Static Properties
#### `TouchBarButton`

View File

@@ -14,6 +14,23 @@ const DEPS_REGEX = /chromium_version':\n +'(.+?)',/m;
const CL_REGEX = /https:\/\/chromium-review\.googlesource\.com\/c\/(?:chromium\/src|v8\/v8)\/\+\/(\d+)(#\S+)?/g;
const ROLLER_BRANCH_PATTERN = /^roller\/chromium\/(.+)$/;
function getCurrentBranch () {
// In CI, use `GITHUB_HEAD_REF` since we checkout the PR branch in detached HEAD state
if (process.env.GITHUB_HEAD_REF) {
return process.env.GITHUB_HEAD_REF;
}
try {
return execSync('git rev-parse --abbrev-ref HEAD', {
cwd: ELECTRON_DIR,
encoding: 'utf8'
}).trim();
} catch {
console.error('Could not determine current git branch');
process.exit(1);
}
}
function getCommitsSinceMergeBase (mergeBase) {
try {
const output = execSync(`git log --format=%H%n%B%n---COMMIT_END--- ${mergeBase}..HEAD`, {
@@ -92,17 +109,7 @@ async function getGerritPatchDetails (clUrl) {
}
async function main () {
let currentBranch;
try {
currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
cwd: ELECTRON_DIR,
encoding: 'utf8'
}).trim();
} catch {
console.error('Could not determine current git branch');
process.exit(1);
}
const currentBranch = getCurrentBranch();
// Check if we're on a roller/chromium/* branch
const branchMatch = ROLLER_BRANCH_PATTERN.exec(currentBranch);

View File

@@ -280,16 +280,22 @@ v8::Local<v8::Value> BrowserWindow::GetWebContents(v8::Isolate* isolate) {
}
void BrowserWindow::OnWindowShow() {
if (!web_contents_shown_) {
web_contents()->WasShown();
web_contents_shown_ = true;
}
BaseWindow::OnWindowShow();
}
void BrowserWindow::OnWindowHide() {
web_contents()->WasOccluded();
web_contents_shown_ = false;
BaseWindow::OnWindowHide();
}
void BrowserWindow::Show() {
web_contents()->WasShown();
web_contents_shown_ = true;
BaseWindow::Show();
}
@@ -298,6 +304,7 @@ void BrowserWindow::ShowInactive() {
if (IsModal())
return;
web_contents()->WasShown();
web_contents_shown_ = true;
BaseWindow::ShowInactive();
}

View File

@@ -80,6 +80,7 @@ class BrowserWindow : public BaseWindow,
// Helpers.
v8::Global<v8::Value> web_contents_;
bool web_contents_shown_ = false;
v8::Global<v8::Value> web_contents_view_;
base::WeakPtr<api::WebContents> api_web_contents_;

View File

@@ -33,7 +33,7 @@ WebContentsView::WebContentsView(v8::Isolate* isolate,
gin_helper::Handle<WebContents> web_contents)
: View(web_contents->inspectable_web_contents()->GetView()),
web_contents_(isolate, web_contents.ToV8()),
api_web_contents_(web_contents.get()) {
api_web_contents_(web_contents->GetWeakPtr()) {
set_delete_view(false);
view()->SetProperty(
views::kFlexBehaviorKey,

View File

@@ -7,7 +7,7 @@
#include <optional>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "content/public/browser/web_contents_observer.h"
#include "shell/browser/api/electron_api_view.h"
#include "shell/browser/draggable_region_provider.h"
@@ -63,7 +63,7 @@ class WebContentsView : public View,
// Keep a reference to v8 wrapper.
v8::Global<v8::Value> web_contents_;
raw_ptr<api::WebContents> api_web_contents_;
base::WeakPtr<api::WebContents> api_web_contents_;
};
} // namespace electron::api

View File

@@ -92,6 +92,13 @@ InspectableWebContentsView::InspectableWebContentsView(
}
InspectableWebContentsView::~InspectableWebContentsView() {
if (devtools_window_web_view_)
devtools_window_web_view_->SetWebContents(nullptr);
if (devtools_web_view_)
devtools_web_view_->SetWebContents(nullptr);
if (contents_web_view_)
contents_web_view_->SetWebContents(nullptr);
if (devtools_window_)
inspectable_web_contents()->SaveDevToolsBounds(
devtools_window_->GetWindowBoundsInScreen());

View File

@@ -9,6 +9,7 @@
#include "components/input/native_web_keyboard_event.h"
#include "shell/browser/native_window.h"
#include "shell/browser/ui/views/menu_bar.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/views/layout/box_layout.h"
namespace electron {
@@ -21,9 +22,21 @@ bool IsAltKey(const input::NativeWebKeyboardEvent& event) {
bool IsAltModifier(const input::NativeWebKeyboardEvent& event) {
using Mods = input::NativeWebKeyboardEvent::Modifiers;
// AltGraph (AltGr) should not be treated as a single Alt keypress for
// menu-bar toggling.
if (event.windows_key_code == ui::VKEY_ALTGR ||
ui::KeycodeConverter::DomKeyToKeyString(event.dom_key) == "AltGraph") {
return false;
}
return (event.GetModifiers() & Mods::kKeyModifiers) == Mods::kAltKey;
}
bool IsSingleAltKey(const input::NativeWebKeyboardEvent& event) {
return IsAltKey(event) && IsAltModifier(event);
}
} // namespace
RootView::RootView(NativeWindow* window)
@@ -98,7 +111,7 @@ void RootView::HandleKeyEvent(const input::NativeWebKeyboardEvent& event) {
return;
// Show accelerator when "Alt" is pressed.
if (menu_bar_visible_ && IsAltKey(event))
if (menu_bar_visible_ && IsSingleAltKey(event))
menu_bar_->SetAcceleratorVisibility(
event.GetType() == blink::WebInputEvent::Type::kRawKeyDown);
@@ -121,11 +134,11 @@ void RootView::HandleKeyEvent(const input::NativeWebKeyboardEvent& event) {
// Toggle the menu bar only when a single Alt is released.
if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown &&
IsAltKey(event)) {
IsSingleAltKey(event)) {
// When a single Alt is pressed:
menu_bar_alt_pressed_ = true;
} else if (event.GetType() == blink::WebInputEvent::Type::kKeyUp &&
IsAltKey(event) && menu_bar_alt_pressed_) {
IsSingleAltKey(event) && menu_bar_alt_pressed_) {
// When a single Alt is released right after a Alt is pressed:
menu_bar_alt_pressed_ = false;
if (menu_bar_autohide_)

View File

@@ -275,12 +275,16 @@ bool WinFrameView::GetShouldPaintAsActive() {
}
gfx::Size WinFrameView::GetMinimumSize() const {
if (!window_)
return gfx::Size();
// Chromium expects minimum size to be in content dimensions on Windows
// because it adds the frame border automatically in OnGetMinMaxInfo.
return window_->GetContentMinimumSize();
}
gfx::Size WinFrameView::GetMaximumSize() const {
if (!window_)
return gfx::Size();
// Chromium expects minimum size to be in content dimensions on Windows
// because it adds the frame border automatically in OnGetMinMaxInfo.
gfx::Size size = window_->GetContentMaximumSize();

View File

@@ -5925,6 +5925,23 @@ describe('BrowserWindow module', () => {
});
});
ifdescribe(process.platform === 'linux')('menu bar AltGr behavior', () => {
it('does not toggle auto-hide menu bar visibility', async () => {
const w = new BrowserWindow({ show: false, autoHideMenuBar: true });
w.setMenuBarVisibility(false);
expect(w.isMenuBarVisible()).to.be.false('isMenuBarVisible');
w.show();
await once(w, 'show');
w.webContents.focus();
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'AltGr' });
w.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'AltGr' });
await setTimeout();
expect(w.isMenuBarVisible()).to.be.false('isMenuBarVisible');
});
});
ifdescribe(process.platform !== 'darwin')('when fullscreen state is changed', () => {
it('correctly remembers state prior to fullscreen change', async () => {
const w = new BrowserWindow({ show: false });

View File

@@ -59,10 +59,11 @@ describe('WebContentsView', () => {
const browserWindow = new BrowserWindow();
const webContentsView = new WebContentsView();
webContentsView.webContents.loadURL('about:blank');
webContentsView.webContents.destroy();
const wc = webContentsView.webContents;
wc.loadURL('about:blank');
wc.destroy();
const destroyed = once(webContentsView.webContents, 'destroyed');
const destroyed = once(wc, 'destroyed');
await destroyed;
expect(() => browserWindow.contentView.addChildView(webContentsView)).to.throw(
'Can\'t add a destroyed child view to a parent view'
@@ -90,13 +91,14 @@ describe('WebContentsView', () => {
const w = new BaseWindow({ show: false });
const v = new View();
const wcv = new WebContentsView();
const wc = wcv.webContents;
w.setContentView(v);
v.addChildView(wcv);
await wcv.webContents.loadURL('about:blank');
const destroyed = once(wcv.webContents, 'destroyed');
wcv.webContents.executeJavaScript('window.close()');
await wc.loadURL('about:blank');
const destroyed = once(wc, 'destroyed');
wc.executeJavaScript('window.close()');
await destroyed;
expect(wcv.webContents.isDestroyed()).to.be.true();
expect(wc.isDestroyed()).to.be.true();
v.removeChildView(wcv);
});
@@ -170,18 +172,19 @@ describe('WebContentsView', () => {
it('does not crash when closed via window.close()', async () => {
const bw = new BrowserWindow();
const wcv = new WebContentsView();
const wc = wcv.webContents;
await bw.loadURL('data:text/html,<h1>Main Window</h1>');
bw.contentView.addChildView(wcv);
const dto = new Promise<boolean>((resolve) => {
wcv.webContents.on('blur', () => {
const devToolsOpen = wcv.webContents.isDevToolsOpened();
wc.on('blur', () => {
const devToolsOpen = !wc.isDestroyed() && wc.isDevToolsOpened();
resolve(devToolsOpen);
});
});
wcv.webContents.loadURL('data:text/html,<script>window.close()</script>');
wc.loadURL('data:text/html,<script>window.close()</script>');
const open = await dto;
expect(open).to.be.false();