Skip to content

Commit

Permalink
Bug 1747722 - Allow changing window class hints using attributes. r=e…
Browse files Browse the repository at this point in the history
…milio,stransky

This patch adds two new attributes to the window's `html` element, `windowclass` and `windowname`, to allow directly controlling the window's class hints on Xorg/Wayland (`res_class` and `res_name`) from the chrome/JS code. When they are set, values are used as class hints for their window. When they are not set, the current behavior of determining `res_class` and `res_name` (using `gdk_get_program_class` for `res_class` and parsing `windowtype` for `res_name`) is preserved.

Changes in `widget/nsIWidget.h` and `widget/nsBaseWidget.h` are only interface changes so that `setWindowClass` can accept three attributes. Although this patch does not affect Windows, `widget/windows/nsWindow.h` and `widget/windows/nsWindow.cpp` have been updated to comply with the new interface. `xpfe/appshell/AppWindow.cpp` has been updated to get the additional attributes and call `setWindowClass` with them. The main changes are in `widget/gtk/nsWindow.h` and `widget/gtk/nsWindow.cpp`.

Differential Revision: https://phabricator.services.mozilla.com/D169720
  • Loading branch information
filips123 committed Mar 2, 2023
1 parent dddca60 commit 20d7c9c
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 34 deletions.
80 changes: 55 additions & 25 deletions widget/gtk/nsWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6347,15 +6347,23 @@ void nsWindow::RefreshWindowClass(void) {
}

#ifdef MOZ_X11
if (!mGtkWindowAppName.IsEmpty() && GdkIsX11Display()) {
if (GdkIsX11Display()) {
XClassHint* class_hint = XAllocClassHint();
if (!class_hint) {
if (!class_hint) return;

const char* res_name =
!mGtkWindowAppName.IsEmpty() ? mGtkWindowAppName.get() : gAppData->name;

const char* res_class = !mGtkWindowAppClass.IsEmpty()
? mGtkWindowAppClass.get()
: gdk_get_program_class();

if (!res_name || !res_class) {
XFree(class_hint);
return;
}
const char* res_class = gdk_get_program_class();
if (!res_class) return;

class_hint->res_name = const_cast<char*>(mGtkWindowAppName.get());
class_hint->res_name = const_cast<char*>(res_name);
class_hint->res_class = const_cast<char*>(res_class);

// Can't use gtk_window_set_wmclass() for this; it prints
Expand All @@ -6368,32 +6376,54 @@ void nsWindow::RefreshWindowClass(void) {
#endif /* MOZ_X11 */
}

void nsWindow::SetWindowClass(const nsAString& xulWinType) {
void nsWindow::SetWindowClass(const nsAString& xulWinType,
const nsAString& xulWinClass,
const nsAString& xulWinName) {
if (!mShell) return;

char* res_name = ToNewCString(xulWinType, mozilla::fallible);
if (!res_name) return;

const char* role = nullptr;
// If window type attribute is set, parse it into name and role
if (!xulWinType.IsEmpty()) {
char* res_name = ToNewCString(xulWinType, mozilla::fallible);
const char* role = nullptr;

if (res_name) {
// Parse res_name into a name and role. Characters other than
// [A-Za-z0-9_-] are converted to '_'. Anything after the first
// colon is assigned to role; if there's no colon, assign the
// whole thing to both role and res_name.
for (char* c = res_name; *c; c++) {
if (':' == *c) {
*c = 0;
role = c + 1;
} else if (!isascii(*c) ||
(!isalnum(*c) && ('_' != *c) && ('-' != *c))) {
*c = '_';
}
}
res_name[0] = (char)toupper(res_name[0]);
if (!role) role = res_name;

// Parse res_name into a name and role. Characters other than
// [A-Za-z0-9_-] are converted to '_'. Anything after the first
// colon is assigned to role; if there's no colon, assign the
// whole thing to both role and res_name.
for (char* c = res_name; *c; c++) {
if (':' == *c) {
*c = 0;
role = c + 1;
} else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c))) {
*c = '_';
mGtkWindowAppName = res_name;
mGtkWindowRoleName = role;
free(res_name);
}
}
res_name[0] = (char)toupper(res_name[0]);
if (!role) role = res_name;

mGtkWindowAppName = res_name;
mGtkWindowRoleName = role;
free(res_name);
// If window class attribute is set, store it as app class
// If this attribute is not set, reset app class to default
if (!xulWinClass.IsEmpty()) {
CopyUTF16toUTF8(xulWinClass, mGtkWindowAppClass);
} else {
mGtkWindowAppClass = nullptr;
}

// If window class attribute is set, store it as app name
// If both name and type are not set, reset app name to default
if (!xulWinName.IsEmpty()) {
CopyUTF16toUTF8(xulWinName, mGtkWindowAppName);
} else if (xulWinType.IsEmpty()) {
mGtkWindowAppClass = nullptr;
}

RefreshWindowClass();
}
Expand Down
4 changes: 3 additions & 1 deletion widget/gtk/nsWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ class nsWindow final : public nsBaseWidget {
void* GetNativeData(uint32_t aDataType) override;
nsresult SetTitle(const nsAString& aTitle) override;
void SetIcon(const nsAString& aIconSpec) override;
void SetWindowClass(const nsAString& xulWinType) override;
void SetWindowClass(const nsAString& xulWinType, const nsAString& xulWinClass,
const nsAString& xulWinName) override;
LayoutDeviceIntPoint WidgetToScreenOffset() override;
void CaptureRollupEvents(bool aDoCapture) override;
[[nodiscard]] nsresult GetAttention(int32_t aCycleCount) override;
Expand Down Expand Up @@ -534,6 +535,7 @@ class nsWindow final : public nsBaseWidget {
void CreateAndPutGdkScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
double aDeltaX, double aDeltaY);

nsCString mGtkWindowAppClass;
nsCString mGtkWindowAppName;
nsCString mGtkWindowRoleName;
void RefreshWindowClass();
Expand Down
3 changes: 2 additions & 1 deletion widget/nsBaseWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference {
const nsTArray<ThemeGeometry>& aThemeGeometries) override {}
void SetModal(bool aModal) override {}
uint32_t GetMaxTouchPoints() const override;
void SetWindowClass(const nsAString& xulWinType) override {}
void SetWindowClass(const nsAString& xulWinType, const nsAString& xulWinClass,
const nsAString& xulWinName) override {}
// Return whether this widget interprets parameters to Move and Resize APIs
// as "desktop pixels" rather than "device pixels", and therefore
// applies its GetDefaultScale() value to them before using them as mBounds
Expand Down
16 changes: 15 additions & 1 deletion widget/nsIWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -1389,8 +1389,22 @@ class nsIWidget : public nsISupports {

/**
* Classify the window for the window manager. Mostly for X11.
*
* @param xulWinType The window type. Characters other than [A-Za-z0-9_-] are
* converted to '_'. Anything before the first colon is
* assigned to name, anything after it to role. If there's
* no colon, assign the whole thing to both role and name.
*
* @param xulWinClass The window class. If set, overrides the normal value.
* Otherwise, the program class it used.
*
* @param xulWinName The window name. If set, overrides the value specified in
* window type. Otherwise, name from window type is used.
*
*/
virtual void SetWindowClass(const nsAString& xulWinType) = 0;
virtual void SetWindowClass(const nsAString& xulWinType,
const nsAString& xulWinClass,
const nsAString& xulWinName) = 0;

/**
* Enables/Disables system capture of any and all events that would cause a
Expand Down
4 changes: 3 additions & 1 deletion widget/windows/nsWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4208,7 +4208,9 @@ uint32_t nsWindow::GetMaxTouchPoints() const {
return WinUtils::GetMaxTouchPoints();
}

void nsWindow::SetWindowClass(const nsAString& xulWinType) {
void nsWindow::SetWindowClass(const nsAString& xulWinType,
const nsAString& xulWinClass,
const nsAString& xulWinName) {
mIsEarlyBlankWindow = xulWinType.EqualsLiteral("navigator:blank");
}

Expand Down
3 changes: 2 additions & 1 deletion widget/windows/nsWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ class nsWindow final : public nsBaseWidget {
void UpdateThemeGeometries(
const nsTArray<ThemeGeometry>& aThemeGeometries) override;
uint32_t GetMaxTouchPoints() const override;
void SetWindowClass(const nsAString& xulWinType) override;
void SetWindowClass(const nsAString& xulWinType, const nsAString& xulWinClass,
const nsAString& xulWinName) override;

/**
* Event helpers
Expand Down
9 changes: 5 additions & 4 deletions xpfe/appshell/AppWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1628,11 +1628,12 @@ void AppWindow::SyncAttributesToWidget() {

NS_ENSURE_TRUE_VOID(mWindow);

// "windowtype" attribute
// "windowtype", "windowclass", "windowname" attributes
nsAutoString windowClassAttr, windowNameAttr;
windowElement->GetAttr(nsGkAtoms::windowtype, attr);
if (!attr.IsEmpty()) {
mWindow->SetWindowClass(attr);
}
windowElement->GetAttribute(u"windowclass"_ns, windowClassAttr);
windowElement->GetAttribute(u"windowname"_ns, windowNameAttr);
mWindow->SetWindowClass(attr, windowClassAttr, windowNameAttr);

NS_ENSURE_TRUE_VOID(mWindow);

Expand Down

0 comments on commit 20d7c9c

Please sign in to comment.