[gnome] Update extensions for version 48

This commit is contained in:
2025-03-29 17:52:56 -04:00
parent e01589e836
commit a84b79ca08
153 changed files with 3479 additions and 2189 deletions

View File

@ -172,7 +172,7 @@ export const Clipboard = GObject.registerClass({
invocation.return_value(retval);
// Without a response, the client will wait for timeout
} catch (e) {
} catch {
invocation.return_dbus_error(
'org.gnome.gjs.JSError.ValueError',
'Service implementation returned an incorrect value type'
@ -183,7 +183,7 @@ export const Clipboard = GObject.registerClass({
/**
* Get the available mimetypes of the current clipboard content
*
* @return {Promise<string[]>} A list of mime-types
* @returns {Promise<string[]>} A list of mime-types
*/
GetMimetypes() {
return new Promise((resolve, reject) => {
@ -202,7 +202,7 @@ export const Clipboard = GObject.registerClass({
/**
* Get the text content of the clipboard
*
* @return {Promise<string>} Text content of the clipboard
* @returns {Promise<string>} Text content of the clipboard
*/
GetText() {
return new Promise((resolve, reject) => {
@ -241,7 +241,7 @@ export const Clipboard = GObject.registerClass({
* Set the text content of the clipboard
*
* @param {string} text - text content to set
* @return {Promise} A promise for the operation
* @returns {Promise} A promise for the operation
*/
SetText(text) {
return new Promise((resolve, reject) => {
@ -270,7 +270,7 @@ export const Clipboard = GObject.registerClass({
* Get the content of the clipboard with the type @mimetype.
*
* @param {string} mimetype - the mimetype to request
* @return {Promise<Uint8Array>} The content of the clipboard
* @returns {Promise<Uint8Array>} The content of the clipboard
*/
GetValue(mimetype) {
return new Promise((resolve, reject) => {
@ -300,7 +300,7 @@ export const Clipboard = GObject.registerClass({
*
* @param {Uint8Array} value - the value to set
* @param {string} mimetype - the mimetype of the value
* @return {Promise} - A promise for the operation
* @returns {Promise} - A promise for the operation
*/
SetValue(value, mimetype) {
return new Promise((resolve, reject) => {
@ -377,4 +377,3 @@ export function unwatchService() {
_portalId = 0;
}
}

View File

@ -10,7 +10,7 @@ import St from 'gi://St';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
import {getIcon} from './utils.js';
import {HAS_ST_ORIENTATION, getIcon} from './utils.js';
import Tooltip from './tooltip.js';
@ -20,7 +20,7 @@ import Tooltip from './tooltip.js';
*
* @param {Gio.MenuModel} model - The menu model containing the item
* @param {number} index - The index of the item in @model
* @return {Object} A dictionary of the item's attributes
* @returns {object} A dictionary of the item's attributes
*/
function getItemInfo(model, index) {
const info = {
@ -92,12 +92,19 @@ export class ListBox extends PopupMenu.PopupMenuSection {
this.actor.add_child(this.box);
// Submenu Container
this.sub = new St.BoxLayout({
clip_to_allocation: true,
vertical: false,
visible: false,
x_expand: true,
});
this.sub = HAS_ST_ORIENTATION
? new St.BoxLayout({
clip_to_allocation: true,
orientation: Clutter.Orientation.HORIZONTAL, // GNOME 48
visible: false,
x_expand: true,
})
: new St.BoxLayout({
clip_to_allocation: true,
vertical: false, // GNOME 46/47
visible: false,
x_expand: true,
});
this.sub.set_pivot_point(1, 1);
this.sub._delegate = this;
this.actor.add_child(this.sub);
@ -464,24 +471,38 @@ export class IconBox extends PopupMenu.PopupMenuSection {
Object.assign(this, params);
// Main Actor
this.actor = new St.BoxLayout({
vertical: true,
x_expand: true,
});
this.actor = HAS_ST_ORIENTATION
? new St.BoxLayout({
orientation: Clutter.Orientation.VERTICAL, // GNOME 48
x_expand: true,
})
: new St.BoxLayout({
vertical: true, // GNOME 46/47
x_expand: true,
});
this.actor._delegate = this;
// Button Box
this.box._delegate = this;
this.box.style_class = 'gsconnect-icon-box';
this.box.vertical = false;
if (HAS_ST_ORIENTATION)
this.box.orientation = Clutter.Orientation.HORIZONTAL; // GNOME 48
else
this.box.vertical = false; // GNOME 46/47
this.actor.add_child(this.box);
// Submenu Container
this.sub = new St.BoxLayout({
clip_to_allocation: true,
vertical: true,
x_expand: true,
});
this.sub = HAS_ST_ORIENTATION
? new St.BoxLayout({
clip_to_allocation: true,
orientation: Clutter.Orientation.VERTICAL, // GNOME 48
x_expand: true,
})
: new St.BoxLayout({
clip_to_allocation: true,
vertical: true, // GNOME 46/47
x_expand: true,
});
this.sub.connect('transitions-completed', this._onTransitionsCompleted);
this.sub._delegate = this;
this.actor.add_child(this.sub);
@ -644,4 +665,3 @@ export class IconBox extends PopupMenu.PopupMenuSection {
this._onItemsChanged(this.model, 0, 0, this.model.get_n_items());
}
}

View File

@ -49,7 +49,7 @@ export class Manager {
*
* @param {string} accelerator - An accelerator in the form '<Control>q'
* @param {Function} callback - A callback for the accelerator
* @return {number} A non-zero action id on success, or 0 on failure
* @returns {*} A non-zero action id on success, or 0 on failure
*/
add(accelerator, callback) {
try {
@ -100,4 +100,3 @@ export class Manager {
this.removeAll();
}
}

View File

@ -9,11 +9,14 @@ import St from 'gi://St';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';
import * as Calendar from 'resource:///org/gnome/shell/ui/calendar.js';
import * as NotificationDaemon from 'resource:///org/gnome/shell/ui/notificationDaemon.js';
import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
import {getIcon} from './utils.js';
import {HAS_MESSAGELIST_NOTIFICATIONMESSAGE, getIcon} from './utils.js';
const {NotificationMessage} = HAS_MESSAGELIST_NOTIFICATIONMESSAGE
? await import('resource:///org/gnome/shell/ui/messageList.js') // GNOME 48
: await import('resource:///org/gnome/shell/ui/calendar.js'); // GNOME 46/47
const APP_ID = 'org.gnome.Shell.Extensions.GSConnect';
const APP_PATH = '/org/gnome/Shell/Extensions/GSConnect';
@ -29,6 +32,7 @@ const REPLY_REGEX = new RegExp(/^([^|]+)\|([\s\S]+)\|([0-9a-f]{8}-[0-9a-f]{4}-[1
/**
* Extracted from notificationDaemon.js, as it's no longer exported
* https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/notificationDaemon.js#L556
*
* @returns {{ 'desktop-startup-id': string }} Object with ID containing current time
*/
function getPlatformData() {
@ -45,7 +49,7 @@ const GtkNotificationDaemon = Main.notificationDaemon._gtkNotificationDaemon.con
*/
const NotificationBanner = GObject.registerClass({
GTypeName: 'GSConnectNotificationBanner',
}, class NotificationBanner extends Calendar.NotificationMessage {
}, class NotificationBanner extends NotificationMessage {
constructor(notification) {
super(notification);
@ -147,7 +151,7 @@ const NotificationBanner = GObject.registerClass({
(connection, res) => {
try {
connection.call_finish(res);
} catch (e) {
} catch {
// Silence errors
}
}
@ -198,7 +202,7 @@ const Source = GObject.registerClass({
(connection, res) => {
try {
connection.call_finish(res);
} catch (e) {
} catch {
// If we fail, reset in case we can try again
notification._remoteClosed = false;
}
@ -384,11 +388,17 @@ const _ensureAppSource = function (appId) {
};
/**
* Update the prototype for {@link GtkNotificationDaemon}.
*/
export function patchGtkNotificationDaemon() {
GtkNotificationDaemon.prototype._ensureAppSource = _ensureAppSource;
}
/**
* Restore the prototype for {@link GtkNotificationDaemon}.
*/
export function unpatchGtkNotificationDaemon() {
GtkNotificationDaemon.prototype._ensureAppSource = __ensureAppSource;
}
@ -399,6 +409,9 @@ export function unpatchGtkNotificationDaemon() {
*/
const _addNotification = NotificationDaemon.GtkNotificationDaemonAppSource.prototype.addNotification;
/**
* Update the prototype for {@link NotificationDaemon.GtkNotificationDaemonAppSource}.
*/
export function patchGtkNotificationSources() {
// eslint-disable-next-line func-style
const _withdrawGSConnectNotification = function (id, notification, reason) {
@ -433,7 +446,7 @@ export function patchGtkNotificationSources() {
(connection, res) => {
try {
connection.call_finish(res);
} catch (e) {
} catch {
// If we fail, reset in case we can try again
notification._remoteWithdrawn = false;
}
@ -445,8 +458,10 @@ export function patchGtkNotificationSources() {
}
/**
* Restore the prototype for {@link NotificationDaemon.GtkNotificationDaemonAppSource}.
*/
export function unpatchGtkNotificationSources() {
NotificationDaemon.GtkNotificationDaemonAppSource.prototype.addNotification = _addNotification;
delete NotificationDaemon.GtkNotificationDaemonAppSource.prototype._withdrawGSConnectNotification;
}

View File

@ -10,6 +10,7 @@ import St from 'gi://St';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import {HAS_ST_ORIENTATION} from './utils.js';
/**
* An StTooltip for ClutterActors
@ -151,7 +152,15 @@ export default class Tooltip {
if (this.custom) {
this._bin.child = this.custom;
} else {
this._bin.child = new St.BoxLayout({vertical: false});
if (HAS_ST_ORIENTATION) {
// GNOME 48
this._bin.child = new St.BoxLayout(
{orientation: Clutter.Orientation.HORIZONTAL}
);
} else {
// GNOME 46/47
this._bin.child = new St.BoxLayout({vertical: false});
}
if (this.gicon) {
this._bin.child.icon = new St.Icon({

View File

@ -3,22 +3,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Gtk from 'gi://Gtk';
import St from 'gi://St';
import Config from '../config.js';
import {PACKAGE_VERSION} from 'resource:///org/gnome/shell/misc/config.js';
let St = null; // St is not available for prefs.js importing this file.
try {
St = (await import('gi://St')).default;
} catch (e) { }
export const SHELL_MAJOR_VERSION = Number(PACKAGE_VERSION.split('.')[0]);
export const HAS_ST_ORIENTATION = SHELL_MAJOR_VERSION >= 48;
export const HAS_MESSAGELIST_NOTIFICATIONMESSAGE = SHELL_MAJOR_VERSION >= 48;
/**
* Get a themed icon, using fallbacks from GSConnect's GResource when necessary.
*
* @param {string} name - A themed icon name
* @return {Gio.Icon} A themed icon
* @returns {Gio.Icon} A themed icon
*/
export function getIcon(name) {
if (getIcon._resource === undefined) {
@ -64,220 +64,3 @@ export function getIcon(name) {
// Fallback to hoping it's in the theme somewhere
return new Gio.ThemedIcon({name: name});
}
/**
* Get the contents of a GResource file, replacing `@PACKAGE_DATADIR@` where
* necessary.
*
* @param {string} relativePath - A path relative to GSConnect's resource path
* @return {string} The file contents as a string
*/
function getResource(relativePath) {
try {
const bytes = Gio.resources_lookup_data(
GLib.build_filenamev([Config.APP_PATH, relativePath]),
Gio.ResourceLookupFlags.NONE
);
const source = new TextDecoder().decode(bytes.toArray());
return source.replace('@PACKAGE_DATADIR@', Config.PACKAGE_DATADIR);
} catch (e) {
logError(e, 'GSConnect');
return null;
}
}
/**
* Install file contents, to an absolute directory path.
*
* @param {string} dirname - An absolute directory path
* @param {string} basename - The file name
* @param {string} contents - The file contents
* @return {boolean} A success boolean
*/
function _installFile(dirname, basename, contents) {
try {
const filename = GLib.build_filenamev([dirname, basename]);
GLib.mkdir_with_parents(dirname, 0o755);
return GLib.file_set_contents(filename, contents);
} catch (e) {
logError(e, 'GSConnect');
return false;
}
}
/**
* Install file contents from a GResource, to an absolute directory path.
*
* @param {string} dirname - An absolute directory path
* @param {string} basename - The file name
* @param {string} relativePath - A path relative to GSConnect's resource path
* @return {boolean} A success boolean
*/
function _installResource(dirname, basename, relativePath) {
try {
const contents = getResource(relativePath);
return _installFile(dirname, basename, contents);
} catch (e) {
logError(e, 'GSConnect');
return false;
}
}
/**
* Use Gio.File to ensure a file's executable bits are set.
*
* @param {string} filepath - An absolute path to a file
* @returns {boolean} - True if the file already was, or is now, executable
*/
function _setExecutable(filepath) {
try {
const file = Gio.File.new_for_path(filepath);
const finfo = file.query_info(
`${Gio.FILE_ATTRIBUTE_STANDARD_TYPE},${Gio.FILE_ATTRIBUTE_UNIX_MODE}`,
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
null);
if (!finfo.has_attribute(Gio.FILE_ATTRIBUTE_UNIX_MODE))
return false;
const mode = finfo.get_attribute_uint32(
Gio.FILE_ATTRIBUTE_UNIX_MODE);
const new_mode = (mode | 0o111);
if (mode === new_mode)
return true;
return file.set_attribute_uint32(
Gio.FILE_ATTRIBUTE_UNIX_MODE,
new_mode,
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
null);
} catch (e) {
logError(e, 'GSConnect');
return false;
}
}
/**
* Ensure critical files in the extension directory have the
* correct permissions.
*/
export function ensurePermissions() {
if (Config.IS_USER) {
const executableFiles = [
'gsconnect-preferences',
'service/daemon.js',
'service/nativeMessagingHost.js',
];
for (const file of executableFiles)
_setExecutable(GLib.build_filenamev([Config.PACKAGE_DATADIR, file]));
}
}
/**
* Install the files necessary for the GSConnect service to run.
*/
export function installService() {
const settings = new Gio.Settings({
settings_schema: Config.GSCHEMA.lookup(
'org.gnome.Shell.Extensions.GSConnect',
null
),
path: '/org/gnome/shell/extensions/gsconnect/',
});
const confDir = GLib.get_user_config_dir();
const dataDir = GLib.get_user_data_dir();
const homeDir = GLib.get_home_dir();
// DBus Service
const dbusDir = GLib.build_filenamev([dataDir, 'dbus-1', 'services']);
const dbusFile = `${Config.APP_ID}.service`;
// Desktop Entry
const appDir = GLib.build_filenamev([dataDir, 'applications']);
const appFile = `${Config.APP_ID}.desktop`;
const appPrefsFile = `${Config.APP_ID}.Preferences.desktop`;
// Application Icon
const iconDir = GLib.build_filenamev([dataDir, 'icons', 'hicolor', 'scalable', 'apps']);
const iconFull = `${Config.APP_ID}.svg`;
const iconSym = `${Config.APP_ID}-symbolic.svg`;
// File Manager Extensions
const fileManagers = [
[`${dataDir}/nautilus-python/extensions`, 'nautilus-gsconnect.py'],
[`${dataDir}/nemo-python/extensions`, 'nemo-gsconnect.py'],
];
// WebExtension Manifests
const manifestFile = 'org.gnome.shell.extensions.gsconnect.json';
const google = getResource(`webextension/${manifestFile}.google.in`);
const mozilla = getResource(`webextension/${manifestFile}.mozilla.in`);
const manifests = [
[`${confDir}/chromium/NativeMessagingHosts/`, google],
[`${confDir}/google-chrome/NativeMessagingHosts/`, google],
[`${confDir}/google-chrome-beta/NativeMessagingHosts/`, google],
[`${confDir}/google-chrome-unstable/NativeMessagingHosts/`, google],
[`${confDir}/BraveSoftware/Brave-Browser/NativeMessagingHosts/`, google],
[`${confDir}/BraveSoftware/Brave-Browser-Beta/NativeMessagingHosts/`, google],
[`${confDir}/BraveSoftware/Brave-Browser-Nightly/NativeMessagingHosts/`, google],
[`${homeDir}/.mozilla/native-messaging-hosts/`, mozilla],
[`${homeDir}/.config/microsoft-edge-dev/NativeMessagingHosts`, google],
[`${homeDir}/.config/microsoft-edge-beta/NativeMessagingHosts`, google],
];
// If running as a user extension, ensure the DBus service, desktop entry,
// file manager scripts, and WebExtension manifests are installed.
if (Config.IS_USER) {
// DBus Service
if (!_installResource(dbusDir, dbusFile, `${dbusFile}.in`))
throw Error('GSConnect: Failed to install DBus Service');
// Desktop Entries
_installResource(appDir, appFile, appFile);
_installResource(appDir, appPrefsFile, appPrefsFile);
// Application Icon
_installResource(iconDir, iconFull, `icons/${iconFull}`);
_installResource(iconDir, iconSym, `icons/${iconSym}`);
// File Manager Extensions
const target = `${Config.PACKAGE_DATADIR}/nautilus-gsconnect.py`;
for (const [dir, name] of fileManagers) {
const script = Gio.File.new_for_path(GLib.build_filenamev([dir, name]));
if (!script.query_exists(null)) {
GLib.mkdir_with_parents(dir, 0o755);
script.make_symbolic_link(target, null);
}
}
// WebExtension Manifests
if (settings.get_boolean('create-native-messaging-hosts')) {
for (const [dirname, contents] of manifests)
_installFile(dirname, manifestFile, contents);
}
// Otherwise, if running as a system extension, ensure anything previously
// installed when running as a user extension is removed.
} else {
GLib.unlink(GLib.build_filenamev([dbusDir, dbusFile]));
GLib.unlink(GLib.build_filenamev([appDir, appFile]));
GLib.unlink(GLib.build_filenamev([appDir, appPrefsFile]));
GLib.unlink(GLib.build_filenamev([iconDir, iconFull]));
GLib.unlink(GLib.build_filenamev([iconDir, iconSym]));
for (const [dir, name] of fileManagers)
GLib.unlink(GLib.build_filenamev([dir, name]));
for (const manifest of manifests)
GLib.unlink(GLib.build_filenamev([manifest[0], manifestFile]));
}
}