mirror of
https://github.com/electron/electron.git
synced 2026-10-28 11:44:46 +08:00
feat: add app.setActivationToken() on Linux (#54989)
* feat: add app.setActivationToken() on Linux A Wayland compositor only raises a window when the request carries an XDG activation token. Electron forwards the tokens it receives at launch and from its own notifications, but an app that gets one another way, for example from a notification it sent over D-Bus itself, had no way to hand it over. The new method sets the token that the next window activation uses. * fix: document the X11 behavior of app.setActivationToken() and harden its spec X11 uses the same token as the startup notification ID of the next window that is created or activated, so the method is not a no-op there. The spec now sets the token before its window is shown, reads the activation request as it appears and fails if the child exits instead of skipping. * docs: say what the token is on X11 in the parameter description
This commit is contained in:
1 parent
c80c71c7c6
commit
3b24d8f7f6
6 files changed
+81
No files matched your search
@@ -836,6 +836,34 @@ identity — packaged apps should always set it explicitly.
|
||||
|
||||
This API must be called before the `ready` event. The value can also be set using `desktopName` in `package.json`.
|
||||
|
||||
### `app.setActivationToken(token)` _Linux_
|
||||
|
||||
<!--
|
||||
```YAML history
|
||||
added:
|
||||
- pr-url: https://github.com/electron/electron/pull/54989
|
||||
```
|
||||
-->
|
||||
|
||||
* `token` string - An [XDG activation](https://wayland.app/protocols/xdg-activation-v1) token, or a
|
||||
[startup notification](https://specifications.freedesktop.org/startup-notification-spec/latest/) ID on X11.
|
||||
|
||||
Sets the token that the next window activation uses, for example
|
||||
[`win.focus()`](base-window.md#winfocus) or [`win.show()`](base-window.md#winshow).
|
||||
|
||||
A Wayland compositor only lets an app raise and focus one of its windows when
|
||||
the request carries a token that proves the user asked for it. Electron passes
|
||||
such tokens along by itself when the app is launched or when one of its
|
||||
[`Notification`](notification.md)s is clicked. Use this method when the app gets
|
||||
a token some other way, for example from a notification it sent over D-Bus
|
||||
itself or from another process, and wants the window it activates next to come
|
||||
to the front. The token is used once. Set it before the window is created or
|
||||
shown, or once the window is on screen: a token that is set while a new window
|
||||
is still being shown may not be used.
|
||||
|
||||
On X11 the token is set as the `_NET_STARTUP_ID` of the next window that is
|
||||
created or activated, which is where window managers read it.
|
||||
|
||||
### `app.getLocale()`
|
||||
|
||||
Returns `string` - The current application locale, fetched using Chromium's `l10n_util` library.
|
||||
|
||||
@@ -1038,6 +1038,12 @@ void App::SetDesktopName(const std::string& desktop_name) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void App::SetActivationToken(const std::string& token) {
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
base::nix::SetActivationToken(token);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string App::GetLocale() {
|
||||
return g_browser_process->GetApplicationLocale();
|
||||
}
|
||||
@@ -2175,6 +2181,7 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
|
||||
.SetMethod("getPath", &App::GetPath)
|
||||
.SetMethod("setAppLogsPath", &App::SetAppLogsPath)
|
||||
.SetMethod("setDesktopName", &App::SetDesktopName)
|
||||
.SetMethod("setActivationToken", &App::SetActivationToken)
|
||||
.SetMethod("getLocale", &App::GetLocale)
|
||||
.SetMethod("getPreferredSystemLanguages", &GetPreferredLanguages)
|
||||
.SetMethod("getSystemLocale", &App::GetSystemLocale)
|
||||
|
||||
@@ -211,6 +211,7 @@ class App final : public gin::Wrappable<App>,
|
||||
const base::FilePath& path);
|
||||
|
||||
void SetDesktopName(const std::string& desktop_name);
|
||||
void SetActivationToken(const std::string& token);
|
||||
std::string GetLocale();
|
||||
std::string GetLocaleCountryCode();
|
||||
std::string GetSystemLocale(gin_helper::ErrorThrower thrower) const;
|
||||
|
||||
@@ -2063,6 +2063,38 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'linux')('app.setActivationToken(token)', () => {
|
||||
it('throws when the token is not a string', () => {
|
||||
expect(() => app.setActivationToken(1 as any)).toThrow(/Error processing argument at index 0/);
|
||||
});
|
||||
|
||||
ifit(isWayland)('activates the next window with the token', async (ctx) => {
|
||||
const child = cp.spawn(
|
||||
process.execPath,
|
||||
[path.join(fixturesPath, 'api', 'activation-token'), '--ozone-platform=wayland'],
|
||||
{ env: { ...process.env, WAYLAND_DEBUG: '1' }, stdio: ['ignore', 'ignore', 'pipe'] }
|
||||
);
|
||||
defer(() => {
|
||||
if (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL');
|
||||
});
|
||||
let protocolLog = '';
|
||||
child.stderr.on('data', (chunk) => {
|
||||
protocolLog += chunk;
|
||||
});
|
||||
const hasActivationProtocol = () => protocolLog.includes('"xdg_activation_v1"');
|
||||
await waitUntil(() => {
|
||||
expect(child.exitCode ?? child.signalCode, protocolLog).toBeNull();
|
||||
return (
|
||||
/xdg_activation_v1[#@]\d+\.activate\(/.test(protocolLog) ||
|
||||
(/xdg_surface[#@]\d+\.ack_configure\(/.test(protocolLog) && !hasActivationProtocol())
|
||||
);
|
||||
});
|
||||
|
||||
if (!hasActivationProtocol()) ctx.skip();
|
||||
expect(protocolLog).toMatch(/xdg_activation_v1[#@]\d+\.activate\("electron-test-token"/);
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'linux')('display server selection', () => {
|
||||
let runtimeDir: string;
|
||||
beforeEach(() => {
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
|
||||
// Stays open. The spec reads the window's activation request from the Wayland
|
||||
// protocol log and then ends this process.
|
||||
app.whenReady().then(() => {
|
||||
app.setActivationToken('electron-test-token');
|
||||
const w = new BrowserWindow({ width: 300, height: 200 });
|
||||
w.once('closed', () => app.quit());
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "electron-test-ozone-platform",
|
||||
"main": "main.js"
|
||||
}
|
||||
Reference in new issue
Block a user