Bug 1883184 - Initial pass at unity menubar support r=emilio

This implements the basic infrastructure to use use libdbusmenu to
export the Linux menubar.

For now, this is not integrated with the front-end, and there are some
remaining bugs, so it lands disabled by default behind the
widget.gtk.global-menu.enabled pref.

Differential Revision: https://phabricator.services.mozilla.com/D200259
This commit is contained in:
Björn Bidar 2024-03-02 11:38:02 +00:00
Родитель 37de9ca93a
Коммит 182fe12e8b
18 изменённых файлов: 1282 добавлений и 55 удалений

Просмотреть файл

@ -15905,6 +15905,14 @@
mirror: always
#endif
# Whether native GTK global menubar support is enabled.
# Disabled because there are some minor bugs and it needs deeper integration
# with the front-end.
- name: widget.gtk.global-menu.enabled
type: RelaxedAtomicBool
value: false
mirror: always
# Whether native GTK context menus are enabled.
# Disabled because at the very least there's missing custom icon support.
- name: widget.gtk.native-context-menus

67
widget/gtk/DBusMenu.cpp Normal file
Просмотреть файл

@ -0,0 +1,67 @@
/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DBusMenu.h"
#include "prlink.h"
#include "nsThreadUtils.h"
#include "mozilla/ArrayUtils.h"
namespace mozilla::widget {
#define FUNC(name, type, params) \
DBusMenuFunctions::_##name##_fn DBusMenuFunctions::s_##name;
DBUSMENU_GLIB_FUNCTIONS
DBUSMENU_GTK_FUNCTIONS
#undef FUNC
static PRLibrary* gDbusmenuGlib = nullptr;
static PRLibrary* gDbusmenuGtk = nullptr;
using DBusMenuFunc = void (*)();
struct DBusMenuDynamicFunction {
const char* functionName;
DBusMenuFunc* function;
};
static bool sInitialized;
static bool sLibPresent;
/* static */ bool DBusMenuFunctions::Init() {
MOZ_ASSERT(NS_IsMainThread());
if (sInitialized) {
return sLibPresent;
}
sInitialized = true;
#define FUNC(name, type, params) \
{#name, (DBusMenuFunc*)&DBusMenuFunctions::s_##name},
static const DBusMenuDynamicFunction kDbusmenuGlibSymbols[] = {
DBUSMENU_GLIB_FUNCTIONS};
static const DBusMenuDynamicFunction kDbusmenuGtkSymbols[] = {
DBUSMENU_GTK_FUNCTIONS};
#define LOAD_LIBRARY(symbol, name) \
if (!g##symbol) { \
g##symbol = PR_LoadLibrary(name); \
if (!g##symbol) { \
return false; \
} \
} \
for (uint32_t i = 0; i < mozilla::ArrayLength(k##symbol##Symbols); ++i) { \
*k##symbol##Symbols[i].function = \
PR_FindFunctionSymbol(g##symbol, k##symbol##Symbols[i].functionName); \
if (!*k##symbol##Symbols[i].function) { \
return false; \
} \
}
LOAD_LIBRARY(DbusmenuGlib, "libdbusmenu-glib.so.4")
LOAD_LIBRARY(DbusmenuGtk, "libdbusmenu-gtk3.so.4")
#undef LOAD_LIBRARY
sLibPresent = true;
return true;
}
} // namespace mozilla::widget

137
widget/gtk/DBusMenu.h Normal file
Просмотреть файл

@ -0,0 +1,137 @@
/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_widget_DBusMenu_h
#define mozilla_widget_DBusMenu_h
#include <glib.h>
#include <gdk/gdk.h>
namespace mozilla {
namespace dom {
class Element;
}
namespace widget {
#define DBUSMENU_GLIB_FUNCTIONS \
FUNC(dbusmenu_menuitem_child_add_position, gboolean, \
(DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position)) \
FUNC(dbusmenu_menuitem_set_root, void, \
(DbusmenuMenuitem * mi, gboolean root)) \
FUNC(dbusmenu_menuitem_child_append, gboolean, \
(DbusmenuMenuitem * mi, DbusmenuMenuitem * child)) \
FUNC(dbusmenu_menuitem_child_delete, gboolean, \
(DbusmenuMenuitem * mi, DbusmenuMenuitem * child)) \
FUNC(dbusmenu_menuitem_get_children, GList*, (DbusmenuMenuitem * mi)) \
FUNC(dbusmenu_menuitem_new, DbusmenuMenuitem*, (void)) \
FUNC(dbusmenu_menuitem_property_get, const gchar*, \
(DbusmenuMenuitem * mi, const gchar* property)) \
FUNC(dbusmenu_menuitem_property_get_bool, gboolean, \
(DbusmenuMenuitem * mi, const gchar* property)) \
FUNC(dbusmenu_menuitem_property_remove, void, \
(DbusmenuMenuitem * mi, const gchar* property)) \
FUNC(dbusmenu_menuitem_property_set, gboolean, \
(DbusmenuMenuitem * mi, const gchar* property, const gchar* value)) \
FUNC(dbusmenu_menuitem_property_set_bool, gboolean, \
(DbusmenuMenuitem * mi, const gchar* property, const gboolean value)) \
FUNC(dbusmenu_menuitem_property_set_int, gboolean, \
(DbusmenuMenuitem * mi, const gchar* property, const gint value)) \
FUNC(dbusmenu_menuitem_show_to_user, void, \
(DbusmenuMenuitem * mi, guint timestamp)) \
FUNC(dbusmenu_menuitem_take_children, GList*, (DbusmenuMenuitem * mi)) \
FUNC(dbusmenu_server_new, DbusmenuServer*, (const gchar* object)) \
FUNC(dbusmenu_server_set_root, void, \
(DbusmenuServer * server, DbusmenuMenuitem * root)) \
FUNC(dbusmenu_server_set_status, void, \
(DbusmenuServer * server, DbusmenuStatus status))
#define DBUSMENU_GTK_FUNCTIONS \
FUNC(dbusmenu_menuitem_property_set_image, gboolean, \
(DbusmenuMenuitem * menuitem, const gchar* property, \
const GdkPixbuf* data)) \
FUNC(dbusmenu_menuitem_property_set_shortcut, gboolean, \
(DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier))
typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
typedef struct _DbusmenuServer DbusmenuServer;
enum DbusmenuStatus { DBUSMENU_STATUS_NORMAL, DBUSMENU_STATUS_NOTICE };
#define DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "submenu"
#define DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "children-display"
#define DBUSMENU_MENUITEM_PROP_ENABLED "enabled"
#define DBUSMENU_MENUITEM_PROP_ICON_DATA "icon-data"
#define DBUSMENU_MENUITEM_PROP_LABEL "label"
#define DBUSMENU_MENUITEM_PROP_SHORTCUT "shortcut"
#define DBUSMENU_MENUITEM_PROP_TYPE "type"
#define DBUSMENU_MENUITEM_PROP_TOGGLE_STATE "toggle-state"
#define DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE "toggle-type"
#define DBUSMENU_MENUITEM_PROP_VISIBLE "visible"
#define DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW "about-to-show"
#define DBUSMENU_MENUITEM_SIGNAL_EVENT "event"
#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated"
#define DBUSMENU_MENUITEM_TOGGLE_CHECK "checkmark"
#define DBUSMENU_MENUITEM_TOGGLE_RADIO "radio"
#define DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED 1
#define DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED 0
#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object"
class DBusMenuFunctions {
public:
DBusMenuFunctions() = delete;
static bool Init();
#define FUNC(name, type, params) \
typedef type(*_##name##_fn) params; \
static _##name##_fn s_##name;
DBUSMENU_GLIB_FUNCTIONS
DBUSMENU_GTK_FUNCTIONS
#undef FUNC
};
#define dbusmenu_menuitem_set_root \
DBusMenuFunctions::s_dbusmenu_menuitem_set_root
#define dbusmenu_menuitem_child_add_position \
DBusMenuFunctions::s_dbusmenu_menuitem_child_add_position
#define dbusmenu_menuitem_child_append \
DBusMenuFunctions::s_dbusmenu_menuitem_child_append
#define dbusmenu_menuitem_child_delete \
DBusMenuFunctions::s_dbusmenu_menuitem_child_delete
#define dbusmenu_menuitem_get_children \
DBusMenuFunctions::s_dbusmenu_menuitem_get_children
#define dbusmenu_menuitem_new DBusMenuFunctions::s_dbusmenu_menuitem_new
#define dbusmenu_menuitem_property_get \
DBusMenuFunctions::s_dbusmenu_menuitem_property_get
#define dbusmenu_menuitem_property_get_bool \
DBusMenuFunctions::s_dbusmenu_menuitem_property_get_bool
#define dbusmenu_menuitem_property_remove \
DBusMenuFunctions::s_dbusmenu_menuitem_property_remove
#define dbusmenu_menuitem_property_set \
DBusMenuFunctions::s_dbusmenu_menuitem_property_set
#define dbusmenu_menuitem_property_set_bool \
DBusMenuFunctions::s_dbusmenu_menuitem_property_set_bool
#define dbusmenu_menuitem_property_set_int \
DBusMenuFunctions::s_dbusmenu_menuitem_property_set_int
#define dbusmenu_menuitem_show_to_user \
DBusMenuFunctions::s_dbusmenu_menuitem_show_to_user
#define dbusmenu_menuitem_take_children \
DBusMenuFunctions::s_dbusmenu_menuitem_take_children
#define dbusmenu_server_new DBusMenuFunctions::s_dbusmenu_server_new
#define dbusmenu_server_set_root DBusMenuFunctions::s_dbusmenu_server_set_root
#define dbusmenu_server_set_status \
DBusMenuFunctions::s_dbusmenu_server_set_status
#define dbusmenu_menuitem_property_set_image \
DBusMenuFunctions::s_dbusmenu_menuitem_property_set_image
#define dbusmenu_menuitem_property_set_shortcut \
DBusMenuFunctions::s_dbusmenu_menuitem_property_set_shortcut
} // namespace widget
} // namespace mozilla
#endif

Просмотреть файл

@ -13,6 +13,9 @@
#include <gtk/gtk.h>
#include "mozilla/RefPtr.h"
typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
typedef struct _DbusmenuServer DbusmenuServer;
namespace mozilla {
template <typename T>
@ -25,6 +28,8 @@ struct GObjectRefPtrTraits {
template <> \
struct RefPtrTraits<type_> : public GObjectRefPtrTraits<type_> {};
GOBJECT_TRAITS(DbusmenuMenuitem)
GOBJECT_TRAITS(DbusmenuServer)
GOBJECT_TRAITS(GtkWidget)
GOBJECT_TRAITS(GFile)
GOBJECT_TRAITS(GFileMonitor)

Просмотреть файл

@ -4,6 +4,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "NativeMenuGtk.h"
#include "AsyncDBus.h"
#include "gdk/gdkkeysyms-compat.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/XULCommandEvent.h"
@ -15,6 +18,9 @@
#include "nsStubMutationObserver.h"
#include "mozilla/dom/Element.h"
#include "mozilla/StaticPrefs_widget.h"
#include "DBusMenu.h"
#include "nsLayoutUtils.h"
#include "nsGtkKeyUtils.h"
#include <dlfcn.h>
#include <gtk/gtk.h>
@ -35,7 +41,8 @@ static bool IsDisabled(const dom::Element& aElement) {
}
static bool NodeIsRelevant(const nsINode& aNode) {
return aNode.IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menuseparator,
nsGkAtoms::menuitem, nsGkAtoms::menugroup);
nsGkAtoms::menuitem, nsGkAtoms::menugroup,
nsGkAtoms::menubar);
}
// If this is a radio / checkbox menuitem, get the current value.
@ -155,7 +162,7 @@ void Actions::Clear() {
mNextActionIndex = 0;
}
class MenuModel final : public nsStubMutationObserver {
class MenuModel : public nsStubMutationObserver {
NS_DECL_ISUPPORTS
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
@ -166,6 +173,44 @@ class MenuModel final : public nsStubMutationObserver {
public:
explicit MenuModel(dom::Element* aElement) : mElement(aElement) {
mElement->AddMutationObserver(this);
}
dom::Element* Element() { return mElement; }
void RecomputeModelIfNeeded() {
if (!mDirty) {
return;
}
RecomputeModel();
mDirty = false;
}
bool IsShowing() { return mShowing; }
void WillShow() {
mShowing = true;
RecomputeModelIfNeeded();
}
void DidHide() { mShowing = false; }
protected:
virtual void RecomputeModel() = 0;
virtual ~MenuModel() { mElement->RemoveMutationObserver(this); }
void DirtyModel() {
mDirty = true;
if (mShowing) {
RecomputeModelIfNeeded();
}
}
RefPtr<dom::Element> mElement;
bool mDirty = true;
bool mShowing = false;
};
class MenuModelGMenu final : public MenuModel {
public:
explicit MenuModelGMenu(dom::Element* aElement) : MenuModel(aElement) {
mGMenu = dont_AddRef(g_menu_new());
mActions.mGroup = dont_AddRef(g_simple_action_group_new());
}
@ -175,32 +220,13 @@ class MenuModel final : public nsStubMutationObserver {
return G_ACTION_GROUP(mActions.mGroup.get());
}
dom::Element* Element() { return mElement; }
protected:
void RecomputeModel() override;
static void RecomputeModelFor(GMenu* aMenu, Actions& aActions,
const dom::Element& aElement);
void RecomputeModelIfNeeded();
bool IsShowing() { return mPoppedUp; }
void WillShow() {
mPoppedUp = true;
RecomputeModelIfNeeded();
}
void DidHide() { mPoppedUp = false; }
private:
virtual ~MenuModel() { mElement->RemoveMutationObserver(this); }
void DirtyModel() {
mDirty = true;
if (mPoppedUp) {
RecomputeModelIfNeeded();
}
}
RefPtr<dom::Element> mElement;
RefPtr<GMenu> mGMenu;
Actions mActions;
bool mDirty = true;
bool mPoppedUp = false;
};
NS_IMPL_ISUPPORTS(MenuModel, nsIMutationObserver)
@ -243,8 +269,8 @@ static const dom::Element* GetMenuPopupChild(const dom::Element& aElement) {
return nullptr;
}
static void RecomputeModelFor(GMenu* aMenu, Actions& aActions,
const dom::Element& aElement) {
void MenuModelGMenu::RecomputeModelFor(GMenu* aMenu, Actions& aActions,
const dom::Element& aElement) {
RefPtr<GMenu> sectionMenu;
auto FlushSectionMenu = [&] {
if (sectionMenu) {
@ -305,10 +331,7 @@ static void RecomputeModelFor(GMenu* aMenu, Actions& aActions,
FlushSectionMenu();
}
void MenuModel::RecomputeModelIfNeeded() {
if (!mDirty) {
return;
}
void MenuModelGMenu::RecomputeModel() {
mActions.Clear();
g_menu_remove_all(mGMenu.get());
RecomputeModelFor(mGMenu.get(), mActions, *mElement);
@ -341,7 +364,7 @@ METHOD_SIGNAL(Unmap);
#undef METHOD_SIGNAL
NativeMenuGtk::NativeMenuGtk(dom::Element* aElement)
: mMenuModel(MakeRefPtr<MenuModel>(aElement)) {
: mMenuModel(MakeRefPtr<MenuModelGMenu>(aElement)) {
// Floating, so no need to dont_AddRef.
mNativeMenu = gtk_menu_new_from_model(mMenuModel->GetModel());
gtk_widget_insert_action_group(mNativeMenu.get(), "menu",
@ -421,4 +444,373 @@ void NativeMenuGtk::CloseSubmenu(dom::Element*) {
// TODO: For testing mostly.
}
class MenubarModelDBus final : public MenuModel {
public:
explicit MenubarModelDBus(dom::Element* aElement) : MenuModel(aElement) {
mRoot = dont_AddRef(dbusmenu_menuitem_new());
dbusmenu_menuitem_set_root(mRoot.get(), true);
mShowing = true;
}
DbusmenuMenuitem* Root() const { return mRoot.get(); }
protected:
void RecomputeModel() override;
static void AppendMenuItem(DbusmenuMenuitem* aParent,
const dom::Element* aElement);
static void AppendSeparator(DbusmenuMenuitem* aParent);
static void AppendSubmenu(DbusmenuMenuitem* aParent,
const dom::Element* aMenu,
const dom::Element* aPopup);
static uint RecomputeModelFor(DbusmenuMenuitem* aParent,
const dom::Element& aElement);
RefPtr<DbusmenuMenuitem> mRoot;
};
void MenubarModelDBus::RecomputeModel() {
while (GList* children = dbusmenu_menuitem_get_children(mRoot.get())) {
auto* first = static_cast<DbusmenuMenuitem*>(children->data);
if (!first) {
break;
}
dbusmenu_menuitem_child_delete(mRoot.get(), first);
}
RecomputeModelFor(mRoot, *Element());
}
static const dom::Element* RelevantElementForKeys(
const dom::Element* aElement) {
nsAutoString key;
aElement->GetAttr(nsGkAtoms::key, key);
if (!key.IsEmpty()) {
dom::Document* document = aElement->OwnerDoc();
dom::Element* element = document->GetElementById(key);
if (element) {
return element;
}
}
return aElement;
}
static uint32_t ParseKey(const nsAString& aKey, const nsAString& aKeyCode) {
guint key = 0;
if (!aKey.IsEmpty()) {
key = gdk_unicode_to_keyval(*aKey.BeginReading());
}
if (key == 0 && !aKeyCode.IsEmpty()) {
key = KeymapWrapper::ConvertGeckoKeyCodeToGDKKeyval(aKeyCode);
}
return key;
}
static uint32_t KeyFrom(const dom::Element* aElement) {
const auto* element = RelevantElementForKeys(aElement);
nsAutoString key;
nsAutoString keycode;
element->GetAttr(nsGkAtoms::key, key);
element->GetAttr(nsGkAtoms::keycode, keycode);
return ParseKey(key, keycode);
}
// TODO(emilio): Unifiy with nsMenuUtilsX::GeckoModifiersForNodeAttribute (or
// at least switch to strtok_r).
static uint32_t ParseModifiers(const nsAString& aModifiers) {
if (aModifiers.IsEmpty()) {
return 0;
}
uint32_t modifier = 0;
char* str = ToNewUTF8String(aModifiers);
char* token = strtok(str, ", \t");
while (token) {
if (nsCRT::strcmp(token, "shift") == 0) {
modifier |= GDK_SHIFT_MASK;
} else if (nsCRT::strcmp(token, "alt") == 0) {
modifier |= GDK_MOD1_MASK;
} else if (nsCRT::strcmp(token, "meta") == 0) {
modifier |= GDK_META_MASK;
} else if (nsCRT::strcmp(token, "control") == 0) {
modifier |= GDK_CONTROL_MASK;
} else if (nsCRT::strcmp(token, "accel") == 0) {
auto accel = WidgetInputEvent::AccelModifier();
if (accel == MODIFIER_META) {
modifier |= GDK_META_MASK;
} else if (accel == MODIFIER_ALT) {
modifier |= GDK_MOD1_MASK;
} else if (accel == MODIFIER_CONTROL) {
modifier |= GDK_CONTROL_MASK;
}
}
token = strtok(nullptr, ", \t");
}
free(str);
return modifier;
}
static uint32_t ModifiersFrom(const dom::Element* aContent) {
const auto* element = RelevantElementForKeys(aContent);
nsAutoString modifiers;
element->GetAttr(nsGkAtoms::modifiers, modifiers);
return ParseModifiers(modifiers);
}
static void UpdateAccel(DbusmenuMenuitem* aItem, const nsIContent* aContent) {
uint32_t key = KeyFrom(aContent->AsElement());
if (key != 0) {
dbusmenu_menuitem_property_set_shortcut(
aItem, key,
static_cast<GdkModifierType>(ModifiersFrom(aContent->AsElement())));
}
}
static void UpdateRadioOrCheck(DbusmenuMenuitem* aItem,
const dom::Element* aContent) {
static mozilla::dom::Element::AttrValuesArray attrs[] = {
nsGkAtoms::checkbox, nsGkAtoms::radio, nullptr};
int32_t type = aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
attrs, eCaseMatters);
if (type < 0 || type >= 2) {
return;
}
if (type == 0) {
dbusmenu_menuitem_property_set(aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
DBUSMENU_MENUITEM_TOGGLE_CHECK);
} else {
dbusmenu_menuitem_property_set(aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE,
DBUSMENU_MENUITEM_TOGGLE_RADIO);
}
bool isChecked = aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
nsGkAtoms::_true, eCaseMatters);
dbusmenu_menuitem_property_set_int(
aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE,
isChecked ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED
: DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED);
}
static void UpdateEnabled(DbusmenuMenuitem* aItem, const nsIContent* aContent) {
bool disabled = aContent->AsElement()->AttrValueIs(
kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters);
dbusmenu_menuitem_property_set_bool(aItem, DBUSMENU_MENUITEM_PROP_ENABLED,
!disabled);
}
// we rebuild the dbus model when elements are removed from the DOM,
// so this isn't going to trigger for asynchronous
static MOZ_CAN_RUN_SCRIPT void DBusActivationCallback(
DbusmenuMenuitem* aMenuitem, guint aTimestamp, gpointer aUserData) {
RefPtr element = static_cast<dom::Element*>(aUserData);
ActivateItem(*element);
}
static void ConnectActivated(DbusmenuMenuitem* aItem,
const dom::Element* aContent) {
g_signal_connect(G_OBJECT(aItem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK(DBusActivationCallback),
const_cast<dom::Element*>(aContent));
}
static MOZ_CAN_RUN_SCRIPT void DBusAboutToShowCallback(
DbusmenuMenuitem* aMenuitem, gpointer aUserData) {
RefPtr element = static_cast<dom::Element*>(aUserData);
FireEvent(element, eXULPopupShowing);
FireEvent(element, eXULPopupShown);
}
static void ConnectAboutToShow(DbusmenuMenuitem* aItem,
const dom::Element* aContent) {
g_signal_connect(G_OBJECT(aItem), DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW,
G_CALLBACK(DBusAboutToShowCallback),
const_cast<dom::Element*>(aContent));
}
void MenubarModelDBus::AppendMenuItem(DbusmenuMenuitem* aParent,
const dom::Element* aChild) {
nsAutoString label;
aChild->GetAttr(nsGkAtoms::label, label);
if (label.IsEmpty()) {
aChild->GetAttr(nsGkAtoms::aria_label, label);
}
RefPtr<DbusmenuMenuitem> child = dont_AddRef(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_LABEL,
NS_ConvertUTF16toUTF8(label).get());
dbusmenu_menuitem_child_append(aParent, child);
UpdateAccel(child, aChild);
UpdateRadioOrCheck(child, aChild);
UpdateEnabled(child, aChild);
ConnectActivated(child, aChild);
// TODO: icons
}
void MenubarModelDBus::AppendSeparator(DbusmenuMenuitem* aParent) {
RefPtr<DbusmenuMenuitem> child = dont_AddRef(dbusmenu_menuitem_new());
dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_TYPE,
"separator");
dbusmenu_menuitem_child_append(aParent, child);
}
void MenubarModelDBus::AppendSubmenu(DbusmenuMenuitem* aParent,
const dom::Element* aMenu,
const dom::Element* aPopup) {
RefPtr<DbusmenuMenuitem> submenu = dont_AddRef(dbusmenu_menuitem_new());
if (RecomputeModelFor(submenu, *aPopup) == 0) {
RefPtr<DbusmenuMenuitem> placeholder = dont_AddRef(dbusmenu_menuitem_new());
dbusmenu_menuitem_child_append(submenu, placeholder);
}
nsAutoString label;
aMenu->GetAttr(nsGkAtoms::label, label);
ConnectAboutToShow(submenu, aPopup);
dbusmenu_menuitem_property_set(submenu, DBUSMENU_MENUITEM_PROP_LABEL,
NS_ConvertUTF16toUTF8(label).get());
dbusmenu_menuitem_child_append(aParent, submenu);
}
uint MenubarModelDBus::RecomputeModelFor(DbusmenuMenuitem* aParent,
const dom::Element& aElement) {
uint childCount = 0;
for (const nsIContent* child = aElement.GetFirstChild(); child;
child = child->GetNextSibling()) {
if (child->IsXULElement(nsGkAtoms::menuitem) &&
!IsDisabled(*child->AsElement())) {
AppendMenuItem(aParent, child->AsElement());
childCount++;
continue;
}
if (child->IsXULElement(nsGkAtoms::menuseparator)) {
AppendSeparator(aParent);
childCount++;
continue;
}
if (child->IsXULElement(nsGkAtoms::menu) &&
!IsDisabled(*child->AsElement())) {
if (const auto* popup = GetMenuPopupChild(*child->AsElement())) {
childCount++;
AppendSubmenu(aParent, child->AsElement(), popup);
}
}
}
return childCount;
}
void DBusMenuBar::NameOwnerChangedCallback(GObject*, GParamSpec*,
gpointer user_data) {
static_cast<DBusMenuBar*>(user_data)->OnNameOwnerChanged();
}
void DBusMenuBar::OnNameOwnerChanged() {
GUniquePtr<gchar> nameOwner(g_dbus_proxy_get_name_owner(mProxy));
if (!nameOwner) {
return;
}
RefPtr win = mMenuModel->Element()->OwnerDoc()->GetInnerWindow();
if (NS_WARN_IF(!win)) {
return;
}
nsIWidget* widget = nsGlobalWindowInner::Cast(win.get())->GetNearestWidget();
if (NS_WARN_IF(!widget)) {
return;
}
auto* gdkWin =
static_cast<GdkWindow*>(widget->GetNativeData(NS_NATIVE_WINDOW));
if (NS_WARN_IF(!gdkWin)) {
return;
}
if (auto* display = widget::WaylandDisplayGet()) {
// modern path
xdg_dbus_annotation_manager_v1* annotationManager =
display->GetXdgDbusAnnotationManager();
if (annotationManager == nullptr) {
NS_WARNING("annotation manager is not there");
return;
}
wl_surface* surface = gdk_wayland_window_get_wl_surface(gdkWin);
if (surface == nullptr) {
NS_WARNING("surface is not there");
return;
}
xdg_dbus_annotation_v1* annotation =
xdg_dbus_annotation_manager_v1_create_surface(
annotationManager, "com.canonical.dbusmenu", surface);
GDBusConnection* connection = g_dbus_proxy_get_connection(mProxy);
const char* myServiceName = g_dbus_connection_get_unique_name(connection);
if (!myServiceName) {
NS_WARNING("we do not have a unique name on the bus");
return;
}
xdg_dbus_annotation_v1_set_address(annotation, myServiceName,
mObjectPath.get());
mAnnotation = annotation;
} else {
// legacy path
auto xid = GDK_WINDOW_XID(gdkWin);
widget::DBusProxyCall(mProxy, "RegisterWindow",
g_variant_new("(uo)", xid, mObjectPath.get()),
G_DBUS_CALL_FLAGS_NONE)
->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}](RefPtr<GVariant>&& aResult) {
self->mMenuModel->Element()->SetBoolAttr(nsGkAtoms::hidden, true);
},
[self = RefPtr{this}](GUniquePtr<GError>&& aError) {
g_printerr("Failed to register window menubar: %s\n",
aError->message);
self->mMenuModel->Element()->SetBoolAttr(nsGkAtoms::hidden,
false);
});
}
}
static unsigned sID = 0;
DBusMenuBar::DBusMenuBar(dom::Element* aElement)
: mObjectPath(nsPrintfCString("/com/canonical/menu/%u", sID++)),
mMenuModel(MakeRefPtr<MenubarModelDBus>(aElement)),
mServer(dont_AddRef(dbusmenu_server_new(mObjectPath.get()))) {
mMenuModel->RecomputeModelIfNeeded();
dbusmenu_server_set_root(mServer.get(), mMenuModel->Root());
widget::CreateDBusProxyForBus(
G_BUS_TYPE_SESSION,
GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
nullptr, "com.canonical.AppMenu.Registrar",
"/com/canonical/AppMenu/Registrar", "com.canonical.AppMenu.Registrar")
->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}](RefPtr<GDBusProxy>&& aProxy) {
self->mProxy = std::move(aProxy);
g_signal_connect(self->mProxy, "notify::g-name-owner",
G_CALLBACK(NameOwnerChangedCallback), self.get());
self->OnNameOwnerChanged();
},
[self = RefPtr{this}](GUniquePtr<GError>&& aError) {
g_printerr("Failed to create DBUS proxy for menubar: %s\n",
aError->message);
});
}
DBusMenuBar::~DBusMenuBar() = default;
} // namespace mozilla::widget

Просмотреть файл

@ -7,10 +7,13 @@
#ifndef mozilla_widget_NativeMenuGtk_h
#define mozilla_widget_NativeMenuGtk_h
#include "mozilla/RefCounted.h"
#include "mozilla/widget/NativeMenu.h"
#include "mozilla/EventForwards.h"
#include "GRefPtr.h"
struct xdg_dbus_annotation_v1;
namespace mozilla {
namespace dom {
@ -19,7 +22,8 @@ class Element;
namespace widget {
class MenuModel;
class MenuModelGMenu;
class MenubarModelDBus;
class NativeMenuGtk : public NativeMenu {
public:
@ -54,10 +58,27 @@ class NativeMenuGtk : public NativeMenu {
bool mPoppedUp = false;
RefPtr<GtkWidget> mNativeMenu;
RefPtr<MenuModel> mMenuModel;
RefPtr<MenuModelGMenu> mMenuModel;
nsTArray<NativeMenu::Observer*> mObservers;
};
class DBusMenuBar final : public RefCounted<DBusMenuBar> {
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(DBusMenuBar)
explicit DBusMenuBar(dom::Element* aElement);
~DBusMenuBar();
protected:
static void NameOwnerChangedCallback(GObject*, GParamSpec*, gpointer);
void OnNameOwnerChanged();
nsCString mObjectPath;
RefPtr<MenubarModelDBus> mMenuModel;
RefPtr<DbusmenuServer> mServer;
RefPtr<GDBusProxy> mProxy;
xdg_dbus_annotation_v1* mAnnotation = nullptr;
};
} // namespace widget
} // namespace mozilla

Просмотреть файл

@ -5,8 +5,11 @@
#include "mozilla/widget/NativeMenuSupport.h"
#include "mozilla/StaticPrefs_widget.h"
#include "MainThreadUtils.h"
#include "NativeMenuGtk.h"
#include "DBusMenu.h"
#include "nsWindow.h"
namespace mozilla::widget {
@ -14,7 +17,12 @@ void NativeMenuSupport::CreateNativeMenuBar(nsIWidget* aParent,
dom::Element* aMenuBarElement) {
MOZ_RELEASE_ASSERT(NS_IsMainThread(),
"Attempting to create native menu bar on wrong thread!");
// TODO
if (aMenuBarElement && StaticPrefs::widget_gtk_global_menu_enabled() &&
DBusMenuFunctions::Init()) {
static_cast<nsWindow*>(aParent)->SetDBusMenuBar(
MakeRefPtr<DBusMenuBar>(aMenuBarElement));
}
}
already_AddRefed<NativeMenu> NativeMenuSupport::CreateNativeContextMenu(

Просмотреть файл

@ -63,6 +63,7 @@ UNIFIED_SOURCES += [
"AsyncGtkClipboardRequest.cpp",
"CompositorWidgetChild.cpp",
"CompositorWidgetParent.cpp",
"DBusMenu.cpp",
"DMABufLibWrapper.cpp",
"DMABufSurface.cpp",
"GfxInfo.cpp",

Просмотреть файл

@ -2125,6 +2125,212 @@ guint KeymapWrapper::GetGDKKeyvalWithoutModifier(
return keyval;
}
struct KeyCodeData {
const char* str;
size_t strlength;
uint32_t keycode;
};
static struct KeyCodeData gKeyCodes[] = {
#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \
{#aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode},
#include "mozilla/VirtualKeyCodeList.h"
#undef NS_DEFINE_VK
{nullptr, 0, 0}};
struct KeyPair {
uint32_t DOMKeyCode;
guint GDKKeyval;
};
//
// Netscape keycodes are defined in widget/public/nsGUIEvent.h
// GTK keycodes are defined in <gdk/gdkkeysyms.h>
//
static const KeyPair gKeyPairs[] = {
{NS_VK_CANCEL, GDK_Cancel},
{NS_VK_BACK, GDK_BackSpace},
{NS_VK_TAB, GDK_Tab},
{NS_VK_CLEAR, GDK_Clear},
{NS_VK_RETURN, GDK_Return},
{NS_VK_SHIFT, GDK_Shift_L},
{NS_VK_CONTROL, GDK_Control_L},
{NS_VK_ALT, GDK_Alt_L},
{NS_VK_META, GDK_Meta_L},
// Assume that Super or Hyper is always mapped to physical Win key.
{NS_VK_WIN, GDK_Super_L},
// GTK's AltGraph key is similar to Mac's Option (Alt) key. However,
// unfortunately, browsers on Mac are using NS_VK_ALT for it even though
// it's really different from Alt key on Windows.
// On the other hand, GTK's AltGrapsh keys are really different from
// Alt key. However, there is no AltGrapsh key on Windows. On Windows,
// both Ctrl and Alt keys are pressed internally when AltGr key is pressed.
// For some languages' users, AltGraph key is important, so, web
// applications on such locale may want to know AltGraph key press.
// Therefore, we should map AltGr keycode for them only on GTK.
{NS_VK_ALTGR, GDK_ISO_Level3_Shift},
{NS_VK_PAUSE, GDK_Pause},
{NS_VK_CAPS_LOCK, GDK_Caps_Lock},
{NS_VK_ESCAPE, GDK_Escape},
// { NS_VK_ACCEPT, GDK_XXX },
// { NS_VK_MODECHANGE, GDK_XXX },
{NS_VK_SPACE, GDK_space},
{NS_VK_PAGE_UP, GDK_Page_Up},
{NS_VK_PAGE_DOWN, GDK_Page_Down},
{NS_VK_END, GDK_End},
{NS_VK_HOME, GDK_Home},
{NS_VK_LEFT, GDK_Left},
{NS_VK_UP, GDK_Up},
{NS_VK_RIGHT, GDK_Right},
{NS_VK_DOWN, GDK_Down},
{NS_VK_SELECT, GDK_Select},
{NS_VK_PRINT, GDK_Print},
{NS_VK_EXECUTE, GDK_Execute},
{NS_VK_PRINTSCREEN, GDK_Print},
{NS_VK_INSERT, GDK_Insert},
{NS_VK_DELETE, GDK_Delete},
{NS_VK_HELP, GDK_Help},
{NS_VK_NUM_LOCK, GDK_Num_Lock},
{NS_VK_SCROLL_LOCK, GDK_Scroll_Lock},
// Function keys
{NS_VK_F1, GDK_F1},
{NS_VK_F2, GDK_F2},
{NS_VK_F3, GDK_F3},
{NS_VK_F4, GDK_F4},
{NS_VK_F5, GDK_F5},
{NS_VK_F6, GDK_F6},
{NS_VK_F7, GDK_F7},
{NS_VK_F8, GDK_F8},
{NS_VK_F9, GDK_F9},
{NS_VK_F10, GDK_F10},
{NS_VK_F11, GDK_F11},
{NS_VK_F12, GDK_F12},
{NS_VK_F13, GDK_F13},
{NS_VK_F14, GDK_F14},
{NS_VK_F15, GDK_F15},
{NS_VK_F16, GDK_F16},
{NS_VK_F17, GDK_F17},
{NS_VK_F18, GDK_F18},
{NS_VK_F19, GDK_F19},
{NS_VK_F20, GDK_F20},
{NS_VK_F21, GDK_F21},
{NS_VK_F22, GDK_F22},
{NS_VK_F23, GDK_F23},
{NS_VK_F24, GDK_F24},
// context menu key, keysym 0xff67, typically keycode 117 on 105-key
// (Microsoft) x86 keyboards, located between right 'Windows' key and right
// Ctrl key
{NS_VK_CONTEXT_MENU, GDK_Menu},
{NS_VK_SLEEP, GDK_Sleep},
{NS_VK_ATTN, GDK_3270_Attn},
{NS_VK_CRSEL, GDK_3270_CursorSelect},
{NS_VK_EXSEL, GDK_3270_ExSelect},
{NS_VK_EREOF, GDK_3270_EraseEOF},
{NS_VK_PLAY, GDK_3270_Play},
//{ NS_VK_ZOOM, GDK_XXX },
{NS_VK_PA1, GDK_3270_PA1},
{NS_VK_MULTIPLY, GDK_KP_Multiply},
{NS_VK_ADD, GDK_KP_Add},
{NS_VK_SEPARATOR, GDK_KP_Separator},
{NS_VK_SUBTRACT, GDK_KP_Subtract},
{NS_VK_DECIMAL, GDK_KP_Decimal},
{NS_VK_DIVIDE, GDK_KP_Divide},
{NS_VK_NUMPAD0, GDK_KP_0},
{NS_VK_NUMPAD1, GDK_KP_1},
{NS_VK_NUMPAD2, GDK_KP_2},
{NS_VK_NUMPAD3, GDK_KP_3},
{NS_VK_NUMPAD4, GDK_KP_4},
{NS_VK_NUMPAD5, GDK_KP_5},
{NS_VK_NUMPAD6, GDK_KP_6},
{NS_VK_NUMPAD7, GDK_KP_7},
{NS_VK_NUMPAD8, GDK_KP_8},
{NS_VK_NUMPAD9, GDK_KP_9},
{NS_VK_SPACE, GDK_space},
{NS_VK_COLON, GDK_colon},
{NS_VK_SEMICOLON, GDK_semicolon},
{NS_VK_LESS_THAN, GDK_less},
{NS_VK_EQUALS, GDK_equal},
{NS_VK_GREATER_THAN, GDK_greater},
{NS_VK_QUESTION_MARK, GDK_question},
{NS_VK_AT, GDK_at},
{NS_VK_CIRCUMFLEX, GDK_asciicircum},
{NS_VK_EXCLAMATION, GDK_exclam},
{NS_VK_DOUBLE_QUOTE, GDK_quotedbl},
{NS_VK_HASH, GDK_numbersign},
{NS_VK_DOLLAR, GDK_dollar},
{NS_VK_PERCENT, GDK_percent},
{NS_VK_AMPERSAND, GDK_ampersand},
{NS_VK_UNDERSCORE, GDK_underscore},
{NS_VK_OPEN_PAREN, GDK_parenleft},
{NS_VK_CLOSE_PAREN, GDK_parenright},
{NS_VK_ASTERISK, GDK_asterisk},
{NS_VK_PLUS, GDK_plus},
{NS_VK_PIPE, GDK_bar},
{NS_VK_HYPHEN_MINUS, GDK_minus},
{NS_VK_OPEN_CURLY_BRACKET, GDK_braceleft},
{NS_VK_CLOSE_CURLY_BRACKET, GDK_braceright},
{NS_VK_TILDE, GDK_asciitilde},
{NS_VK_COMMA, GDK_comma},
{NS_VK_PERIOD, GDK_period},
{NS_VK_SLASH, GDK_slash},
{NS_VK_BACK_QUOTE, GDK_grave},
{NS_VK_OPEN_BRACKET, GDK_bracketleft},
{NS_VK_BACK_SLASH, GDK_backslash},
{NS_VK_CLOSE_BRACKET, GDK_bracketright},
{NS_VK_QUOTE, GDK_apostrophe},
};
/* static */
guint KeymapWrapper::ConvertGeckoKeyCodeToGDKKeyval(const nsAString& aKeyCode) {
NS_ConvertUTF16toUTF8 keyName(aKeyCode);
ToUpperCase(keyName); // We want case-insensitive comparison with data
// stored as uppercase.
uint32_t keyCode = 0;
uint32_t keyNameLength = keyName.Length();
const char* keyNameStr = keyName.get();
for (const auto& code : gKeyCodes) {
if (keyNameLength == code.strlength &&
!nsCRT::strcmp(code.str, keyNameStr)) {
keyCode = code.keycode;
break;
}
}
// First, try to handle alphanumeric input, not listed in nsKeycodes:
// most likely, more letters will be getting typed in than things in
// the key list, so we will look through these first.
if (keyCode >= NS_VK_A && keyCode <= NS_VK_Z) {
// gdk and DOM both use the ASCII codes for these keys.
return keyCode;
}
// numbers
if (keyCode >= NS_VK_0 && keyCode <= NS_VK_9) {
// gdk and DOM both use the ASCII codes for these keys.
return keyCode - NS_VK_0 + GDK_0;
}
// misc other things
for (const auto& pair : gKeyPairs) {
if (pair.DOMKeyCode == keyCode) {
return pair.GDKKeyval;
}
}
return 0;
}
/* static */
uint32_t KeymapWrapper::GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval) {
switch (aGdkKeyval) {

Просмотреть файл

@ -55,6 +55,8 @@ class KeymapWrapper {
*/
static CodeNameIndex ComputeDOMCodeNameIndex(const GdkEventKey* aGdkKeyEvent);
static guint ConvertGeckoKeyCodeToGDKKeyval(const nsAString& aKeyCode);
/**
* We need to translate modifiers masks from Gdk to Gecko.
* MappedModifier is a table of mapped modifiers, we ignore other

Просмотреть файл

@ -93,6 +93,11 @@ void nsWaylandDisplay::SetXdgActivation(xdg_activation_v1* aXdgActivation) {
mXdgActivation = aXdgActivation;
}
void nsWaylandDisplay::SetXdgDbusAnnotationManager(
xdg_dbus_annotation_manager_v1* aXdgDbusAnnotationManager) {
mXdgDbusAnnotationManager = aXdgDbusAnnotationManager;
}
static void global_registry_handler(void* data, wl_registry* registry,
uint32_t id, const char* interface,
uint32_t version) {
@ -140,6 +145,11 @@ static void global_registry_handler(void* data, wl_registry* registry,
auto* activation = WaylandRegistryBind<xdg_activation_v1>(
registry, id, &xdg_activation_v1_interface, 1);
display->SetXdgActivation(activation);
} else if (iface.EqualsLiteral("xdg_dbus_annotation_manager_v1")) {
auto* annotationManager =
WaylandRegistryBind<xdg_dbus_annotation_manager_v1>(
registry, id, &xdg_dbus_annotation_manager_v1_interface, 1);
display->SetXdgDbusAnnotationManager(annotationManager);
} else if (iface.EqualsLiteral("wl_seat")) {
// Install keyboard handlers for main thread only
auto* seat =

Просмотреть файл

@ -19,6 +19,7 @@
#include "mozilla/widget/linux-dmabuf-unstable-v1-client-protocol.h"
#include "mozilla/widget/viewporter-client-protocol.h"
#include "mozilla/widget/xdg-activation-v1-client-protocol.h"
#include "mozilla/widget/xdg-dbus-annotation-v1-client-protocol.h"
#include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
namespace mozilla::widget {
@ -48,6 +49,9 @@ class nsWaylandDisplay {
}
zwp_linux_dmabuf_v1* GetDmabuf() { return mDmabuf; };
xdg_activation_v1* GetXdgActivation() { return mXdgActivation; };
xdg_dbus_annotation_manager_v1* GetXdgDbusAnnotationManager() {
return mXdgDbusAnnotationManager;
}
wp_fractional_scale_manager_v1* GetFractionalScaleManager() {
return mFractionalScaleManager;
}
@ -64,6 +68,8 @@ class nsWaylandDisplay {
void SetPointerConstraints(zwp_pointer_constraints_v1* aPointerConstraints);
void SetDmabuf(zwp_linux_dmabuf_v1* aDmabuf);
void SetXdgActivation(xdg_activation_v1* aXdgActivation);
void SetXdgDbusAnnotationManager(
xdg_dbus_annotation_manager_v1* aXdgDbusAnnotationManager);
void SetFractionalScaleManager(wp_fractional_scale_manager_v1* aManager) {
mFractionalScaleManager = aManager;
}
@ -84,6 +90,7 @@ class nsWaylandDisplay {
wp_viewporter* mViewporter = nullptr;
zwp_linux_dmabuf_v1* mDmabuf = nullptr;
xdg_activation_v1* mXdgActivation = nullptr;
xdg_dbus_annotation_manager_v1* mXdgDbusAnnotationManager = nullptr;
wp_fractional_scale_manager_v1* mFractionalScaleManager = nullptr;
bool mExplicitSync = false;
bool mIsPrimarySelectionEnabled = false;

Просмотреть файл

@ -100,6 +100,7 @@
#include "ScreenHelperGTK.h"
#include "SystemTimeConverter.h"
#include "WidgetUtilsGtk.h"
#include "NativeMenuGtk.h"
#ifdef ACCESSIBILITY
# include "mozilla/a11y/LocalAccessible.h"
@ -6987,6 +6988,11 @@ void nsWindow::UpdateWindowDraggingRegion(
}
}
void nsWindow::SetDBusMenuBar(
RefPtr<mozilla::widget::DBusMenuBar> aDbusMenuBar) {
mDBusMenuBar = std::move(aDbusMenuBar);
}
LayoutDeviceIntCoord nsWindow::GetTitlebarRadius() {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
int32_t cssCoord = LookAndFeel::GetInt(LookAndFeel::IntID::TitlebarRadius);

Просмотреть файл

@ -113,6 +113,7 @@ class CurrentX11TimeGetter;
#endif
namespace widget {
class DBusMenuBar;
class Screen;
} // namespace widget
} // namespace mozilla
@ -373,6 +374,8 @@ class nsWindow final : public nsBaseWidget {
void UpdateWindowDraggingRegion(
const LayoutDeviceIntRegion& aRegion) override;
void SetDBusMenuBar(RefPtr<mozilla::widget::DBusMenuBar> aDbusMenuBar);
// HiDPI scale conversion
gint GdkCeiledScaleFactor();
double FractionalScaleFactor();
@ -905,6 +908,8 @@ class nsWindow final : public nsBaseWidget {
RefPtr<nsWindow> mWaylandPopupNext;
RefPtr<nsWindow> mWaylandPopupPrev;
RefPtr<mozilla::widget::DBusMenuBar> mDBusMenuBar;
// When popup is resized by Gtk by move-to-rect callback,
// we store final popup size here. Then we use mMoveToRectPopupSize size
// in following popup operations unless mLayoutPopupSizeCleared is set.

Просмотреть файл

@ -15,6 +15,7 @@ SOURCES += [
"relative-pointer-unstable-v1-protocol.c",
"viewporter-protocol.c",
"xdg-activation-v1-protocol.c",
"xdg-dbus-annotation-v1-protocol.c",
"xdg-output-unstable-v1-protocol.c",
]
@ -26,6 +27,7 @@ EXPORTS.mozilla.widget += [
"relative-pointer-unstable-v1-client-protocol.h",
"viewporter-client-protocol.h",
"xdg-activation-v1-client-protocol.h",
"xdg-dbus-annotation-v1-client-protocol.h",
"xdg-output-unstable-v1-client-protocol.h",
]

Просмотреть файл

@ -0,0 +1,284 @@
/* Generated by wayland-scanner 1.19.0 */
#ifndef XDG_DBUS_ANNOTATION_V1_CLIENT_PROTOCOL_H
#define XDG_DBUS_ANNOTATION_V1_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_xdg_dbus_annotation_v1 The xdg_dbus_annotation_v1 protocol
* Wayland protocol for associating DBus objects with toplevels
*
* @section page_desc_xdg_dbus_annotation_v1 Description
*
* This description provides a high-level overview of the interplay between
* the interfaces defined in this protocol. For details, see the protocol
* specification.
*
* The dbus_annotation_manager allows a client to request the creation of an
* annotation object associated with an wl_surface or itself. The annotation
* object allows a client to notify the compositor of a DBus object associated
* with itself.
*
* Clients should request the creation of an dbus_annotation object when they
* create a DBus object associated with an wl_surface or themselves, and should
* release the object when they destroy a DBus object associated with their
* wl_surface or themselves.
*
* Clients should only own at most one dbus_annotation object with a given name
* for each of their wl_surface objects or themselves. A protocol error will be
* raised if a client requests more than one dbus_annotation object for an
* wl_surface or themselves with a given name.
*
* @section page_ifaces_xdg_dbus_annotation_v1 Interfaces
* - @subpage page_iface_xdg_dbus_annotation_manager_v1 - controller object for
* registering dbus objects associated with wl_surfaces or clients
* - @subpage page_iface_xdg_dbus_annotation_v1 - controller object for
* associating dbus objects with an wl_surface
* @section page_copyright_xdg_dbus_annotation_v1 Copyright
* <pre>
*
* Copyright © 2017 David Edmundson
* Copyrihgt © 2023 Janet Blackquill
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
* </pre>
*/
struct wl_surface;
struct xdg_dbus_annotation_manager_v1;
struct xdg_dbus_annotation_v1;
#ifndef XDG_DBUS_ANNOTATION_MANAGER_V1_INTERFACE
# define XDG_DBUS_ANNOTATION_MANAGER_V1_INTERFACE
/**
* @page page_iface_xdg_dbus_annotation_manager_v1
* xdg_dbus_annotation_manager_v1
* @section page_iface_xdg_dbus_annotation_manager_v1_desc Description
*
* An object that provides access to the creation of dbus_annotation objects.
* @section page_iface_xdg_dbus_annotation_manager_v1_api API
* See @ref iface_xdg_dbus_annotation_manager_v1.
*/
/**
* @defgroup iface_xdg_dbus_annotation_manager_v1 The
* xdg_dbus_annotation_manager_v1 interface
*
* An object that provides access to the creation of dbus_annotation objects.
*/
extern const struct wl_interface xdg_dbus_annotation_manager_v1_interface;
#endif
#ifndef XDG_DBUS_ANNOTATION_V1_INTERFACE
# define XDG_DBUS_ANNOTATION_V1_INTERFACE
/**
* @page page_iface_xdg_dbus_annotation_v1 xdg_dbus_annotation_v1
* @section page_iface_xdg_dbus_annotation_v1_desc Description
*
* An object that provides access to clients to notify the compositor of
* associated DBus objects for an wl_surface.
*
* If not applicable, clients should remove this object.
* @section page_iface_xdg_dbus_annotation_v1_api API
* See @ref iface_xdg_dbus_annotation_v1.
*/
/**
* @defgroup iface_xdg_dbus_annotation_v1 The xdg_dbus_annotation_v1 interface
*
* An object that provides access to clients to notify the compositor of
* associated DBus objects for an wl_surface.
*
* If not applicable, clients should remove this object.
*/
extern const struct wl_interface xdg_dbus_annotation_v1_interface;
#endif
#ifndef XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM
# define XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM
enum xdg_dbus_annotation_manager_v1_error {
/**
* given wl_surface or client already has a dbus_annotation with the same
* interface
*/
XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ALREADY_ANNOTATED = 0,
/**
* given wl_surface is invalid
*/
XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_BAD_TARGET = 1,
};
#endif /* XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM */
#define XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY 0
#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT 1
#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE 2
/**
* @ingroup iface_xdg_dbus_annotation_manager_v1
*/
#define XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_xdg_dbus_annotation_manager_v1
*/
#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT_SINCE_VERSION 1
/**
* @ingroup iface_xdg_dbus_annotation_manager_v1
*/
#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE_SINCE_VERSION 1
/** @ingroup iface_xdg_dbus_annotation_manager_v1 */
static inline void xdg_dbus_annotation_manager_v1_set_user_data(
struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1,
void* user_data) {
wl_proxy_set_user_data((struct wl_proxy*)xdg_dbus_annotation_manager_v1,
user_data);
}
/** @ingroup iface_xdg_dbus_annotation_manager_v1 */
static inline void* xdg_dbus_annotation_manager_v1_get_user_data(
struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) {
return wl_proxy_get_user_data(
(struct wl_proxy*)xdg_dbus_annotation_manager_v1);
}
static inline uint32_t xdg_dbus_annotation_manager_v1_get_version(
struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) {
return wl_proxy_get_version((struct wl_proxy*)xdg_dbus_annotation_manager_v1);
}
/**
* @ingroup iface_xdg_dbus_annotation_manager_v1
*
* Destroy the xdg_dbus_annotation_manager object. xdg_dbus_annotation objects
* created from this object remain valid and should be destroyed separately.
*/
static inline void xdg_dbus_annotation_manager_v1_destroy(
struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) {
wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_manager_v1,
XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY);
wl_proxy_destroy((struct wl_proxy*)xdg_dbus_annotation_manager_v1);
}
/**
* @ingroup iface_xdg_dbus_annotation_manager_v1
*
* The interface other DBus clients can expect the object specified by the
* annotation to implement.
*/
static inline struct xdg_dbus_annotation_v1*
xdg_dbus_annotation_manager_v1_create_client(
struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1,
const char* interface) {
struct wl_proxy* id;
id = wl_proxy_marshal_constructor(
(struct wl_proxy*)xdg_dbus_annotation_manager_v1,
XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT,
&xdg_dbus_annotation_v1_interface, interface, NULL);
return (struct xdg_dbus_annotation_v1*)id;
}
/**
* @ingroup iface_xdg_dbus_annotation_manager_v1
*
* The surface to associate the annotation with
*/
static inline struct xdg_dbus_annotation_v1*
xdg_dbus_annotation_manager_v1_create_surface(
struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1,
const char* interface, struct wl_surface* toplevel) {
struct wl_proxy* id;
id = wl_proxy_marshal_constructor(
(struct wl_proxy*)xdg_dbus_annotation_manager_v1,
XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE,
&xdg_dbus_annotation_v1_interface, interface, NULL, toplevel);
return (struct xdg_dbus_annotation_v1*)id;
}
#define XDG_DBUS_ANNOTATION_V1_DESTROY 0
#define XDG_DBUS_ANNOTATION_V1_SET_ADDRESS 1
/**
* @ingroup iface_xdg_dbus_annotation_v1
*/
#define XDG_DBUS_ANNOTATION_V1_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_xdg_dbus_annotation_v1
*/
#define XDG_DBUS_ANNOTATION_V1_SET_ADDRESS_SINCE_VERSION 1
/** @ingroup iface_xdg_dbus_annotation_v1 */
static inline void xdg_dbus_annotation_v1_set_user_data(
struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1, void* user_data) {
wl_proxy_set_user_data((struct wl_proxy*)xdg_dbus_annotation_v1, user_data);
}
/** @ingroup iface_xdg_dbus_annotation_v1 */
static inline void* xdg_dbus_annotation_v1_get_user_data(
struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) {
return wl_proxy_get_user_data((struct wl_proxy*)xdg_dbus_annotation_v1);
}
static inline uint32_t xdg_dbus_annotation_v1_get_version(
struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) {
return wl_proxy_get_version((struct wl_proxy*)xdg_dbus_annotation_v1);
}
/**
* @ingroup iface_xdg_dbus_annotation_v1
*/
static inline void xdg_dbus_annotation_v1_destroy(
struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) {
wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_v1,
XDG_DBUS_ANNOTATION_V1_DESTROY);
wl_proxy_destroy((struct wl_proxy*)xdg_dbus_annotation_v1);
}
/**
* @ingroup iface_xdg_dbus_annotation_v1
*
* Set or update the service name and object path corresponding to the
* DBus object. The DBus object should be registered on the session bus
* before sending this request.
*
* Strings should be formatted in Latin-1 matching the relevant DBus
* specifications.
*/
static inline void xdg_dbus_annotation_v1_set_address(
struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1,
const char* service_name, const char* object_path) {
wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_v1,
XDG_DBUS_ANNOTATION_V1_SET_ADDRESS, service_name,
object_path);
}
#ifdef __cplusplus
}
#endif
#endif

Просмотреть файл

@ -0,0 +1,75 @@
/* Generated by wayland-scanner 1.19.0 */
/*
* Copyright © 2017 David Edmundson
* Copyrihgt © 2023 Janet Blackquill
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
# define WL_PRIVATE __attribute__((visibility("hidden")))
#else
# define WL_PRIVATE
#endif
#pragma GCC visibility push(default)
extern const struct wl_interface wl_surface_interface;
#pragma GCC visibility pop
extern const struct wl_interface xdg_dbus_annotation_v1_interface;
static const struct wl_interface* xdg_dbus_annotation_v1_types[] = {
NULL,
NULL,
NULL,
&xdg_dbus_annotation_v1_interface,
NULL,
&xdg_dbus_annotation_v1_interface,
&wl_surface_interface,
};
static const struct wl_message xdg_dbus_annotation_manager_v1_requests[] = {
{"destroy", "", xdg_dbus_annotation_v1_types + 0},
{"create_client", "sn", xdg_dbus_annotation_v1_types + 2},
{"create_surface", "sno", xdg_dbus_annotation_v1_types + 4},
};
WL_PRIVATE const struct wl_interface xdg_dbus_annotation_manager_v1_interface =
{
"xdg_dbus_annotation_manager_v1", 1, 3,
xdg_dbus_annotation_manager_v1_requests, 0, NULL,
};
static const struct wl_message xdg_dbus_annotation_v1_requests[] = {
{"destroy", "", xdg_dbus_annotation_v1_types + 0},
{"set_address", "ss", xdg_dbus_annotation_v1_types + 0},
};
WL_PRIVATE const struct wl_interface xdg_dbus_annotation_v1_interface = {
"xdg_dbus_annotation_v1", 1, 2, xdg_dbus_annotation_v1_requests, 0, NULL,
};

Просмотреть файл

@ -78,7 +78,7 @@
#include "mozilla/dom/DocumentL10n.h"
#ifdef XP_MACOSX
#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
# include "mozilla/widget/NativeMenuSupport.h"
# define USE_NATIVE_MENUS
#endif
@ -491,10 +491,10 @@ NS_IMETHODIMP AppWindow::ShowModal() {
#ifdef USE_NATIVE_MENUS
if (!gfxPlatform::IsHeadless()) {
// macOS only: For modals created early in startup.
// (e.g. ProfileManager/ProfileDowngrade) this creates a fallback menu for
// the menu bar which only contains a "Quit" menu item.
// This allows the user to quit the application in a regular way with cmd+Q.
// On macOS, for modals created early in startup. (e.g.
// ProfileManager/ProfileDowngrade) this creates a fallback menu for the
// menu bar which only contains a "Quit" menu item. This allows the user to
// quit the application in a regular way with cmd+Q.
widget::NativeMenuSupport::CreateNativeMenuBar(mWindow, nullptr);
}
#endif
@ -3140,22 +3140,15 @@ static void LoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
// Find the menubar tag (if there is more than one, we ignore all but
// the first).
nsCOMPtr<nsINodeList> menubarElements = aDoc->GetElementsByTagNameNS(
nsLiteralString(
u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
u"menubar"_ns);
nsCOMPtr<nsINode> menubarNode;
RefPtr<Element> menubar;
if (menubarElements) {
menubarNode = menubarElements->Item(0);
menubar = Element::FromNodeOrNull(menubarElements->Item(0));
}
using widget::NativeMenuSupport;
if (menubarNode) {
nsCOMPtr<Element> menubarContent(do_QueryInterface(menubarNode));
NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubarContent);
} else {
NativeMenuSupport::CreateNativeMenuBar(aParentWindow, nullptr);
}
widget::NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubar);
if (!sHiddenWindowLoadedNativeMenus) {
sHiddenWindowLoadedNativeMenus = true;
@ -3197,13 +3190,11 @@ class L10nReadyPromiseHandler final : public dom::PromiseNativeHandler {
NS_IMPL_ISUPPORTS0(L10nReadyPromiseHandler)
static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
RefPtr<DocumentL10n> l10n = aDoc->GetL10n();
if (l10n) {
if (RefPtr<DocumentL10n> l10n = aDoc->GetL10n()) {
// Wait for l10n to be ready so the menus are localized.
RefPtr<Promise> promise = l10n->Ready();
MOZ_ASSERT(promise);
RefPtr<L10nReadyPromiseHandler> handler =
new L10nReadyPromiseHandler(aDoc, aParentWindow);
RefPtr handler = new L10nReadyPromiseHandler(aDoc, aParentWindow);
promise->AppendNativeHandler(handler);
} else {
// Something went wrong loading the doc and l10n wasn't created. This