Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 769494
Collapse All | Expand All

(-)file_not_specified_in_diff (-111 / +47 lines)
Line  Link Here
                https://hg.mozilla.org/mozilla-central/rev/f8eb1926bfdb
                https://hg.mozilla.org/mozilla-central/rev/f8eb1926bfdb
1
                https://hg.mozilla.org/mozilla-central/rev/0ce69d9f596b
1
                https://hg.mozilla.org/mozilla-central/rev/0ce69d9f596b
2
                https://hg.mozilla.org/mozilla-central/rev/1859720a3225
2
                https://hg.mozilla.org/mozilla-central/rev/1859720a3225
3
                https://hg.mozilla.org/mozilla-central/rev/39213a71d10f
3
                https://hg.mozilla.org/mozilla-central/rev/39213a71d10f
4
-- a/accessible/atk/Platform.cpp
4
++ b/accessible/atk/Platform.cpp
Lines 19-29 Link Here
19
#endif
19
#endif
20
#include <gtk/gtk.h>
20
#include <gtk/gtk.h>
21
21
22
#ifdef MOZ_WIDGET_GTK
23
extern "C" __attribute__((weak, visibility("default"))) int
24
atk_bridge_adaptor_init(int*, char**[]);
25
#endif
26
27
using namespace mozilla;
22
using namespace mozilla;
28
using namespace mozilla::a11y;
23
using namespace mozilla::a11y;
29
24
Lines 33-40 Link Here
33
28
34
extern "C" {
29
extern "C" {
35
typedef GType (*AtkGetTypeType)(void);
30
typedef GType (*AtkGetTypeType)(void);
36
typedef void (*GnomeAccessibilityInit)(void);
31
typedef void (*AtkBridgeAdaptorInit)(int*, char**[]);
37
typedef void (*GnomeAccessibilityShutdown)(void);
38
}
32
}
39
33
40
static PRLibrary* sATKLib = nullptr;
34
static PRLibrary* sATKLib = nullptr;
Lines 50-111 Link Here
50
44
51
GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
45
GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
52
46
53
struct GnomeAccessibilityModule {
47
struct AtkBridgeModule {
54
  const char* libName;
48
  const char* libName;
55
  PRLibrary* lib;
49
  PRLibrary* lib;
56
  const char* initName;
50
  const char* initName;
57
  GnomeAccessibilityInit init;
51
  AtkBridgeAdaptorInit init;
58
  const char* shutdownName;
59
  GnomeAccessibilityShutdown shutdown;
60
};
52
};
61
53
62
static GnomeAccessibilityModule sAtkBridge = {
54
static AtkBridgeModule sAtkBridge = {"libatk-bridge-2.0.so.0", nullptr,
63
#ifdef AIX
55
                                     "atk_bridge_adaptor_init", nullptr};
64
    "libatk-bridge.a(libatk-bridge.so.0)", nullptr,
65
#else
66
    "libatk-bridge.so", nullptr,
67
#endif
68
    "gnome_accessibility_module_init",     nullptr,
69
    "gnome_accessibility_module_shutdown", nullptr};
70
56
71
static nsresult LoadGtkModule(GnomeAccessibilityModule& aModule) {
57
static nsresult LoadGtkModule(AtkBridgeModule& aModule) {
72
  NS_ENSURE_ARG(aModule.libName);
58
  NS_ENSURE_ARG(aModule.libName);
73
59
74
  if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
60
  if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
75
    // try to load the module with "gtk-2.0/modules" appended
61
    return NS_ERROR_FAILURE;
76
    char* curLibPath = PR_GetLibraryPath();
77
    nsAutoCString libPath(curLibPath);
78
#if defined(LINUX) && defined(__x86_64__)
79
    libPath.AppendLiteral(":/usr/lib64:/usr/lib");
80
#else
81
    libPath.AppendLiteral(":/usr/lib");
82
#endif
83
    PR_FreeLibraryName(curLibPath);
84
85
    int16_t loc1 = 0, loc2 = 0;
86
    int16_t subLen = 0;
87
    while (loc2 >= 0) {
88
      loc2 = libPath.FindChar(':', loc1);
89
      if (loc2 < 0) {
90
        subLen = libPath.Length() - loc1;
91
      } else {
92
        subLen = loc2 - loc1;
93
      }
94
      nsAutoCString sub(Substring(libPath, loc1, subLen));
95
      sub.AppendLiteral("/gtk-3.0/modules/");
96
      sub.Append(aModule.libName);
97
      aModule.lib = PR_LoadLibrary(sub.get());
98
      if (aModule.lib) break;
99
100
      loc1 = loc2 + 1;
101
    }
102
    if (!aModule.lib) return NS_ERROR_FAILURE;
103
  }
62
  }
104
63
105
  // we have loaded the library, try to get the function ptrs
64
  // we have loaded the library, try to get the function ptrs
106
  if (!(aModule.init = PR_FindFunctionSymbol(aModule.lib, aModule.initName)) ||
65
  if (!(aModule.init = (AtkBridgeAdaptorInit)PR_FindFunctionSymbol(
107
      !(aModule.shutdown =
66
            aModule.lib, aModule.initName))) {
108
            PR_FindFunctionSymbol(aModule.lib, aModule.shutdownName))) {
109
    // fail, :(
67
    // fail, :(
110
    PR_UnloadLibrary(aModule.lib);
68
    PR_UnloadLibrary(aModule.lib);
111
    aModule.lib = nullptr;
69
    aModule.lib = nullptr;
Lines 164-179 Link Here
164
122
165
  // Init atk-bridge now
123
  // Init atk-bridge now
166
  PR_SetEnv("NO_AT_BRIDGE=0");
124
  PR_SetEnv("NO_AT_BRIDGE=0");
167
#ifdef MOZ_WIDGET_GTK
125
  nsresult rv = LoadGtkModule(sAtkBridge);
168
  if (atk_bridge_adaptor_init) {
126
  if (NS_SUCCEEDED(rv)) {
169
    atk_bridge_adaptor_init(nullptr, nullptr);
127
    (*sAtkBridge.init)(nullptr, nullptr);
170
  } else
171
#endif
172
  {
173
    nsresult rv = LoadGtkModule(sAtkBridge);
174
    if (NS_SUCCEEDED(rv)) {
175
      (*sAtkBridge.init)();
176
    }
177
  }
128
  }
178
129
179
  if (!sToplevel_event_hook_added) {
130
  if (!sToplevel_event_hook_added) {
Lines 199-210 Link Here
199
  if (sAtkBridge.lib) {
150
  if (sAtkBridge.lib) {
200
    // Do not shutdown/unload atk-bridge,
151
    // Do not shutdown/unload atk-bridge,
201
    // an exit function registered will take care of it
152
    // an exit function registered will take care of it
202
    // if (sAtkBridge.shutdown)
203
    //     (*sAtkBridge.shutdown)();
204
    // PR_UnloadLibrary(sAtkBridge.lib);
153
    // PR_UnloadLibrary(sAtkBridge.lib);
205
    sAtkBridge.lib = nullptr;
154
    sAtkBridge.lib = nullptr;
206
    sAtkBridge.init = nullptr;
155
    sAtkBridge.init = nullptr;
207
    sAtkBridge.shutdown = nullptr;
208
  }
156
  }
209
  // if (sATKLib) {
157
  // if (sATKLib) {
210
  //     PR_UnloadLibrary(sATKLib);
158
  //     PR_UnloadLibrary(sATKLib);
211
-- a/browser/installer/package-manifest.in
159
++ b/browser/installer/package-manifest.in
Lines 105-111 Link Here
105
#endif
105
#endif
106
#ifdef MOZ_GTK
106
#ifdef MOZ_GTK
107
@BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
107
@BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
108
@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
109
#ifdef MOZ_WAYLAND
108
#ifdef MOZ_WAYLAND
110
@BINPATH@/@DLL_PREFIX@mozwayland@DLL_SUFFIX@
109
@BINPATH@/@DLL_PREFIX@mozwayland@DLL_SUFFIX@
111
#endif
110
#endif
112
-- a/config/recurse.mk
111
++ b/config/recurse.mk
Lines 210-219 Link Here
210
210
211
# Interdependencies that moz.build world don't know about yet for compilation.
211
# Interdependencies that moz.build world don't know about yet for compilation.
212
# Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
212
# Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
213
ifeq ($(MOZ_WIDGET_TOOLKIT),gtk)
214
toolkit/library/build/target: widget/gtk/mozgtk/gtk3/target
215
endif
216
217
ifndef MOZ_FOLD_LIBS
213
ifndef MOZ_FOLD_LIBS
218
ifndef MOZ_SYSTEM_NSS
214
ifndef MOZ_SYSTEM_NSS
219
netwerk/test/http3server/target: security/nss/lib/nss/nss_nss3/target security/nss/lib/ssl/ssl_ssl3/target
215
netwerk/test/http3server/target: security/nss/lib/nss/nss_nss3/target security/nss/lib/ssl/ssl_ssl3/target
220
-- a/old-configure.in
216
++ b/old-configure.in
Lines 53-59 Link Here
53
GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_42
53
GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_42
54
GLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_42
54
GLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_42
55
CAIRO_VERSION=1.10
55
CAIRO_VERSION=1.10
56
GTK2_VERSION=2.18.0
57
GTK3_VERSION=3.14.0
56
GTK3_VERSION=3.14.0
58
GDK_VERSION_MAX_ALLOWED=GDK_VERSION_3_14
57
GDK_VERSION_MAX_ALLOWED=GDK_VERSION_3_14
59
W32API_VERSION=3.14
58
W32API_VERSION=3.14
Lines 1581-1591 Link Here
1581
  if test "$MOZ_ENABLE_GTK"; then
1580
  if test "$MOZ_ENABLE_GTK"; then
1582
    AC_DEFINE_UNQUOTED(GLIB_VERSION_MIN_REQUIRED,$GLIB_VERSION_MIN_REQUIRED)
1581
    AC_DEFINE_UNQUOTED(GLIB_VERSION_MIN_REQUIRED,$GLIB_VERSION_MIN_REQUIRED)
1583
    AC_DEFINE_UNQUOTED(GLIB_VERSION_MAX_ALLOWED,$GLIB_VERSION_MAX_ALLOWED)
1582
    AC_DEFINE_UNQUOTED(GLIB_VERSION_MAX_ALLOWED,$GLIB_VERSION_MAX_ALLOWED)
1584
1585
    if test "$MOZ_X11"; then
1586
      PKG_CHECK_MODULES(MOZ_GTK2, gtk+-2.0 >= $GTK2_VERSION gtk+-unix-print-2.0 glib-2.0 >= $GLIB_VERSION gobject-2.0 gio-unix-2.0 gdk-x11-2.0)
1587
      MOZ_GTK2_CFLAGS="-I${_topsrcdir}/widget/gtk/compat $MOZ_GTK2_CFLAGS"
1588
    fi
1589
  fi
1583
  fi
1590
fi # COMPILE_ENVIRONMENT
1584
fi # COMPILE_ENVIRONMENT
1591
1585
1592
-- a/python/mozboot/mozboot/archlinux.py
1586
++ b/python/mozboot/mozboot/archlinux.py
Lines 33-39 Link Here
33
    BROWSER_PACKAGES = [
33
    BROWSER_PACKAGES = [
34
        "alsa-lib",
34
        "alsa-lib",
35
        "dbus-glib",
35
        "dbus-glib",
36
        "gtk2",
37
        "gtk3",
36
        "gtk3",
38
        "libevent",
37
        "libevent",
39
        "libvpx",
38
        "libvpx",
40
-- a/python/mozboot/mozboot/centosfedora.py
39
++ b/python/mozboot/mozboot/centosfedora.py
Lines 34-40 Link Here
34
            "alsa-lib-devel",
34
            "alsa-lib-devel",
35
            "dbus-glib-devel",
35
            "dbus-glib-devel",
36
            "glibc-static",
36
            "glibc-static",
37
            "gtk2-devel",  # It is optional in Fedora 20's GNOME Software
38
            # Development group.
37
            # Development group.
39
            "libstdc++-static",
38
            "libstdc++-static",
40
            "libXt-devel",
39
            "libXt-devel",
41
-- a/python/mozboot/mozboot/debian.py
40
++ b/python/mozboot/mozboot/debian.py
Lines 55-61 Link Here
55
        "libdbus-glib-1-dev",
55
        "libdbus-glib-1-dev",
56
        "libdrm-dev",
56
        "libdrm-dev",
57
        "libgtk-3-dev",
57
        "libgtk-3-dev",
58
        "libgtk2.0-dev",
59
        "libpulse-dev",
58
        "libpulse-dev",
60
        "libx11-xcb-dev",
59
        "libx11-xcb-dev",
61
        "libxt-dev",
60
        "libxt-dev",
62
-- a/python/mozboot/mozboot/freebsd.py
61
++ b/python/mozboot/mozboot/freebsd.py
Lines 27-33 Link Here
27
27
28
        self.browser_packages = [
28
        self.browser_packages = [
29
            "dbus-glib",
29
            "dbus-glib",
30
            "gtk2",
31
            "gtk3",
30
            "gtk3",
32
            "libXt",
31
            "libXt",
33
            "mesa-dri",  # depends on llvm*
32
            "mesa-dri",  # depends on llvm*
34
-- a/python/mozboot/mozboot/gentoo.py
33
++ b/python/mozboot/mozboot/gentoo.py
Lines 51-57 Link Here
51
                "--newuse",
51
                "--newuse",
52
                "dev-libs/dbus-glib",
52
                "dev-libs/dbus-glib",
53
                "media-sound/pulseaudio",
53
                "media-sound/pulseaudio",
54
                "x11-libs/gtk+:2",
55
                "x11-libs/gtk+:3",
54
                "x11-libs/gtk+:3",
56
                "x11-libs/libXt",
55
                "x11-libs/libXt",
57
            ]
56
            ]
58
-- a/python/mozboot/mozboot/openbsd.py
57
++ b/python/mozboot/mozboot/openbsd.py
Lines 23-29 Link Here
23
        self.browser_packages = [
23
        self.browser_packages = [
24
            "llvm",
24
            "llvm",
25
            "nasm",
25
            "nasm",
26
            "gtk+2",
27
            "gtk+3",
26
            "gtk+3",
28
            "dbus-glib",
27
            "dbus-glib",
29
            "pulseaudio",
28
            "pulseaudio",
30
-- a/python/mozboot/mozboot/opensuse.py
29
++ b/python/mozboot/mozboot/opensuse.py
Lines 31-37 Link Here
31
        "libXt-devel",
31
        "libXt-devel",
32
        "libproxy-devel",
32
        "libproxy-devel",
33
        "libuuid-devel",
33
        "libuuid-devel",
34
        "gtk2-devel",
35
        "clang-devel",
34
        "clang-devel",
36
        "patterns-gnome-devel_gnome",
35
        "patterns-gnome-devel_gnome",
37
    ]
36
    ]
38
-- a/python/mozboot/mozboot/solus.py
37
++ b/python/mozboot/mozboot/solus.py
Lines 32-38 Link Here
32
    BROWSER_PACKAGES = [
32
    BROWSER_PACKAGES = [
33
        "alsa-lib",
33
        "alsa-lib",
34
        "dbus",
34
        "dbus",
35
        "libgtk-2",
36
        "libgtk-3",
35
        "libgtk-3",
37
        "libevent",
36
        "libevent",
38
        "libvpx",
37
        "libvpx",
39
-- a/taskcluster/docker/debian10-test-iris/install_iris_deps.sh
38
++ b/taskcluster/docker/debian10-test-iris/install_iris_deps.sh
Lines 17-23 Link Here
17
apt_packages+=('automake')
17
apt_packages+=('automake')
18
apt_packages+=('fluxbox')
18
apt_packages+=('fluxbox')
19
apt_packages+=('libcairo2-dev')
19
apt_packages+=('libcairo2-dev')
20
apt_packages+=('libgtk2.0-dev')
21
apt_packages+=('libicu-dev')
20
apt_packages+=('libicu-dev')
22
apt_packages+=('libjpeg62-turbo-dev')
21
apt_packages+=('libjpeg62-turbo-dev')
23
apt_packages+=('libopencv-contrib-dev')
22
apt_packages+=('libopencv-contrib-dev')
24
-- a/taskcluster/docker/recipes/debian-test-system-setup.sh
23
++ b/taskcluster/docker/recipes/debian-test-system-setup.sh
Lines 33-39 Link Here
33
apt_packages+=('libdbus-1-dev')
33
apt_packages+=('libdbus-1-dev')
34
apt_packages+=('libdbus-glib-1-dev')
34
apt_packages+=('libdbus-glib-1-dev')
35
apt_packages+=('libgconf2-dev')
35
apt_packages+=('libgconf2-dev')
36
apt_packages+=('libgtk2.0-dev')
37
apt_packages+=('libiw-dev')
36
apt_packages+=('libiw-dev')
38
apt_packages+=('libnotify-dev')
37
apt_packages+=('libnotify-dev')
39
apt_packages+=('libpulse-dev')
38
apt_packages+=('libpulse-dev')
40
-- a/taskcluster/docker/recipes/ubuntu1804-test-system-setup-base.sh
39
++ b/taskcluster/docker/recipes/ubuntu1804-test-system-setup-base.sh
Lines 48-54 Link Here
48
apt_packages+=('libgl1-mesa-glx')
48
apt_packages+=('libgl1-mesa-glx')
49
apt_packages+=('libgstreamer-plugins-base1.0-dev')
49
apt_packages+=('libgstreamer-plugins-base1.0-dev')
50
apt_packages+=('libgstreamer1.0-dev')
50
apt_packages+=('libgstreamer1.0-dev')
51
apt_packages+=('libgtk2.0-dev')
52
apt_packages+=('libgtk-3-0')
51
apt_packages+=('libgtk-3-0')
53
apt_packages+=('libiw-dev')
52
apt_packages+=('libiw-dev')
54
apt_packages+=('libx11-xcb1')
53
apt_packages+=('libx11-xcb1')
Lines 120-126 Link Here
120
apt_packages+=('libxt6:i386')
119
apt_packages+=('libxt6:i386')
121
apt_packages+=('libxtst6:i386')
120
apt_packages+=('libxtst6:i386')
122
apt_packages+=('libsecret-1-0:i386')
121
apt_packages+=('libsecret-1-0:i386')
123
apt_packages+=('libgtk2.0-0:i386')
124
apt_packages+=('libgtk-3-0:i386')
122
apt_packages+=('libgtk-3-0:i386')
125
apt_packages+=('libx11-xcb1:i386')
123
apt_packages+=('libx11-xcb1:i386')
126
apt_packages+=('libxcb1:i386')
124
apt_packages+=('libxcb1:i386')
127
-- a/taskcluster/docker/static-analysis-build/Dockerfile
125
++ b/taskcluster/docker/static-analysis-build/Dockerfile
Lines 62-68 Link Here
62
      libgconf2-dev \
62
      libgconf2-dev \
63
      libgmp-dev \
63
      libgmp-dev \
64
      libgtk-3-dev \
64
      libgtk-3-dev \
65
      libgtk2.0-dev \
66
      libpango1.0-dev \
65
      libpango1.0-dev \
67
      libpulse-dev \
66
      libpulse-dev \
68
      libx11-xcb-dev \
67
      libx11-xcb-dev \
69
-- a/taskcluster/docker/update-verify/Dockerfile
68
++ b/taskcluster/docker/update-verify/Dockerfile
Lines 7-14 Link Here
7
RUN dpkg --add-architecture i386 && apt-get -q update \
7
RUN dpkg --add-architecture i386 && apt-get -q update \
8
    # p7zip-full is for extracting Windows and OS X packages
8
    # p7zip-full is for extracting Windows and OS X packages
9
    # wget is for downloading update.xml, installers, and MARs
9
    # wget is for downloading update.xml, installers, and MARs
10
    # libgtk-3-0 and libgtk2.0-0 are required to run the Firefox updater
10
    # libgtk-3-0 is required to run the Firefox updater
11
    && apt-get -q --yes install p7zip-full wget libgtk-3-0 libgtk-3.0:i386 libgtk2.0-0 libgtk2.0-0:i386 \
11
    && apt-get -q --yes install p7zip-full wget libgtk-3-0 libgtk-3.0:i386 \
12
    && apt-get clean
12
    && apt-get clean
13
13
14
RUN mkdir /builds
14
RUN mkdir /builds
15
-- a/taskcluster/scripts/misc/build-sysroot.sh
15
++ b/taskcluster/scripts/misc/build-sysroot.sh
Lines 32-38 Link Here
32
  libgconf2-dev
32
  libgconf2-dev
33
  libgcc-${gcc_version}-dev
33
  libgcc-${gcc_version}-dev
34
  libgtk-3-dev
34
  libgtk-3-dev
35
  libgtk2.0-dev
36
  libpango1.0-dev
35
  libpango1.0-dev
37
  libpulse-dev
36
  libpulse-dev
38
  libx11-xcb-dev
37
  libx11-xcb-dev
39
-- a/toolkit/library/moz.build
38
++ b/toolkit/library/moz.build
Lines 157-165 Link Here
157
]
157
]
158
158
159
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
159
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
160
    # The mozgtk library is a workaround that makes Gtk+ use libwayland-client
161
    # instead of mozwayland. The reason it works is that by being a dependency
162
    # of libxul, mozgtk appears in dependentlibs.list, preceding mozwayland
163
    # (which is important and guaranteed by the USE_LIBS order in this file).
164
    # That, in turn, makes firefox dlopen() mozgtk before mozwayland, which
165
    # will trigger the loading of the Gtk+ libraries (mozgtk depending on them).
166
    # Those libraries, if they depend on libwayland-client, will use the symbols
167
    # from libwayland-client because mozwayland is not loaded yet.
168
    # When eventually libxul is loaded after both mozgtk and mozwayland, it will
169
    # get symbols from libwayland-client too.
170
    # In the case where Gtk+ doesn't have wayland support, libwayland-client is
171
    # not loaded, and libxul ends up using the mozwayland symbols.
160
    USE_LIBS += [
172
    USE_LIBS += [
161
        "mozgtk_stub",
173
        "mozgtk",
162
    ]
174
    ]
175
    OS_LIBS += CONFIG["MOZ_GTK3_LIBS"]
163
176
164
if CONFIG["MOZ_WAYLAND"]:
177
if CONFIG["MOZ_WAYLAND"]:
165
    USE_LIBS += [
178
    USE_LIBS += [
(-)a/widget/gtk/moz.build.orig (-765 / +215 lines)
Line 0 Link Here
0
-- a/widget/gtk/mozgtk/gtk2/moz.build
1
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
2
# vim: set filetype=python:
3
# This Source Code Form is subject to the terms of the Mozilla Public
4
# License, v. 2.0. If a copy of the MPL was not distributed with this
5
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
with Files("**"):
8
    BUG_COMPONENT = ("Core", "Widget: Gtk")
9
10
with Files("*CompositorWidget*"):
11
    BUG_COMPONENT = ("Core", "Graphics")
12
13
with Files("*WindowSurface*"):
14
    BUG_COMPONENT = ("Core", "Graphics")
15
16
with Files("*IMContextWrapper*"):
17
    BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling")
18
19
with Files("*nsGtkKeyUtils*"):
20
    BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling")
21
22
if CONFIG["MOZ_WAYLAND"]:
23
    DIRS += ["wayland", "mozwayland"]
24
25
EXPORTS += [
26
    "MozContainer.h",
27
    "nsGTKToolkit.h",
28
    "nsIImageToPixbuf.h",
29
]
30
31
EXPORTS.mozilla += ["WidgetUtilsGtk.h"]
32
33
UNIFIED_SOURCES += [
34
    "IMContextWrapper.cpp",
35
    "MozContainer.cpp",
36
    "MPRISServiceHandler.cpp",
37
    "NativeKeyBindings.cpp",
38
    "nsAppShell.cpp",
39
    "nsBidiKeyboard.cpp",
40
    "nsColorPicker.cpp",
41
    "nsFilePicker.cpp",
42
    "nsGtkKeyUtils.cpp",
43
    "nsImageToPixbuf.cpp",
44
    "nsLookAndFeel.cpp",
45
    "nsNativeBasicThemeGTK.cpp",
46
    "nsNativeThemeGTK.cpp",
47
    "nsSound.cpp",
48
    "nsToolkit.cpp",
49
    "nsWidgetFactory.cpp",
50
    "ScreenHelperGTK.cpp",
51
    "TaskbarProgress.cpp",
52
    "WakeLockListener.cpp",
53
    "WidgetTraceEvent.cpp",
54
    "WidgetUtilsGtk.cpp",
55
]
56
57
SOURCES += [
58
    "MediaKeysEventSourceFactory.cpp",
59
    "nsWindow.cpp",  # conflicts with X11 headers
60
    "WaylandVsyncSource.cpp",  # conflicts with X11 headers
61
]
62
63
if CONFIG["MOZ_X11"]:
64
    UNIFIED_SOURCES += [
65
        "CompositorWidgetChild.cpp",
66
        "CompositorWidgetParent.cpp",
67
        "GtkCompositorWidget.cpp",
68
        "InProcessGtkCompositorWidget.cpp",
69
        "nsUserIdleServiceGTK.cpp",
70
    ]
71
    EXPORTS.mozilla.widget += [
72
        "CompositorWidgetChild.h",
73
        "CompositorWidgetParent.h",
74
        "GtkCompositorWidget.h",
75
        "InProcessGtkCompositorWidget.h",
76
    ]
77
78
if CONFIG["NS_PRINTING"]:
79
    UNIFIED_SOURCES += [
80
        "nsDeviceContextSpecG.cpp",
81
        "nsPrintDialogGTK.cpp",
82
        "nsPrintSettingsGTK.cpp",
83
        "nsPrintSettingsServiceGTK.cpp",
84
    ]
85
86
if CONFIG["MOZ_X11"]:
87
    UNIFIED_SOURCES += [
88
        "nsClipboard.cpp",
89
        "nsClipboardX11.cpp",
90
        "nsDragService.cpp",
91
        "WindowSurfaceProvider.cpp",
92
        "WindowSurfaceX11.cpp",
93
        "WindowSurfaceX11Image.cpp",
94
        "WindowSurfaceXRender.cpp",
95
    ]
96
    EXPORTS.mozilla.widget += [
97
        "WindowSurfaceProvider.h",
98
    ]
99
100
if CONFIG["MOZ_WAYLAND"]:
101
    UNIFIED_SOURCES += [
102
        "DMABufLibWrapper.cpp",
103
        "DMABufSurface.cpp",
104
        "MozContainerWayland.cpp",
105
        "nsClipboardWayland.cpp",
106
        "nsWaylandDisplay.cpp",
107
        "WindowSurfaceWayland.cpp",
108
    ]
109
    EXPORTS.mozilla.widget += [
110
        "DMABufLibWrapper.h",
111
        "DMABufSurface.h",
112
        "MozContainerWayland.h",
113
        "nsWaylandDisplay.h",
114
    ]
115
116
if CONFIG["ACCESSIBILITY"]:
117
    UNIFIED_SOURCES += [
118
        "maiRedundantObjectFactory.c",
119
    ]
120
121
UNIFIED_SOURCES += [
122
    "gtk3drawing.cpp",
123
    "nsApplicationChooser.cpp",
124
    "WidgetStyleCache.cpp",
125
]
126
127
XPCOM_MANIFESTS += [
128
    "components.conf",
129
]
130
131
include("/ipc/chromium/chromium-config.mozbuild")
132
133
FINAL_LIBRARY = "xul"
134
135
LOCAL_INCLUDES += [
136
    "/layout/base",
137
    "/layout/forms",
138
    "/layout/generic",
139
    "/layout/xul",
140
    "/other-licenses/atk-1.0",
141
    "/third_party/cups/include",
142
    "/widget",
143
    "/widget/headless",
144
]
145
146
if CONFIG["MOZ_X11"]:
147
    LOCAL_INCLUDES += [
148
        "/widget/x11",
149
    ]
150
151
DEFINES["CAIRO_GFX"] = True
152
153
DEFINES["MOZ_APP_NAME"] = '"%s"' % CONFIG["MOZ_APP_NAME"]
154
155
# When building with GTK3, the widget code always needs to use
156
# system Cairo headers, regardless of whether we are also linked
157
# against and using in-tree Cairo. By not using in-tree Cairo
158
# headers, we avoid picking up our renamed symbols, and instead
159
# use only system Cairo symbols that GTK3 uses. This allows that
160
# any Cairo objects created can be freely passed back and forth
161
# between the widget code and GTK3.
162
if not (CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk" and CONFIG["MOZ_TREE_CAIRO"]):
163
    CXXFLAGS += CONFIG["MOZ_CAIRO_CFLAGS"]
164
165
CFLAGS += CONFIG["TK_CFLAGS"]
166
CXXFLAGS += CONFIG["TK_CFLAGS"]
167
168
if CONFIG["MOZ_WAYLAND"]:
169
    CFLAGS += CONFIG["MOZ_WAYLAND_CFLAGS"]
170
    CXXFLAGS += CONFIG["MOZ_WAYLAND_CFLAGS"]
171
172
if CONFIG["MOZ_ENABLE_DBUS"]:
173
    CXXFLAGS += CONFIG["MOZ_DBUS_GLIB_CFLAGS"]
174
175
CXXFLAGS += ["-Wno-error=shadow"]
176
CXXFLAGS += ["-Werror=switch"]
Lines 1-40 Link Here
1
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
2
# vim: set filetype=python:
3
# This Source Code Form is subject to the terms of the Mozilla Public
4
# License, v. 2.0. If a copy of the MPL was not distributed with this
5
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
SOURCES += [
8
    "../mozgtk.c",
9
]
10
11
DEFINES["GTK3_SYMBOLS"] = True
12
13
SharedLibrary("mozgtk2")
14
15
SHARED_LIBRARY_NAME = "mozgtk"
16
17
FINAL_TARGET = "dist/bin/gtk2"
18
19
# If LDFLAGS contains -Wl,--as-needed or if it's the default for the toolchain,
20
# we need to add -Wl,--no-as-needed before the gtk libraries, otherwise the
21
# linker will drop those dependencies because no symbols are used from them.
22
# But those dependencies need to be kept for things to work properly.
23
# Ideally, we'd only add -Wl,--no-as-needed if necessary, but it's just simpler
24
# to add it unconditionally. This library is also simple enough that forcing
25
# -Wl,--as-needed after the gtk libraries is not going to make a significant
26
# difference.
27
if CONFIG["GCC_USE_GNU_LD"]:
28
    no_as_needed = ["-Wl,--no-as-needed"]
29
    as_needed = ["-Wl,--as-needed"]
30
else:
31
    no_as_needed = []
32
    as_needed = []
33
34
OS_LIBS += [f for f in CONFIG["MOZ_GTK2_LIBS"] if f.startswith("-L")]
35
OS_LIBS += no_as_needed
36
OS_LIBS += [
37
    "gtk-x11-2.0",
38
    "gdk-x11-2.0",
39
]
40
OS_LIBS += as_needed
41
-- a/widget/gtk/mozgtk/gtk3/moz.build
Lines 1-38 Link Here
1
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
0
++ b/widget/gtk/mozgtk/moz.build
2
# vim: set filetype=python:
3
# This Source Code Form is subject to the terms of the Mozilla Public
4
# License, v. 2.0. If a copy of the MPL was not distributed with this
5
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
SOURCES += [
8
    "../mozgtk.c",
9
]
10
11
DEFINES["GTK2_SYMBOLS"] = True
12
13
SharedLibrary("mozgtk")
14
15
SONAME = "mozgtk"
16
17
# If LDFLAGS contains -Wl,--as-needed or if it's the default for the toolchain,
18
# we need to add -Wl,--no-as-needed before the gtk libraries, otherwise the
19
# linker will drop those dependencies because no symbols are used from them.
20
# But those dependencies need to be kept for things to work properly.
21
# Ideally, we'd only add -Wl,--no-as-needed if necessary, but it's just simpler
22
# to add it unconditionally. This library is also simple enough that forcing
23
# -Wl,--as-needed after the gtk libraries is not going to make a significant
24
# difference.
25
if CONFIG["GCC_USE_GNU_LD"]:
26
    no_as_needed = ["-Wl,--no-as-needed"]
27
    as_needed = ["-Wl,--as-needed"]
28
else:
29
    no_as_needed = []
30
    as_needed = []
31
32
OS_LIBS += [f for f in CONFIG["MOZ_GTK3_LIBS"] if f.startswith("-L")]
33
OS_LIBS += no_as_needed
34
OS_LIBS += [
35
    "gtk-3",
36
    "gdk-3",
37
]
38
OS_LIBS += as_needed
39
-- a/widget/gtk/mozgtk/moz.build
Lines 4-7 Link Here
4
# License, v. 2.0. If a copy of the MPL was not distributed with this
4
# License, v. 2.0. If a copy of the MPL was not distributed with this
5
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
6
7
DIRS += ["stub", "gtk2", "gtk3"]
7
SharedLibrary("mozgtk")
8
9
SOURCES += [
10
    "mozgtk.c",
11
]
12
13
# If LDFLAGS contains -Wl,--as-needed or if it's the default for the toolchain,
14
# we need to add -Wl,--no-as-needed before the gtk libraries, otherwise the
15
# linker will drop those dependencies because no symbols are used from them.
16
# But those dependencies need to be kept for things to work properly.
17
# Ideally, we'd only add -Wl,--no-as-needed if necessary, but it's just simpler
18
# to add it unconditionally. This library is also simple enough that forcing
19
# -Wl,--as-needed after the gtk libraries is not going to make a significant
20
# difference.
21
if CONFIG["GCC_USE_GNU_LD"]:
22
    no_as_needed = ["-Wl,--no-as-needed"]
23
    as_needed = ["-Wl,--as-needed"]
24
else:
25
    no_as_needed = []
26
    as_needed = []
27
28
OS_LIBS += [f for f in CONFIG["MOZ_GTK3_LIBS"] if f.startswith("-L")]
29
OS_LIBS += no_as_needed
30
OS_LIBS += [
31
    "gtk-3",
32
    "gdk-3",
33
]
34
OS_LIBS += as_needed
8
-- a/widget/gtk/mozgtk/mozgtk.c
35
++ b/widget/gtk/mozgtk/mozgtk.c
Lines 5-674 Link Here
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
6
7
#include "mozilla/Types.h"
7
#include "mozilla/Types.h"
8
#include "mozilla/Assertions.h"
9
8
10
#define STUB(symbol) \
9
#include <X11/Xlib.h>
11
  MOZ_EXPORT void symbol(void) { MOZ_CRASH(); }
12
13
#ifdef COMMON_SYMBOLS
14
STUB(gdk_atom_intern)
15
STUB(gdk_atom_name)
16
STUB(gdk_beep)
17
STUB(gdk_cairo_create)
18
STUB(gdk_cairo_surface_create_from_pixbuf)
19
STUB(gdk_color_free)
20
STUB(gdk_color_parse)
21
STUB(gdk_cursor_new_for_display)
22
STUB(gdk_cursor_new_from_name)
23
STUB(gdk_cursor_new_from_pixbuf)
24
STUB(gdk_cursor_new_from_surface)
25
STUB(gdk_display_close)
26
STUB(gdk_display_get_default)
27
STUB(gdk_display_get_default_screen)
28
STUB(gdk_display_get_pointer)
29
STUB(gdk_display_get_window_at_pointer)
30
STUB(gdk_display_manager_get)
31
STUB(gdk_display_manager_set_default_display)
32
STUB(gdk_display_open)
33
STUB(gdk_display_sync)
34
STUB(gdk_display_warp_pointer)
35
STUB(gdk_drag_context_get_actions)
36
STUB(gdk_drag_context_get_dest_window)
37
STUB(gdk_drag_context_get_source_window)
38
STUB(gdk_drag_context_list_targets)
39
STUB(gdk_drag_status)
40
STUB(gdk_error_trap_pop)
41
STUB(gdk_error_trap_push)
42
STUB(gdk_event_copy)
43
STUB(gdk_event_free)
44
STUB(gdk_event_get_axis)
45
STUB(gdk_event_get_time)
46
STUB(gdk_event_handler_set)
47
STUB(gdk_event_peek)
48
STUB(gdk_event_put)
49
STUB(gdk_flush)
50
STUB(gdk_get_default_root_window)
51
STUB(gdk_get_display)
52
STUB(gdk_get_display_arg_name)
53
STUB(gdk_get_program_class)
54
STUB(gdk_keymap_get_default)
55
STUB(gdk_keymap_get_direction)
56
STUB(gdk_keymap_get_entries_for_keyval)
57
STUB(gdk_keymap_get_for_display)
58
STUB(gdk_keymap_have_bidi_layouts)
59
STUB(gdk_keymap_translate_keyboard_state)
60
STUB(gdk_keyval_name)
61
STUB(gdk_keyval_to_unicode)
62
STUB(gdk_pango_context_get)
63
STUB(gdk_pointer_grab)
64
STUB(gdk_pointer_ungrab)
65
STUB(gdk_property_change)
66
STUB(gdk_property_get)
67
STUB(gdk_property_delete)
68
STUB(gdk_screen_get_default)
69
STUB(gdk_screen_get_display)
70
STUB(gdk_screen_get_font_options)
71
STUB(gdk_screen_get_height)
72
STUB(gdk_screen_get_height_mm)
73
STUB(gdk_screen_get_n_monitors)
74
STUB(gdk_screen_get_monitor_at_window)
75
STUB(gdk_screen_get_monitor_geometry)
76
STUB(gdk_screen_get_monitor_height_mm)
77
STUB(gdk_screen_get_number)
78
STUB(gdk_screen_get_resolution)
79
STUB(gdk_screen_get_rgba_visual)
80
STUB(gdk_screen_get_root_window)
81
STUB(gdk_screen_get_system_visual)
82
STUB(gdk_screen_get_width)
83
STUB(gdk_screen_height)
84
STUB(gdk_screen_is_composited)
85
STUB(gdk_screen_width)
86
STUB(gdk_selection_owner_get)
87
STUB(gdk_set_program_class)
88
STUB(gdk_unicode_to_keyval)
89
STUB(gdk_visual_get_depth)
90
STUB(gdk_visual_get_system)
91
STUB(gdk_window_add_filter)
92
STUB(gdk_window_begin_move_drag)
93
STUB(gdk_window_begin_resize_drag)
94
STUB(gdk_window_destroy)
95
STUB(gdk_window_focus)
96
STUB(gdk_window_get_children)
97
STUB(gdk_window_get_display)
98
STUB(gdk_window_get_events)
99
STUB(gdk_window_get_geometry)
100
STUB(gdk_window_get_height)
101
STUB(gdk_window_get_origin)
102
STUB(gdk_window_get_parent)
103
STUB(gdk_window_get_position)
104
STUB(gdk_window_get_root_origin)
105
STUB(gdk_window_get_screen)
106
STUB(gtk_window_get_size)
107
STUB(gdk_window_get_state)
108
STUB(gdk_window_get_toplevel)
109
STUB(gdk_window_get_update_area)
110
STUB(gdk_window_get_user_data)
111
STUB(gdk_window_get_visual)
112
STUB(gdk_window_get_width)
113
STUB(gdk_window_get_window_type)
114
STUB(gdk_window_hide)
115
STUB(gdk_window_input_shape_combine_region)
116
STUB(gdk_window_invalidate_rect)
117
STUB(gdk_window_invalidate_region)
118
STUB(gdk_window_is_destroyed)
119
STUB(gdk_window_is_visible)
120
STUB(gdk_window_lower)
121
STUB(gdk_window_move)
122
STUB(gdk_window_move_resize)
123
STUB(gdk_window_new)
124
STUB(gdk_window_peek_children)
125
STUB(gdk_window_process_updates)
126
STUB(gdk_window_raise)
127
STUB(gdk_window_remove_filter)
128
STUB(gdk_window_reparent)
129
STUB(gdk_window_resize)
130
STUB(gdk_window_set_cursor)
131
STUB(gdk_window_set_debug_updates)
132
STUB(gdk_window_set_decorations)
133
STUB(gdk_window_set_events)
134
STUB(gdk_window_set_role)
135
STUB(gdk_window_set_urgency_hint)
136
STUB(gdk_window_set_user_data)
137
STUB(gdk_window_shape_combine_region)
138
STUB(gdk_window_show)
139
STUB(gdk_window_show_unraised)
140
STUB(gdk_x11_atom_to_xatom)
141
STUB(gdk_x11_display_get_user_time)
142
STUB(gdk_x11_display_get_xdisplay)
143
STUB(gdk_x11_get_default_root_xwindow)
144
STUB(gdk_x11_get_default_xdisplay)
145
STUB(gdk_x11_get_server_time)
146
STUB(gdk_x11_get_xatom_by_name)
147
STUB(gdk_x11_get_xatom_by_name_for_display)
148
STUB(gdk_x11_lookup_xdisplay)
149
STUB(gdk_x11_screen_get_xscreen)
150
STUB(gdk_x11_screen_get_screen_number)
151
STUB(gdk_x11_screen_lookup_visual)
152
STUB(gdk_x11_screen_supports_net_wm_hint)
153
STUB(gdk_x11_visual_get_xvisual)
154
STUB(gdk_x11_window_foreign_new_for_display)
155
STUB(gdk_x11_window_lookup_for_display)
156
STUB(gdk_x11_window_set_user_time)
157
STUB(gdk_x11_xatom_to_atom)
158
STUB(gdk_x11_set_sm_client_id)
159
STUB(gtk_accel_label_new)
160
STUB(gtk_alignment_get_type)
161
STUB(gtk_alignment_new)
162
STUB(gtk_alignment_set_padding)
163
STUB(gtk_arrow_get_type)
164
STUB(gtk_arrow_new)
165
STUB(gtk_bindings_activate)
166
STUB(gtk_bin_get_child)
167
STUB(gtk_bin_get_type)
168
STUB(gtk_border_free)
169
STUB(gtk_box_get_type)
170
STUB(gtk_box_pack_start)
171
STUB(gtk_button_new)
172
STUB(gtk_button_new_with_label)
173
STUB(gtk_check_button_new_with_label)
174
STUB(gtk_check_button_new_with_mnemonic)
175
STUB(gtk_check_menu_item_new)
176
STUB(gtk_check_version)
177
STUB(gtk_clipboard_clear)
178
STUB(gtk_clipboard_get)
179
STUB(gtk_clipboard_request_contents)
180
STUB(gtk_clipboard_request_text)
181
STUB(gtk_clipboard_set_can_store)
182
STUB(gtk_clipboard_set_with_data)
183
STUB(gtk_clipboard_store)
184
STUB(gtk_color_selection_dialog_get_color_selection)
185
STUB(gtk_color_selection_dialog_get_type)
186
STUB(gtk_color_selection_dialog_new)
187
STUB(gtk_color_selection_get_current_color)
188
STUB(gtk_color_selection_get_type)
189
STUB(gtk_color_selection_set_current_color)
190
STUB(gtk_combo_box_get_active)
191
STUB(gtk_combo_box_get_type)
192
STUB(gtk_combo_box_new)
193
STUB(gtk_combo_box_new_with_entry)
194
STUB(gtk_combo_box_set_active)
195
STUB(gtk_combo_box_text_get_type)
196
STUB(gtk_combo_box_text_new)
197
STUB(gtk_container_add)
198
STUB(gtk_container_forall)
199
STUB(gtk_container_get_border_width)
200
STUB(gtk_container_get_type)
201
STUB(gtk_container_set_border_width)
202
STUB(gtk_container_set_resize_mode)
203
STUB(gtk_dialog_get_content_area)
204
STUB(gtk_dialog_get_type)
205
STUB(gtk_dialog_new_with_buttons)
206
STUB(gtk_dialog_run)
207
STUB(gtk_dialog_set_alternative_button_order)
208
STUB(gtk_dialog_set_default_response)
209
STUB(gtk_drag_begin)
210
STUB(gtk_drag_dest_set)
211
STUB(gtk_drag_finish)
212
STUB(gtk_drag_get_data)
213
STUB(gtk_drag_get_source_widget)
214
STUB(gtk_drag_set_icon_pixbuf)
215
STUB(gtk_drag_set_icon_widget)
216
STUB(gtk_editable_get_type)
217
STUB(gtk_editable_select_region)
218
STUB(gtk_entry_get_text)
219
STUB(gtk_entry_get_type)
220
STUB(gtk_entry_new)
221
STUB(gtk_entry_set_activates_default)
222
STUB(gtk_entry_set_text)
223
STUB(gtk_enumerate_printers)
224
STUB(gtk_expander_new)
225
STUB(gtk_file_chooser_add_filter)
226
STUB(gtk_file_chooser_dialog_new)
227
STUB(gtk_file_chooser_get_filenames)
228
STUB(gtk_file_chooser_get_filter)
229
STUB(gtk_file_chooser_get_preview_filename)
230
STUB(gtk_file_chooser_get_type)
231
STUB(gtk_file_chooser_get_uri)
232
STUB(gtk_file_chooser_list_filters)
233
STUB(gtk_file_chooser_set_current_folder)
234
STUB(gtk_file_chooser_set_current_name)
235
STUB(gtk_file_chooser_set_do_overwrite_confirmation)
236
STUB(gtk_file_chooser_set_filename)
237
STUB(gtk_file_chooser_set_filter)
238
STUB(gtk_file_chooser_set_local_only)
239
STUB(gtk_file_chooser_set_preview_widget)
240
STUB(gtk_file_chooser_set_preview_widget_active)
241
STUB(gtk_file_chooser_set_select_multiple)
242
STUB(gtk_file_chooser_widget_get_type)
243
STUB(gtk_file_filter_add_pattern)
244
STUB(gtk_file_filter_new)
245
STUB(gtk_file_filter_set_name)
246
STUB(gtk_fixed_new)
247
STUB(gtk_frame_new)
248
STUB(gtk_get_current_event_time)
249
STUB(gtk_grab_add)
250
STUB(gtk_grab_remove)
251
STUB(gtk_handle_box_new)
252
STUB(gtk_hbox_new)
253
STUB(gtk_icon_info_free)
254
STUB(gtk_icon_info_load_icon)
255
STUB(gtk_icon_set_add_source)
256
STUB(gtk_icon_set_new)
257
STUB(gtk_icon_set_render_icon)
258
STUB(gtk_icon_set_unref)
259
STUB(gtk_icon_size_lookup)
260
STUB(gtk_icon_source_free)
261
STUB(gtk_icon_source_new)
262
STUB(gtk_icon_source_set_icon_name)
263
STUB(gtk_icon_theme_add_builtin_icon)
264
STUB(gtk_icon_theme_get_default)
265
STUB(gtk_icon_theme_get_icon_sizes)
266
STUB(gtk_icon_theme_lookup_by_gicon)
267
STUB(gtk_icon_theme_lookup_icon)
268
STUB(gtk_icon_theme_lookup_icon_for_scale)
269
STUB(gtk_image_get_icon_name)
270
STUB(gtk_image_get_type)
271
STUB(gtk_image_new)
272
STUB(gtk_image_new_from_icon_name)
273
STUB(gtk_image_new_from_stock)
274
STUB(gtk_image_set_from_pixbuf)
275
STUB(gtk_im_context_filter_keypress)
276
STUB(gtk_im_context_focus_in)
277
STUB(gtk_im_context_focus_out)
278
STUB(gtk_im_context_get_preedit_string)
279
STUB(gtk_im_context_reset)
280
STUB(gtk_im_context_set_client_window)
281
STUB(gtk_im_context_set_cursor_location)
282
STUB(gtk_im_context_set_surrounding)
283
STUB(gtk_im_context_simple_new)
284
STUB(gtk_im_multicontext_get_context_id)
285
STUB(gtk_im_multicontext_get_type)
286
STUB(gtk_im_multicontext_new)
287
STUB(gtk_info_bar_get_type)
288
STUB(gtk_info_bar_get_content_area)
289
STUB(gtk_info_bar_new)
290
STUB(gtk_init)
291
STUB(gtk_invisible_new)
292
STUB(gtk_key_snooper_install)
293
STUB(gtk_key_snooper_remove)
294
STUB(gtk_label_get_type)
295
STUB(gtk_label_new)
296
STUB(gtk_label_set_markup)
297
STUB(gtk_link_button_new)
298
STUB(gtk_main_do_event)
299
STUB(gtk_main_iteration)
300
STUB(gtk_menu_attach_to_widget)
301
STUB(gtk_menu_bar_new)
302
STUB(gtk_menu_get_type)
303
STUB(gtk_menu_item_get_type)
304
STUB(gtk_menu_item_new)
305
STUB(gtk_menu_item_set_submenu)
306
STUB(gtk_menu_new)
307
STUB(gtk_menu_shell_append)
308
STUB(gtk_menu_shell_get_type)
309
STUB(gtk_misc_get_alignment)
310
STUB(gtk_misc_get_padding)
311
STUB(gtk_misc_get_type)
312
STUB(gtk_misc_set_alignment)
313
STUB(gtk_misc_set_padding)
314
STUB(gtk_notebook_new)
315
STUB(gtk_page_setup_copy)
316
STUB(gtk_page_setup_get_bottom_margin)
317
STUB(gtk_page_setup_get_left_margin)
318
STUB(gtk_page_setup_get_orientation)
319
STUB(gtk_page_setup_get_paper_size)
320
STUB(gtk_page_setup_get_right_margin)
321
STUB(gtk_page_setup_get_top_margin)
322
STUB(gtk_page_setup_new)
323
STUB(gtk_page_setup_set_bottom_margin)
324
STUB(gtk_page_setup_set_left_margin)
325
STUB(gtk_page_setup_set_orientation)
326
STUB(gtk_page_setup_set_paper_size)
327
STUB(gtk_page_setup_set_paper_size_and_default_margins)
328
STUB(gtk_page_setup_set_right_margin)
329
STUB(gtk_page_setup_set_top_margin)
330
STUB(gtk_page_setup_to_key_file)
331
STUB(gtk_paper_size_free)
332
STUB(gtk_paper_size_get_display_name)
333
STUB(gtk_paper_size_get_height)
334
STUB(gtk_paper_size_get_name)
335
STUB(gtk_paper_size_get_width)
336
STUB(gtk_paper_size_is_custom)
337
STUB(gtk_paper_size_is_equal)
338
STUB(gtk_paper_size_new)
339
STUB(gtk_paper_size_new_custom)
340
STUB(gtk_paper_size_set_size)
341
STUB(gtk_parse_args)
342
STUB(gtk_plug_get_socket_window)
343
STUB(gtk_plug_get_type)
344
STUB(gtk_printer_accepts_pdf)
345
STUB(gtk_printer_get_name)
346
STUB(gtk_printer_get_type)
347
STUB(gtk_printer_is_default)
348
STUB(gtk_print_job_new)
349
STUB(gtk_print_job_send)
350
STUB(gtk_print_job_set_source_file)
351
STUB(gtk_print_run_page_setup_dialog)
352
STUB(gtk_print_settings_copy)
353
STUB(gtk_print_settings_foreach)
354
STUB(gtk_print_settings_get)
355
STUB(gtk_print_settings_get_duplex)
356
STUB(gtk_print_settings_get_n_copies)
357
STUB(gtk_print_settings_get_page_ranges)
358
STUB(gtk_print_settings_get_paper_size)
359
STUB(gtk_print_settings_get_printer)
360
STUB(gtk_print_settings_get_print_pages)
361
STUB(gtk_print_settings_get_resolution)
362
STUB(gtk_print_settings_get_reverse)
363
STUB(gtk_print_settings_get_scale)
364
STUB(gtk_print_settings_get_use_color)
365
STUB(gtk_print_settings_has_key)
366
STUB(gtk_print_settings_new)
367
STUB(gtk_print_settings_set)
368
STUB(gtk_print_settings_set_duplex)
369
STUB(gtk_print_settings_set_n_copies)
370
STUB(gtk_print_settings_set_orientation)
371
STUB(gtk_print_settings_set_page_ranges)
372
STUB(gtk_print_settings_set_paper_size)
373
STUB(gtk_print_settings_set_printer)
374
STUB(gtk_print_settings_set_print_pages)
375
STUB(gtk_print_settings_set_resolution)
376
STUB(gtk_print_settings_set_reverse)
377
STUB(gtk_print_settings_set_scale)
378
STUB(gtk_print_settings_set_use_color)
379
STUB(gtk_print_unix_dialog_add_custom_tab)
380
STUB(gtk_print_unix_dialog_get_page_setup)
381
STUB(gtk_print_unix_dialog_get_selected_printer)
382
STUB(gtk_print_unix_dialog_get_settings)
383
STUB(gtk_print_unix_dialog_get_type)
384
STUB(gtk_print_unix_dialog_new)
385
STUB(gtk_print_unix_dialog_set_manual_capabilities)
386
STUB(gtk_print_unix_dialog_set_page_setup)
387
STUB(gtk_print_unix_dialog_set_settings)
388
STUB(gtk_progress_bar_new)
389
STUB(gtk_propagate_event)
390
STUB(gtk_radio_button_get_type)
391
STUB(gtk_radio_button_new_with_label)
392
STUB(gtk_radio_button_new_with_mnemonic)
393
STUB(gtk_radio_button_new_with_mnemonic_from_widget)
394
STUB(gtk_range_get_min_slider_size)
395
STUB(gtk_range_get_type)
396
STUB(gtk_recent_manager_add_item)
397
STUB(gtk_recent_manager_get_default)
398
STUB(gtk_scrollbar_get_type)
399
STUB(gtk_scrolled_window_new)
400
STUB(gtk_selection_data_copy)
401
STUB(gtk_selection_data_free)
402
STUB(gtk_selection_data_get_data)
403
STUB(gtk_selection_data_get_length)
404
STUB(gtk_selection_data_get_selection)
405
STUB(gtk_selection_data_get_target)
406
STUB(gtk_selection_data_get_targets)
407
STUB(gtk_selection_data_set)
408
STUB(gtk_selection_data_set_pixbuf)
409
STUB(gtk_selection_data_set_text)
410
STUB(gtk_selection_data_targets_include_text)
411
STUB(gtk_separator_get_type)
412
STUB(gtk_separator_menu_item_new)
413
STUB(gtk_separator_tool_item_new)
414
STUB(gtk_settings_get_default)
415
STUB(gtk_settings_get_for_screen)
416
STUB(gtk_show_uri)
417
STUB(gtk_socket_add_id)
418
STUB(gtk_socket_get_id)
419
STUB(gtk_socket_get_type)
420
STUB(gtk_socket_get_plug_window)
421
STUB(gtk_socket_new)
422
STUB(gtk_spin_button_new)
423
STUB(gtk_statusbar_new)
424
STUB(gtk_style_lookup_icon_set)
425
STUB(gtk_table_attach)
426
STUB(gtk_table_get_type)
427
STUB(gtk_table_new)
428
STUB(gtk_target_list_add)
429
STUB(gtk_target_list_add_image_targets)
430
STUB(gtk_target_list_add_text_targets)
431
STUB(gtk_target_list_new)
432
STUB(gtk_target_list_unref)
433
STUB(gtk_targets_include_image)
434
STUB(gtk_targets_include_text)
435
STUB(gtk_target_table_free)
436
STUB(gtk_target_table_new_from_list)
437
STUB(gtk_text_view_new)
438
STUB(gtk_toggle_button_get_active)
439
STUB(gtk_toggle_button_get_type)
440
STUB(gtk_toggle_button_new)
441
STUB(gtk_toggle_button_set_active)
442
STUB(gtk_toggle_button_set_inconsistent)
443
STUB(gtk_toolbar_new)
444
STUB(gtk_tooltip_get_type)
445
STUB(gtk_tree_view_append_column)
446
STUB(gtk_tree_view_column_new)
447
STUB(gtk_tree_view_column_set_title)
448
STUB(gtk_tree_view_get_type)
449
STUB(gtk_tree_view_new)
450
STUB(gtk_vbox_new)
451
STUB(gtk_widget_add_events)
452
STUB(gtk_widget_class_find_style_property)
453
STUB(gtk_widget_destroy)
454
STUB(gtk_widget_destroyed)
455
STUB(gtk_widget_ensure_style)
456
STUB(gtk_widget_event)
457
STUB(gtk_widget_get_accessible)
458
STUB(gtk_widget_get_allocation)
459
STUB(gtk_widget_get_default_direction)
460
STUB(gtk_widget_get_display)
461
STUB(gtk_widget_get_events)
462
STUB(gtk_widget_get_has_window)
463
STUB(gtk_widget_get_mapped)
464
STUB(gtk_widget_get_parent)
465
STUB(gtk_widget_get_parent_window)
466
STUB(gtk_widget_get_realized)
467
STUB(gtk_widget_get_screen)
468
STUB(gtk_widget_get_style)
469
STUB(gtk_widget_get_toplevel)
470
STUB(gtk_widget_get_type)
471
STUB(gtk_widget_get_visible)
472
STUB(gtk_widget_get_visual)
473
STUB(gtk_widget_get_window)
474
STUB(gtk_widget_grab_focus)
475
STUB(gtk_widget_has_focus)
476
STUB(gtk_widget_has_grab)
477
STUB(gtk_widget_hide)
478
STUB(gtk_widget_is_focus)
479
STUB(gtk_widget_is_toplevel)
480
STUB(gtk_widget_map)
481
STUB(gtk_widget_modify_bg)
482
STUB(gtk_widget_realize)
483
STUB(gtk_widget_reparent)
484
STUB(gtk_widget_set_allocation)
485
STUB(gtk_widget_set_app_paintable)
486
STUB(gtk_widget_set_size_request)
487
STUB(gtk_window_set_auto_startup_notification)
488
STUB(gtk_window_set_keep_above)
489
STUB(gtk_window_set_opacity)
490
STUB(gtk_window_set_screen)
491
STUB(gtk_widget_set_can_focus)
492
STUB(gtk_widget_set_direction)
493
STUB(gtk_widget_set_double_buffered)
494
STUB(gtk_widget_set_has_window)
495
STUB(gtk_widget_set_mapped)
496
STUB(gtk_widget_set_name)
497
STUB(gtk_widget_set_parent)
498
STUB(gtk_widget_set_parent_window)
499
STUB(gtk_widget_set_realized)
500
STUB(gtk_widget_set_redraw_on_allocate)
501
STUB(gtk_widget_set_sensitive)
502
STUB(gtk_widget_set_window)
503
STUB(gtk_widget_show)
504
STUB(gtk_widget_show_all)
505
STUB(gtk_widget_size_allocate)
506
STUB(gtk_widget_style_get)
507
STUB(gtk_widget_unparent)
508
STUB(gtk_widget_unrealize)
509
STUB(gtk_window_deiconify)
510
STUB(gtk_window_fullscreen)
511
STUB(gtk_window_get_group)
512
STUB(gtk_window_get_modal)
513
STUB(gtk_window_get_transient_for)
514
STUB(gtk_window_get_type)
515
STUB(gtk_window_get_type_hint)
516
STUB(gtk_window_get_window_type)
517
STUB(gtk_window_group_add_window)
518
STUB(gtk_window_group_get_current_grab)
519
STUB(gtk_window_group_new)
520
STUB(gtk_window_iconify)
521
STUB(gtk_window_is_active)
522
STUB(gtk_window_maximize)
523
STUB(gtk_window_move)
524
STUB(gtk_window_new)
525
STUB(gtk_window_present_with_time)
526
STUB(gtk_window_resize)
527
STUB(gtk_window_set_accept_focus)
528
STUB(gtk_window_set_decorated)
529
STUB(gtk_window_set_deletable)
530
STUB(gtk_window_set_destroy_with_parent)
531
STUB(gtk_window_set_focus_on_map)
532
STUB(gtk_window_set_geometry_hints)
533
STUB(gtk_window_set_icon_name)
534
STUB(gtk_window_set_modal)
535
STUB(gtk_window_set_skip_taskbar_hint)
536
STUB(gtk_window_set_startup_id)
537
STUB(gtk_window_set_title)
538
STUB(gtk_window_set_transient_for)
539
STUB(gtk_window_set_type_hint)
540
STUB(gtk_window_set_wmclass)
541
STUB(gtk_window_unfullscreen)
542
STUB(gtk_window_unmaximize)
543
#endif
544
545
#ifdef GTK3_SYMBOLS
546
STUB(gtk_css_provider_load_from_data)
547
STUB(gtk_css_provider_new)
548
STUB(gdk_device_get_source)
549
STUB(gdk_device_manager_get_client_pointer)
550
STUB(gdk_disable_multidevice)
551
STUB(gdk_device_manager_list_devices)
552
STUB(gdk_display_get_device_manager)
553
STUB(gdk_display_manager_open_display)
554
STUB(gdk_error_trap_pop_ignored)
555
STUB(gdk_event_get_source_device)
556
STUB(gdk_screen_get_monitor_workarea)
557
STUB(gdk_window_get_frame_clock)
558
STUB(gdk_window_get_type)
559
STUB(gdk_window_set_opaque_region)
560
STUB(gdk_x11_window_get_xid)
561
STUB(gdk_wayland_display_get_wl_compositor)
562
STUB(gdk_wayland_display_get_wl_display)
563
STUB(gdk_wayland_window_get_wl_surface)
564
STUB(gtk_box_new)
565
STUB(gtk_cairo_should_draw_window)
566
STUB(gtk_cairo_transform_to_window)
567
STUB(gtk_combo_box_text_append)
568
STUB(gtk_drag_set_icon_surface)
569
STUB(gtk_get_major_version)
570
STUB(gtk_get_micro_version)
571
STUB(gtk_get_minor_version)
572
STUB(gtk_icon_info_load_symbolic_for_context)
573
STUB(gtk_menu_button_new)
574
STUB(gtk_offscreen_window_new)
575
STUB(gtk_paned_new)
576
STUB(gtk_radio_menu_item_new)
577
STUB(gtk_render_activity)
578
STUB(gtk_render_arrow)
579
STUB(gtk_render_background)
580
STUB(gtk_render_check)
581
STUB(gtk_render_expander)
582
STUB(gtk_render_extension)
583
STUB(gtk_render_focus)
584
STUB(gtk_render_frame)
585
STUB(gtk_render_frame_gap)
586
STUB(gtk_render_handle)
587
STUB(gtk_render_icon)
588
STUB(gtk_render_line)
589
STUB(gtk_render_option)
590
STUB(gtk_render_slider)
591
STUB(gtk_scale_new)
592
STUB(gtk_scrollbar_new)
593
STUB(gtk_style_context_add_class)
594
STUB(gtk_style_context_add_provider)
595
STUB(gtk_style_context_add_region)
596
STUB(gtk_style_context_get)
597
STUB(gtk_style_context_get_background_color)
598
STUB(gtk_style_context_get_border)
599
STUB(gtk_style_context_get_border_color)
600
STUB(gtk_style_context_get_color)
601
STUB(gtk_style_context_get_direction)
602
STUB(gtk_style_context_get_margin)
603
STUB(gtk_style_context_get_padding)
604
STUB(gtk_style_context_get_path)
605
STUB(gtk_style_context_get_property)
606
STUB(gtk_style_context_get_state)
607
STUB(gtk_style_context_get_style)
608
STUB(gtk_style_context_has_class)
609
STUB(gtk_style_context_invalidate)
610
STUB(gtk_style_context_list_classes)
611
STUB(gtk_style_context_new)
612
STUB(gtk_style_context_remove_class)
613
STUB(gtk_style_context_remove_region)
614
STUB(gtk_style_context_restore)
615
STUB(gtk_style_context_save)
616
STUB(gtk_style_context_set_direction)
617
STUB(gtk_style_context_set_path)
618
STUB(gtk_style_context_set_parent)
619
STUB(gtk_style_context_set_state)
620
STUB(gtk_style_properties_lookup_property)
621
STUB(gtk_style_provider_get_type)
622
STUB(gtk_tree_view_column_get_button)
623
STUB(gtk_widget_get_preferred_size)
624
STUB(gtk_widget_get_preferred_width)
625
STUB(gtk_widget_get_preferred_height)
626
STUB(gtk_widget_get_state_flags)
627
STUB(gtk_widget_get_style_context)
628
STUB(gtk_widget_path_append_type)
629
STUB(gtk_widget_path_copy)
630
STUB(gtk_widget_path_free)
631
STUB(gtk_widget_path_iter_add_class)
632
STUB(gtk_widget_path_get_object_type)
633
STUB(gtk_widget_path_length)
634
STUB(gtk_widget_path_new)
635
STUB(gtk_widget_path_unref)
636
STUB(gtk_widget_set_valign)
637
STUB(gtk_widget_set_visual)
638
STUB(gtk_window_set_titlebar)
639
STUB(gtk_app_chooser_dialog_new_for_content_type)
640
STUB(gtk_app_chooser_get_type)
641
STUB(gtk_app_chooser_get_app_info)
642
STUB(gtk_app_chooser_dialog_get_type)
643
STUB(gtk_app_chooser_dialog_set_heading)
644
STUB(gtk_color_chooser_dialog_new)
645
STUB(gtk_color_chooser_dialog_get_type)
646
STUB(gtk_color_chooser_get_type)
647
STUB(gtk_color_chooser_set_rgba)
648
STUB(gtk_color_chooser_get_rgba)
649
STUB(gtk_color_chooser_set_use_alpha)
650
STUB(gdk_wayland_device_get_wl_pointer)
651
#endif
652
653
#ifdef GTK2_SYMBOLS
654
STUB(gdk_drawable_get_screen)
655
STUB(gdk_rgb_get_colormap)
656
STUB(gdk_rgb_get_visual)
657
STUB(gdk_window_lookup)
658
STUB(gdk_window_set_back_pixmap)
659
STUB(gdk_x11_colormap_foreign_new)
660
STUB(gdk_x11_colormap_get_xcolormap)
661
STUB(gdk_x11_drawable_get_xdisplay)
662
STUB(gdk_x11_drawable_get_xid)
663
STUB(gdk_x11_window_get_drawable_impl)
664
STUB(gdkx_visual_get)
665
STUB(gtk_object_get_type)
666
#endif
667
668
#ifndef GTK3_SYMBOLS
669
// Only define the following workaround when using GTK3, which we detect
670
// by checking if GTK3 stubs are not provided.
671
#  include <X11/Xlib.h>
672
// Bug 1271100
10
// Bug 1271100
673
// We need to trick system Cairo into not using the XShm extension due to
11
// We need to trick system Cairo into not using the XShm extension due to
674
// a race condition in it that results in frequent BadAccess errors. Cairo
12
// a race condition in it that results in frequent BadAccess errors. Cairo
Lines 676-680 Link Here
676
// So we define our own stub that always indicates XShm not being present.
14
// So we define our own stub that always indicates XShm not being present.
677
// mozgtk loads before libXext/libcairo and so this stub will take priority.
15
// mozgtk loads before libXext/libcairo and so this stub will take priority.
678
// Our tree usage goes through xcb and remains unaffected by this.
16
// Our tree usage goes through xcb and remains unaffected by this.
17
//
18
// This is also used to force libxul to depend on the mozgtk library. If we
19
// ever can remove this workaround for system Cairo, we'll need something
20
// to replace it for that purpose.
679
MOZ_EXPORT Bool XShmQueryExtension(Display* aDisplay) { return False; }
21
MOZ_EXPORT Bool XShmQueryExtension(Display* aDisplay) { return False; }
680
#endif
681
-- a/widget/gtk/mozgtk/stub/moz.build
Lines 1-16 Link Here
1
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
0
++ b/widget/gtk/nsWindow.cpp
2
# vim: set filetype=python:
3
# This Source Code Form is subject to the terms of the Mozilla Public
4
# License, v. 2.0. If a copy of the MPL was not distributed with this
5
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
SOURCES += [
8
    "../mozgtk.c",
9
]
10
11
for var in ("COMMON_SYMBOLS", "GTK2_SYMBOLS", "GTK3_SYMBOLS"):
12
    DEFINES[var] = True
13
14
SharedLibrary("mozgtk_stub")
15
16
SONAME = "mozgtk"
17
-- a/widget/gtk/nsWindow.cpp
Lines 5158-5163 Link Here
5158
      // vblank.
5158
      // vblank.
5159
      SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
5159
      SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
5160
    }
5160
    }
5161
    // Dummy call to a function in mozgtk to prevent the linker from removing
5162
    // the dependency with --as-needed.
5163
    XShmQueryExtension(mXDisplay);
5161
  }
5164
  }
5162
#  ifdef MOZ_WAYLAND
5165
#  ifdef MOZ_WAYLAND
5163
  else if (!mIsX11Display) {
5166
  else if (!mIsX11Display) {
(-)a/widget/gtk/nsWindow.cpp.orig (+8750 lines)
Line 0 Link Here
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:expandtab:shiftwidth=2:tabstop=2:
3
 */
4
/* This Source Code Form is subject to the terms of the Mozilla Public
5
 * License, v. 2.0. If a copy of the MPL was not distributed with this
6
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8
#include "nsWindow.h"
9
10
#include "mozilla/ArrayUtils.h"
11
#include "mozilla/EventForwards.h"
12
#include "mozilla/Maybe.h"
13
#include "mozilla/MiscEvents.h"
14
#include "mozilla/MouseEvents.h"
15
#include "mozilla/PresShell.h"
16
#include "mozilla/ProfilerLabels.h"
17
#include "mozilla/RefPtr.h"
18
#include "mozilla/ScopeExit.h"
19
#include "mozilla/StaticPrefs_apz.h"
20
#include "mozilla/StaticPrefs_ui.h"
21
#include "mozilla/TextEventDispatcher.h"
22
#include "mozilla/TextEvents.h"
23
#include "mozilla/TimeStamp.h"
24
#include "mozilla/TouchEvents.h"
25
#include "mozilla/UniquePtrExtensions.h"
26
#include "mozilla/WidgetUtils.h"
27
#include "mozilla/WritingModes.h"
28
#include "mozilla/X11Util.h"
29
#include "mozilla/XREAppData.h"
30
#include "mozilla/dom/Document.h"
31
#include "mozilla/dom/WheelEventBinding.h"
32
#include "InputData.h"
33
#include "nsAppRunner.h"
34
#include <algorithm>
35
36
#include "prlink.h"
37
#include "nsGTKToolkit.h"
38
#include "nsIRollupListener.h"
39
#include "nsINode.h"
40
41
#include "nsWidgetsCID.h"
42
#include "nsDragService.h"
43
#include "nsIWidgetListener.h"
44
#include "nsIScreenManager.h"
45
#include "SystemTimeConverter.h"
46
#include "nsViewManager.h"
47
#include "nsMenuPopupFrame.h"
48
#include "nsXPLookAndFeel.h"
49
50
#include "nsGtkKeyUtils.h"
51
#include "nsGtkCursors.h"
52
#include "ScreenHelperGTK.h"
53
#include "WidgetUtilsGtk.h"
54
55
#include <gtk/gtk.h>
56
#include <gtk/gtkx.h>
57
58
#ifdef MOZ_WAYLAND
59
#  include <gdk/gdkwayland.h>
60
#endif /* MOZ_WAYLAND */
61
62
#ifdef MOZ_X11
63
#  include <gdk/gdkx.h>
64
#  include <X11/Xatom.h>
65
#  include <X11/extensions/XShm.h>
66
#  include <X11/extensions/shape.h>
67
#  include <gdk/gdkkeysyms-compat.h>
68
#endif /* MOZ_X11 */
69
70
#include <gdk/gdkkeysyms.h>
71
72
#if defined(MOZ_WAYLAND)
73
#  include <gdk/gdkwayland.h>
74
#  include "nsView.h"
75
#endif
76
77
#include "nsGkAtoms.h"
78
79
#include "mozilla/Assertions.h"
80
#include "mozilla/Likely.h"
81
#include "mozilla/Preferences.h"
82
#include "nsGfxCIID.h"
83
#include "nsGtkUtils.h"
84
#include "nsLayoutUtils.h"
85
#include "mozilla/layers/LayersTypes.h"
86
#include "nsIUserIdleServiceInternal.h"
87
#include "GLContext.h"
88
#include "gfx2DGlue.h"
89
90
#ifdef ACCESSIBILITY
91
#  include "mozilla/a11y/LocalAccessible.h"
92
#  include "mozilla/a11y/Platform.h"
93
#  include "nsAccessibilityService.h"
94
95
using namespace mozilla;
96
using namespace mozilla::widget;
97
#endif
98
99
/* For SetIcon */
100
#include "nsAppDirectoryServiceDefs.h"
101
#include "nsString.h"
102
#include "nsIFile.h"
103
104
/* SetCursor(imgIContainer*) */
105
#include <gdk/gdk.h>
106
#include <wchar.h>
107
#include "imgIContainer.h"
108
#include "nsGfxCIID.h"
109
#include "nsImageToPixbuf.h"
110
#include "nsIInterfaceRequestorUtils.h"
111
#include "ClientLayerManager.h"
112
#include "nsIGSettingsService.h"
113
114
#include "gfxPlatformGtk.h"
115
#include "gfxContext.h"
116
#include "gfxImageSurface.h"
117
#include "gfxUtils.h"
118
#include "Layers.h"
119
#include "GLContextProvider.h"
120
#include "mozilla/gfx/2D.h"
121
#include "mozilla/gfx/HelpersCairo.h"
122
#include "mozilla/gfx/GPUProcessManager.h"
123
#include "mozilla/layers/CompositorBridgeParent.h"
124
#include "mozilla/layers/CompositorThread.h"
125
#include "mozilla/layers/KnowsCompositor.h"
126
#include "mozilla/layers/WebRenderBridgeChild.h"
127
#include "mozilla/layers/WebRenderLayerManager.h"
128
129
#include "mozilla/layers/APZInputBridge.h"
130
#include "mozilla/layers/IAPZCTreeManager.h"
131
132
#ifdef MOZ_X11
133
#  include "mozilla/gfx/gfxVars.h"
134
#  include "GLContextGLX.h"  // for GLContextGLX::FindVisual()
135
#  include "GLContextEGL.h"  // for GLContextEGL::FindVisual()
136
#  include "GtkCompositorWidget.h"
137
#  include "gfxXlibSurface.h"
138
#  include "WindowSurfaceX11Image.h"
139
#  include "WindowSurfaceX11SHM.h"
140
#  include "WindowSurfaceXRender.h"
141
#endif  // MOZ_X11
142
#ifdef MOZ_WAYLAND
143
#  include "nsIClipboard.h"
144
#endif
145
146
#include "nsShmImage.h"
147
#include "gtkdrawing.h"
148
149
#include "NativeKeyBindings.h"
150
151
#include <dlfcn.h>
152
#include "nsPresContext.h"
153
154
using namespace mozilla;
155
using namespace mozilla::gfx;
156
using namespace mozilla::widget;
157
using namespace mozilla::layers;
158
using mozilla::gl::GLContextEGL;
159
using mozilla::gl::GLContextGLX;
160
161
// Don't put more than this many rects in the dirty region, just fluff
162
// out to the bounding-box if there are more
163
#define MAX_RECTS_IN_REGION 100
164
165
#if !GTK_CHECK_VERSION(3, 18, 0)
166
167
struct _GdkEventTouchpadPinch {
168
  GdkEventType type;
169
  GdkWindow* window;
170
  gint8 send_event;
171
  gint8 phase;
172
  gint8 n_fingers;
173
  guint32 time;
174
  gdouble x;
175
  gdouble y;
176
  gdouble dx;
177
  gdouble dy;
178
  gdouble angle_delta;
179
  gdouble scale;
180
  gdouble x_root, y_root;
181
  guint state;
182
};
183
184
typedef enum {
185
  GDK_TOUCHPAD_GESTURE_PHASE_BEGIN,
186
  GDK_TOUCHPAD_GESTURE_PHASE_UPDATE,
187
  GDK_TOUCHPAD_GESTURE_PHASE_END,
188
  GDK_TOUCHPAD_GESTURE_PHASE_CANCEL
189
} GdkTouchpadGesturePhase;
190
191
GdkEventMask GDK_TOUCHPAD_GESTURE_MASK = static_cast<GdkEventMask>(1 << 24);
192
GdkEventType GDK_TOUCHPAD_PINCH = static_cast<GdkEventType>(42);
193
194
#endif
195
196
const gint kEvents = GDK_TOUCHPAD_GESTURE_MASK | GDK_EXPOSURE_MASK |
197
                     GDK_STRUCTURE_MASK | GDK_VISIBILITY_NOTIFY_MASK |
198
                     GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
199
                     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
200
                     GDK_SMOOTH_SCROLL_MASK | GDK_TOUCH_MASK | GDK_SCROLL_MASK |
201
                     GDK_POINTER_MOTION_MASK | GDK_PROPERTY_CHANGE_MASK |
202
                     GDK_FOCUS_CHANGE_MASK;
203
204
#if !GTK_CHECK_VERSION(3, 22, 0)
205
typedef enum {
206
  GDK_ANCHOR_FLIP_X = 1 << 0,
207
  GDK_ANCHOR_FLIP_Y = 1 << 1,
208
  GDK_ANCHOR_SLIDE_X = 1 << 2,
209
  GDK_ANCHOR_SLIDE_Y = 1 << 3,
210
  GDK_ANCHOR_RESIZE_X = 1 << 4,
211
  GDK_ANCHOR_RESIZE_Y = 1 << 5,
212
  GDK_ANCHOR_FLIP = GDK_ANCHOR_FLIP_X | GDK_ANCHOR_FLIP_Y,
213
  GDK_ANCHOR_SLIDE = GDK_ANCHOR_SLIDE_X | GDK_ANCHOR_SLIDE_Y,
214
  GDK_ANCHOR_RESIZE = GDK_ANCHOR_RESIZE_X | GDK_ANCHOR_RESIZE_Y
215
} GdkAnchorHints;
216
#endif
217
218
/* utility functions */
219
static bool is_mouse_in_window(GdkWindow* aWindow, gdouble aMouseX,
220
                               gdouble aMouseY);
221
static nsWindow* get_window_for_gtk_widget(GtkWidget* widget);
222
static nsWindow* get_window_for_gdk_window(GdkWindow* window);
223
static GtkWidget* get_gtk_widget_for_gdk_window(GdkWindow* window);
224
static GdkCursor* get_gtk_cursor(nsCursor aCursor);
225
226
static GdkWindow* get_inner_gdk_window(GdkWindow* aWindow, gint x, gint y,
227
                                       gint* retx, gint* rety);
228
229
static int is_parent_ungrab_enter(GdkEventCrossing* aEvent);
230
static int is_parent_grab_leave(GdkEventCrossing* aEvent);
231
232
/* callbacks from widgets */
233
static gboolean expose_event_cb(GtkWidget* widget, cairo_t* rect);
234
static gboolean configure_event_cb(GtkWidget* widget, GdkEventConfigure* event);
235
static void container_unrealize_cb(GtkWidget* widget);
236
static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation);
237
static void toplevel_window_size_allocate_cb(GtkWidget* widget,
238
                                             GtkAllocation* allocation);
239
static gboolean delete_event_cb(GtkWidget* widget, GdkEventAny* event);
240
static gboolean enter_notify_event_cb(GtkWidget* widget,
241
                                      GdkEventCrossing* event);
242
static gboolean leave_notify_event_cb(GtkWidget* widget,
243
                                      GdkEventCrossing* event);
244
static gboolean motion_notify_event_cb(GtkWidget* widget,
245
                                       GdkEventMotion* event);
246
MOZ_CAN_RUN_SCRIPT static gboolean button_press_event_cb(GtkWidget* widget,
247
                                                         GdkEventButton* event);
248
static gboolean button_release_event_cb(GtkWidget* widget,
249
                                        GdkEventButton* event);
250
static gboolean focus_in_event_cb(GtkWidget* widget, GdkEventFocus* event);
251
static gboolean focus_out_event_cb(GtkWidget* widget, GdkEventFocus* event);
252
static gboolean key_press_event_cb(GtkWidget* widget, GdkEventKey* event);
253
static gboolean key_release_event_cb(GtkWidget* widget, GdkEventKey* event);
254
static gboolean property_notify_event_cb(GtkWidget* widget,
255
                                         GdkEventProperty* event);
256
static gboolean scroll_event_cb(GtkWidget* widget, GdkEventScroll* event);
257
258
static void hierarchy_changed_cb(GtkWidget* widget,
259
                                 GtkWidget* previous_toplevel);
260
static gboolean window_state_event_cb(GtkWidget* widget,
261
                                      GdkEventWindowState* event);
262
static void settings_changed_cb(GtkSettings* settings, GParamSpec* pspec,
263
                                nsWindow* data);
264
static void settings_xft_dpi_changed_cb(GtkSettings* settings,
265
                                        GParamSpec* pspec, nsWindow* data);
266
static void check_resize_cb(GtkContainer* container, gpointer user_data);
267
static void screen_composited_changed_cb(GdkScreen* screen, gpointer user_data);
268
static void widget_composited_changed_cb(GtkWidget* widget, gpointer user_data);
269
270
static void scale_changed_cb(GtkWidget* widget, GParamSpec* aPSpec,
271
                             gpointer aPointer);
272
static gboolean touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent);
273
static gboolean generic_event_cb(GtkWidget* widget, GdkEvent* aEvent);
274
275
static nsWindow* GetFirstNSWindowForGDKWindow(GdkWindow* aGdkWindow);
276
277
#ifdef __cplusplus
278
extern "C" {
279
#endif /* __cplusplus */
280
#ifdef MOZ_X11
281
static GdkFilterReturn popup_take_focus_filter(GdkXEvent* gdk_xevent,
282
                                               GdkEvent* event, gpointer data);
283
#endif /* MOZ_X11 */
284
#ifdef __cplusplus
285
}
286
#endif /* __cplusplus */
287
288
static gboolean drag_motion_event_cb(GtkWidget* aWidget,
289
                                     GdkDragContext* aDragContext, gint aX,
290
                                     gint aY, guint aTime, gpointer aData);
291
static void drag_leave_event_cb(GtkWidget* aWidget,
292
                                GdkDragContext* aDragContext, guint aTime,
293
                                gpointer aData);
294
static gboolean drag_drop_event_cb(GtkWidget* aWidget,
295
                                   GdkDragContext* aDragContext, gint aX,
296
                                   gint aY, guint aTime, gpointer aData);
297
static void drag_data_received_event_cb(GtkWidget* aWidget,
298
                                        GdkDragContext* aDragContext, gint aX,
299
                                        gint aY,
300
                                        GtkSelectionData* aSelectionData,
301
                                        guint aInfo, guint32 aTime,
302
                                        gpointer aData);
303
304
/* initialization static functions */
305
static nsresult initialize_prefs(void);
306
307
static guint32 sLastUserInputTime = GDK_CURRENT_TIME;
308
static guint32 sRetryGrabTime;
309
310
static SystemTimeConverter<guint32>& TimeConverter() {
311
  static SystemTimeConverter<guint32> sTimeConverterSingleton;
312
  return sTimeConverterSingleton;
313
}
314
315
nsWindow::GtkWindowDecoration nsWindow::sGtkWindowDecoration =
316
    GTK_DECORATION_UNKNOWN;
317
bool nsWindow::sTransparentMainWindow = false;
318
static bool sIgnoreChangedSettings = false;
319
320
void nsWindow::WithSettingsChangesIgnored(const std::function<void()>& aFn) {
321
  AutoRestore ar(sIgnoreChangedSettings);
322
  sIgnoreChangedSettings = true;
323
  aFn();
324
}
325
326
namespace mozilla {
327
328
class CurrentX11TimeGetter {
329
 public:
330
  explicit CurrentX11TimeGetter(GdkWindow* aWindow)
331
      : mWindow(aWindow), mAsyncUpdateStart() {}
332
333
  guint32 GetCurrentTime() const { return gdk_x11_get_server_time(mWindow); }
334
335
  void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {
336
    // Check for in-flight request
337
    if (!mAsyncUpdateStart.IsNull()) {
338
      return;
339
    }
340
    mAsyncUpdateStart = aNow;
341
342
    Display* xDisplay = GDK_WINDOW_XDISPLAY(mWindow);
343
    Window xWindow = GDK_WINDOW_XID(mWindow);
344
    unsigned char c = 'a';
345
    Atom timeStampPropAtom = TimeStampPropAtom();
346
    XChangeProperty(xDisplay, xWindow, timeStampPropAtom, timeStampPropAtom, 8,
347
                    PropModeReplace, &c, 1);
348
    XFlush(xDisplay);
349
  }
350
351
  gboolean PropertyNotifyHandler(GtkWidget* aWidget, GdkEventProperty* aEvent) {
352
    if (aEvent->atom != gdk_x11_xatom_to_atom(TimeStampPropAtom())) {
353
      return FALSE;
354
    }
355
356
    guint32 eventTime = aEvent->time;
357
    TimeStamp lowerBound = mAsyncUpdateStart;
358
359
    TimeConverter().CompensateForBackwardsSkew(eventTime, lowerBound);
360
    mAsyncUpdateStart = TimeStamp();
361
    return TRUE;
362
  }
363
364
 private:
365
  static Atom TimeStampPropAtom() {
366
    return gdk_x11_get_xatom_by_name_for_display(gdk_display_get_default(),
367
                                                 "GDK_TIMESTAMP_PROP");
368
  }
369
370
  // This is safe because this class is stored as a member of mWindow and
371
  // won't outlive it.
372
  GdkWindow* mWindow;
373
  TimeStamp mAsyncUpdateStart;
374
};
375
376
}  // namespace mozilla
377
378
static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID);
379
380
// The window from which the focus manager asks us to dispatch key events.
381
static nsWindow* gFocusWindow = nullptr;
382
static bool gBlockActivateEvent = false;
383
static bool gGlobalsInitialized = false;
384
static bool gRaiseWindows = true;
385
static bool gUseWaylandVsync = true;
386
static bool gUseAspectRatio = true;
387
static GList* gVisibleWaylandPopupWindows = nullptr;
388
static uint32_t gLastTouchID = 0;
389
390
#define NS_WINDOW_TITLE_MAX_LENGTH 4095
391
392
// If after selecting profile window, the startup fail, please refer to
393
// http://bugzilla.gnome.org/show_bug.cgi?id=88940
394
395
// needed for imgIContainer cursors
396
// GdkDisplay* was added in 2.2
397
typedef struct _GdkDisplay GdkDisplay;
398
399
#define kWindowPositionSlop 20
400
401
// cursor cache
402
static GdkCursor* gCursorCache[eCursorCount];
403
404
static GtkWidget* gInvisibleContainer = nullptr;
405
406
// Sometimes this actually also includes the state of the modifier keys, but
407
// only the button state bits are used.
408
static guint gButtonState;
409
410
static inline int32_t GetBitmapStride(int32_t width) {
411
#if defined(MOZ_X11)
412
  return (width + 7) / 8;
413
#else
414
  return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
415
#endif
416
}
417
418
static inline bool TimestampIsNewerThan(guint32 a, guint32 b) {
419
  // Timestamps are just the least significant bits of a monotonically
420
  // increasing function, and so the use of unsigned overflow arithmetic.
421
  return a - b <= G_MAXUINT32 / 2;
422
}
423
424
static void UpdateLastInputEventTime(void* aGdkEvent) {
425
  nsCOMPtr<nsIUserIdleServiceInternal> idleService =
426
      do_GetService("@mozilla.org/widget/useridleservice;1");
427
  if (idleService) {
428
    idleService->ResetIdleTimeOut(0);
429
  }
430
431
  guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
432
  if (timestamp == GDK_CURRENT_TIME) return;
433
434
  sLastUserInputTime = timestamp;
435
}
436
437
void GetWindowOrigin(GdkWindow* aWindow, int* aX, int* aY) {
438
  if (aWindow) {
439
    gdk_window_get_origin(aWindow, aX, aY);
440
  }
441
442
  // TODO(bug 1655924): gdk_window_get_origin is can block waiting for the x
443
  // server for a long time, we would like to use the implementation below
444
  // instead. However, removing the synchronous x server queries causes a race
445
  // condition to surface, causing issues such as bug 1652743 and bug 1653711.
446
#if 0
447
  *aX = 0;
448
  *aY = 0;
449
  if (!aWindow) {
450
    return;
451
  }
452
453
  GdkWindow* current = aWindow;
454
  while (GdkWindow* parent = gdk_window_get_parent(current)) {
455
    if (parent == current) {
456
      break;
457
    }
458
459
    int x = 0;
460
    int y = 0;
461
    gdk_window_get_position(current, &x, &y);
462
    *aX += x;
463
    *aY += y;
464
465
    current = parent;
466
  }
467
#endif
468
}
469
470
nsWindow::nsWindow() {
471
  mIsTopLevel = false;
472
  mIsDestroyed = false;
473
  mListenForResizes = false;
474
  mNeedsDispatchResized = false;
475
  mIsShown = false;
476
  mNeedsShow = false;
477
  mEnabled = true;
478
  mCreated = false;
479
  mHandleTouchEvent = false;
480
  mIsDragPopup = false;
481
  mIsX11Display = gfxPlatformGtk::GetPlatform()->IsX11Display();
482
483
  mContainer = nullptr;
484
  mGdkWindow = nullptr;
485
  mShell = nullptr;
486
  mCompositorWidgetDelegate = nullptr;
487
  mHasMappedToplevel = false;
488
  mRetryPointerGrab = false;
489
  mWindowType = eWindowType_child;
490
  mSizeState = nsSizeMode_Normal;
491
  mBoundsAreValid = true;
492
  mAspectRatio = 0.0f;
493
  mAspectRatioSaved = 0.0f;
494
  mLastSizeMode = nsSizeMode_Normal;
495
  mSizeConstraints.mMaxSize = GetSafeWindowSize(mSizeConstraints.mMaxSize);
496
497
#ifdef MOZ_X11
498
  mOldFocusWindow = 0;
499
500
  mXDisplay = nullptr;
501
  mXWindow = X11None;
502
  mXVisual = nullptr;
503
  mXDepth = 0;
504
#endif /* MOZ_X11 */
505
506
#ifdef MOZ_WAYLAND
507
  mNeedsCompositorResume = false;
508
  mCompositorInitiallyPaused = false;
509
  mNativePointerLockCenter = LayoutDeviceIntPoint();
510
  mRelativePointer = nullptr;
511
  mLockedPointer = nullptr;
512
#endif
513
  mWaitingForMoveToRectCB = false;
514
  mPendingSizeRect = LayoutDeviceIntRect(0, 0, 0, 0);
515
516
  if (!gGlobalsInitialized) {
517
    gGlobalsInitialized = true;
518
519
    // It's OK if either of these fail, but it may not be one day.
520
    initialize_prefs();
521
522
#ifdef MOZ_WAYLAND
523
    // Wayland provides clipboard data to application on focus-in event
524
    // so we need to init our clipboard hooks before we create window
525
    // and get focus.
526
    if (!mIsX11Display) {
527
      nsCOMPtr<nsIClipboard> clipboard =
528
          do_GetService("@mozilla.org/widget/clipboard;1");
529
      NS_ASSERTION(clipboard, "Failed to init clipboard!");
530
    }
531
#endif
532
  }
533
534
  mLastMotionPressure = 0;
535
536
#ifdef ACCESSIBILITY
537
  mRootAccessible = nullptr;
538
#endif
539
540
  mIsTransparent = false;
541
  mTransparencyBitmap = nullptr;
542
  mTransparencyBitmapForTitlebar = false;
543
544
  mTransparencyBitmapWidth = 0;
545
  mTransparencyBitmapHeight = 0;
546
547
  mLastScrollEventTime = GDK_CURRENT_TIME;
548
549
  mPendingConfigures = 0;
550
  mGtkWindowDecoration = GTK_DECORATION_NONE;
551
  mDrawToContainer = false;
552
  mDrawInTitlebar = false;
553
  mTitlebarBackdropState = false;
554
555
  mHasAlphaVisual = false;
556
  mIsWaylandPanelWindow = false;
557
  mIsPIPWindow = false;
558
  mAlwaysOnTop = false;
559
560
  mWindowScaleFactorChanged = true;
561
  mWindowScaleFactor = 1;
562
563
  mCompositedScreen = gdk_screen_is_composited(gdk_screen_get_default());
564
}
565
566
nsWindow::~nsWindow() {
567
  LOG(("nsWindow::~nsWindow() [%p]\n", (void*)this));
568
569
  delete[] mTransparencyBitmap;
570
  mTransparencyBitmap = nullptr;
571
572
  Destroy();
573
}
574
575
/* static */
576
void nsWindow::ReleaseGlobals() {
577
  for (auto& cursor : gCursorCache) {
578
    if (cursor) {
579
      g_object_unref(cursor);
580
      cursor = nullptr;
581
    }
582
  }
583
}
584
585
void nsWindow::CommonCreate(nsIWidget* aParent, bool aListenForResizes) {
586
  mParent = aParent;
587
  mListenForResizes = aListenForResizes;
588
  mCreated = true;
589
}
590
591
void nsWindow::DispatchActivateEvent(void) {
592
  NS_ASSERTION(mContainer || mIsDestroyed,
593
               "DispatchActivateEvent only intended for container windows");
594
595
#ifdef ACCESSIBILITY
596
  DispatchActivateEventAccessible();
597
#endif  // ACCESSIBILITY
598
599
  if (mWidgetListener) mWidgetListener->WindowActivated();
600
}
601
602
void nsWindow::DispatchDeactivateEvent(void) {
603
  if (mWidgetListener) mWidgetListener->WindowDeactivated();
604
605
#ifdef ACCESSIBILITY
606
  DispatchDeactivateEventAccessible();
607
#endif  // ACCESSIBILITY
608
}
609
610
void nsWindow::DispatchResized() {
611
  mNeedsDispatchResized = false;
612
  if (mWidgetListener) {
613
    mWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
614
  }
615
  if (mAttachedWidgetListener) {
616
    mAttachedWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
617
  }
618
}
619
620
void nsWindow::MaybeDispatchResized() {
621
  if (mNeedsDispatchResized && !mIsDestroyed) {
622
    DispatchResized();
623
  }
624
}
625
626
nsIWidgetListener* nsWindow::GetListener() {
627
  return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener;
628
}
629
630
nsresult nsWindow::DispatchEvent(WidgetGUIEvent* aEvent,
631
                                 nsEventStatus& aStatus) {
632
#ifdef DEBUG
633
  debug_DumpEvent(stdout, aEvent->mWidget, aEvent, "something", 0);
634
#endif
635
  aStatus = nsEventStatus_eIgnore;
636
  nsIWidgetListener* listener = GetListener();
637
  if (listener) {
638
    aStatus = listener->HandleEvent(aEvent, mUseAttachedEvents);
639
  }
640
641
  return NS_OK;
642
}
643
644
void nsWindow::OnDestroy(void) {
645
  if (mOnDestroyCalled) return;
646
647
  mOnDestroyCalled = true;
648
649
  // Prevent deletion.
650
  nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
651
652
  // release references to children, device context, toolkit + app shell
653
  nsBaseWidget::OnDestroy();
654
655
  // Remove association between this object and its parent and siblings.
656
  nsBaseWidget::Destroy();
657
  mParent = nullptr;
658
659
  NotifyWindowDestroyed();
660
}
661
662
bool nsWindow::AreBoundsSane() {
663
  return mBounds.width > 0 && mBounds.height > 0;
664
}
665
666
static GtkWidget* EnsureInvisibleContainer() {
667
  if (!gInvisibleContainer) {
668
    // GtkWidgets need to be anchored to a GtkWindow to be realized (to
669
    // have a window).  Using GTK_WINDOW_POPUP rather than
670
    // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
671
    // initialization and window manager interaction.
672
    GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
673
    gInvisibleContainer = moz_container_new();
674
    gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer);
675
    gtk_widget_realize(gInvisibleContainer);
676
  }
677
  return gInvisibleContainer;
678
}
679
680
static void CheckDestroyInvisibleContainer() {
681
  MOZ_ASSERT(gInvisibleContainer, "oh, no");
682
683
  if (!gdk_window_peek_children(gtk_widget_get_window(gInvisibleContainer))) {
684
    // No children, so not in use.
685
    // Make sure to destroy the GtkWindow also.
686
    gtk_widget_destroy(gtk_widget_get_parent(gInvisibleContainer));
687
    gInvisibleContainer = nullptr;
688
  }
689
}
690
691
// Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging
692
// to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of
693
// the GdkWindow hierarchy to aNewWidget.
694
static void SetWidgetForHierarchy(GdkWindow* aWindow, GtkWidget* aOldWidget,
695
                                  GtkWidget* aNewWidget) {
696
  gpointer data;
697
  gdk_window_get_user_data(aWindow, &data);
698
699
  if (data != aOldWidget) {
700
    if (!GTK_IS_WIDGET(data)) return;
701
702
    auto* widget = static_cast<GtkWidget*>(data);
703
    if (gtk_widget_get_parent(widget) != aOldWidget) return;
704
705
    // This window belongs to a child widget, which will no longer be a
706
    // child of aOldWidget.
707
    gtk_widget_reparent(widget, aNewWidget);
708
709
    return;
710
  }
711
712
  GList* children = gdk_window_get_children(aWindow);
713
  for (GList* list = children; list; list = list->next) {
714
    SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget);
715
  }
716
  g_list_free(children);
717
718
  gdk_window_set_user_data(aWindow, aNewWidget);
719
}
720
721
// Walk the list of child windows and call destroy on them.
722
void nsWindow::DestroyChildWindows() {
723
  if (!mGdkWindow) return;
724
725
  while (GList* children = gdk_window_peek_children(mGdkWindow)) {
726
    GdkWindow* child = GDK_WINDOW(children->data);
727
    nsWindow* kid = get_window_for_gdk_window(child);
728
    if (kid) {
729
      kid->Destroy();
730
    } else {
731
      // This child is not an nsWindow.
732
      // Destroy the child GtkWidget.
733
      gpointer data;
734
      gdk_window_get_user_data(child, &data);
735
      if (GTK_IS_WIDGET(data)) {
736
        gtk_widget_destroy(static_cast<GtkWidget*>(data));
737
      }
738
    }
739
  }
740
}
741
742
void nsWindow::Destroy() {
743
  if (mIsDestroyed || !mCreated) return;
744
745
  LOG(("nsWindow::Destroy [%p]\n", (void*)this));
746
  mIsDestroyed = true;
747
  mCreated = false;
748
749
  /** Need to clean our LayerManager up while still alive */
750
  if (mLayerManager) {
751
    mLayerManager->Destroy();
752
  }
753
  mLayerManager = nullptr;
754
755
#ifdef MOZ_WAYLAND
756
  // Shut down our local vsync source
757
  if (mWaylandVsyncSource) {
758
    mWaylandVsyncSource->Shutdown();
759
    mWaylandVsyncSource = nullptr;
760
  }
761
#endif
762
763
  // It is safe to call DestroyeCompositor several times (here and
764
  // in the parent class) since it will take effect only once.
765
  // The reason we call it here is because on gtk platforms we need
766
  // to destroy the compositor before we destroy the gdk window (which
767
  // destroys the the gl context attached to it).
768
  DestroyCompositor();
769
770
#ifdef MOZ_X11
771
  // Ensure any resources assigned to the window get cleaned up first
772
  // to avoid double-freeing.
773
  mSurfaceProvider.CleanupResources();
774
#endif
775
776
  ClearCachedResources();
777
778
  g_signal_handlers_disconnect_by_data(gtk_settings_get_default(), this);
779
780
  nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
781
  if (rollupListener) {
782
    nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
783
    if (static_cast<nsIWidget*>(this) == rollupWidget) {
784
      rollupListener->Rollup(0, false, nullptr, nullptr);
785
    }
786
  }
787
788
  // dragService will be null after shutdown of the service manager.
789
  RefPtr<nsDragService> dragService = nsDragService::GetInstance();
790
  if (dragService && this == dragService->GetMostRecentDestWindow()) {
791
    dragService->ScheduleLeaveEvent();
792
  }
793
794
  NativeShow(false);
795
796
  if (mIMContext) {
797
    mIMContext->OnDestroyWindow(this);
798
  }
799
800
  // make sure that we remove ourself as the focus window
801
  if (gFocusWindow == this) {
802
    LOGFOCUS(("automatically losing focus...\n"));
803
    gFocusWindow = nullptr;
804
  }
805
806
  GtkWidget* owningWidget = GetMozContainerWidget();
807
  if (mShell) {
808
    gtk_widget_destroy(mShell);
809
    mShell = nullptr;
810
    mContainer = nullptr;
811
    MOZ_ASSERT(!mGdkWindow,
812
               "mGdkWindow should be NULL when mContainer is destroyed");
813
  } else if (mContainer) {
814
    gtk_widget_destroy(GTK_WIDGET(mContainer));
815
    mContainer = nullptr;
816
    MOZ_ASSERT(!mGdkWindow,
817
               "mGdkWindow should be NULL when mContainer is destroyed");
818
  } else if (mGdkWindow) {
819
    // Destroy child windows to ensure that their mThebesSurfaces are
820
    // released and to remove references from GdkWindows back to their
821
    // container widget.  (OnContainerUnrealize() does this when the
822
    // MozContainer widget is destroyed.)
823
    DestroyChildWindows();
824
825
    gdk_window_set_user_data(mGdkWindow, nullptr);
826
    g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
827
    gdk_window_destroy(mGdkWindow);
828
    mGdkWindow = nullptr;
829
  }
830
831
  if (gInvisibleContainer && owningWidget == gInvisibleContainer) {
832
    CheckDestroyInvisibleContainer();
833
  }
834
835
#ifdef ACCESSIBILITY
836
  if (mRootAccessible) {
837
    mRootAccessible = nullptr;
838
  }
839
#endif
840
841
  // Save until last because OnDestroy() may cause us to be deleted.
842
  OnDestroy();
843
}
844
845
nsIWidget* nsWindow::GetParent(void) { return mParent; }
846
847
float nsWindow::GetDPI() {
848
  float dpi = 96.0f;
849
  nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
850
  if (screen) {
851
    screen->GetDpi(&dpi);
852
  }
853
  return dpi;
854
}
855
856
double nsWindow::GetDefaultScaleInternal() {
857
  return GdkScaleFactor() * gfxPlatformGtk::GetFontScaleFactor();
858
}
859
860
DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScale() {
861
#ifdef MOZ_WAYLAND
862
  if (!mIsX11Display) {
863
    return DesktopToLayoutDeviceScale(GdkScaleFactor());
864
  }
865
#endif
866
867
  // In Gtk/X11, we manage windows using device pixels.
868
  return DesktopToLayoutDeviceScale(1.0);
869
}
870
871
DesktopToLayoutDeviceScale nsWindow::GetDesktopToDeviceScaleByScreen() {
872
#ifdef MOZ_WAYLAND
873
  // In Wayland there's no way to get absolute position of the window and use it
874
  // to determine the screen factor of the monitor on which the window is
875
  // placed. The window is notified of the current scale factor but not at this
876
  // point, so the GdkScaleFactor can return wrong value which can lead to wrong
877
  // popup placement. We need to use parent's window scale factor for the new
878
  // one.
879
  if (!mIsX11Display) {
880
    nsView* view = nsView::GetViewFor(this);
881
    if (view) {
882
      nsView* parentView = view->GetParent();
883
      if (parentView) {
884
        nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr);
885
        if (parentWidget) {
886
          return DesktopToLayoutDeviceScale(
887
              parentWidget->RoundsWidgetCoordinatesTo());
888
        }
889
        NS_WARNING("Widget has no parent");
890
      }
891
    } else {
892
      NS_WARNING("Cannot find widget view");
893
    }
894
  }
895
#endif
896
  return nsBaseWidget::GetDesktopToDeviceScale();
897
}
898
899
void nsWindow::SetParent(nsIWidget* aNewParent) {
900
  if (!mGdkWindow) {
901
    MOZ_ASSERT_UNREACHABLE("The native window has already been destroyed");
902
    return;
903
  }
904
905
  if (mContainer) {
906
    // FIXME bug 1469183
907
    NS_ERROR("nsWindow should not have a container here");
908
    return;
909
  }
910
911
  nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
912
  if (mParent) {
913
    mParent->RemoveChild(this);
914
  }
915
  mParent = aNewParent;
916
917
  GtkWidget* oldContainer = GetMozContainerWidget();
918
  if (!oldContainer) {
919
    // The GdkWindows have been destroyed so there is nothing else to
920
    // reparent.
921
    MOZ_ASSERT(gdk_window_is_destroyed(mGdkWindow),
922
               "live GdkWindow with no widget");
923
    return;
924
  }
925
926
  nsWindow* newParent = static_cast<nsWindow*>(aNewParent);
927
  GdkWindow* newParentWindow = nullptr;
928
  GtkWidget* newContainer = nullptr;
929
  if (aNewParent) {
930
    aNewParent->AddChild(this);
931
    newParentWindow = newParent->mGdkWindow;
932
    newContainer = newParent->GetMozContainerWidget();
933
  } else {
934
    // aNewParent is nullptr, but reparent to a hidden window to avoid
935
    // destroying the GdkWindow and its descendants.
936
    // An invisible container widget is needed to hold descendant
937
    // GtkWidgets.
938
    newContainer = EnsureInvisibleContainer();
939
    newParentWindow = gtk_widget_get_window(newContainer);
940
  }
941
942
  if (!newContainer) {
943
    // The new parent GdkWindow has been destroyed.
944
    MOZ_ASSERT(!newParentWindow || gdk_window_is_destroyed(newParentWindow),
945
               "live GdkWindow with no widget");
946
    Destroy();
947
  } else {
948
    if (newContainer != oldContainer) {
949
      MOZ_ASSERT(!gdk_window_is_destroyed(newParentWindow),
950
                 "destroyed GdkWindow with widget");
951
      SetWidgetForHierarchy(mGdkWindow, oldContainer, newContainer);
952
953
      if (oldContainer == gInvisibleContainer) {
954
        CheckDestroyInvisibleContainer();
955
      }
956
    }
957
958
    gdk_window_reparent(mGdkWindow, newParentWindow,
959
                        DevicePixelsToGdkCoordRoundDown(mBounds.x),
960
                        DevicePixelsToGdkCoordRoundDown(mBounds.y));
961
  }
962
963
  bool parentHasMappedToplevel = newParent && newParent->mHasMappedToplevel;
964
  if (mHasMappedToplevel != parentHasMappedToplevel) {
965
    SetHasMappedToplevel(parentHasMappedToplevel);
966
  }
967
}
968
969
bool nsWindow::WidgetTypeSupportsAcceleration() {
970
  if (IsSmallPopup()) {
971
    return false;
972
  }
973
  // Workaround for Bug 1479135
974
  // We draw transparent popups on non-compositing screens by SW as we don't
975
  // implement X shape masks in WebRender.
976
  if (mWindowType == eWindowType_popup) {
977
    return mCompositedScreen;
978
  }
979
  return true;
980
}
981
982
void nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) {
983
  MOZ_ASSERT(aNewParent, "null widget");
984
  MOZ_ASSERT(!mIsDestroyed, "");
985
  MOZ_ASSERT(!static_cast<nsWindow*>(aNewParent)->mIsDestroyed, "");
986
  MOZ_ASSERT(!gdk_window_is_destroyed(mGdkWindow),
987
             "destroyed GdkWindow with widget");
988
989
  MOZ_ASSERT(
990
      !mParent,
991
      "nsWindow::ReparentNativeWidget() works on toplevel windows only.");
992
993
  auto* newParent = static_cast<nsWindow*>(aNewParent);
994
  GtkWindow* newParentWidget = GTK_WINDOW(newParent->GetGtkWidget());
995
  GtkWindow* shell = GTK_WINDOW(mShell);
996
997
  if (shell && gtk_window_get_transient_for(shell)) {
998
    gtk_window_set_transient_for(shell, newParentWidget);
999
  }
1000
}
1001
1002
void nsWindow::SetModal(bool aModal) {
1003
  LOG(("nsWindow::SetModal [%p] %d\n", (void*)this, aModal));
1004
  if (mIsDestroyed) return;
1005
  if (!mIsTopLevel || !mShell) return;
1006
  gtk_window_set_modal(GTK_WINDOW(mShell), aModal ? TRUE : FALSE);
1007
}
1008
1009
// nsIWidget method, which means IsShown.
1010
bool nsWindow::IsVisible() const { return mIsShown; }
1011
1012
void nsWindow::RegisterTouchWindow() {
1013
  mHandleTouchEvent = true;
1014
  mTouches.Clear();
1015
}
1016
1017
void nsWindow::ConstrainPosition(bool aAllowSlop, int32_t* aX, int32_t* aY) {
1018
  if (!mIsTopLevel || !mShell) return;
1019
1020
  double dpiScale = GetDefaultScale().scale;
1021
1022
  // we need to use the window size in logical screen pixels
1023
  int32_t logWidth = std::max(NSToIntRound(mBounds.width / dpiScale), 1);
1024
  int32_t logHeight = std::max(NSToIntRound(mBounds.height / dpiScale), 1);
1025
1026
  /* get our playing field. use the current screen, or failing that
1027
    for any reason, use device caps for the default screen. */
1028
  nsCOMPtr<nsIScreen> screen;
1029
  nsCOMPtr<nsIScreenManager> screenmgr =
1030
      do_GetService("@mozilla.org/gfx/screenmanager;1");
1031
  if (screenmgr) {
1032
    screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight,
1033
                             getter_AddRefs(screen));
1034
  }
1035
1036
  // We don't have any screen so leave the coordinates as is
1037
  if (!screen) return;
1038
1039
  nsIntRect screenRect;
1040
  if (mSizeMode != nsSizeMode_Fullscreen) {
1041
    // For normalized windows, use the desktop work area.
1042
    screen->GetAvailRectDisplayPix(&screenRect.x, &screenRect.y,
1043
                                   &screenRect.width, &screenRect.height);
1044
  } else {
1045
    // For full screen windows, use the desktop.
1046
    screen->GetRectDisplayPix(&screenRect.x, &screenRect.y, &screenRect.width,
1047
                              &screenRect.height);
1048
  }
1049
1050
  if (aAllowSlop) {
1051
    if (*aX < screenRect.x - logWidth + kWindowPositionSlop)
1052
      *aX = screenRect.x - logWidth + kWindowPositionSlop;
1053
    else if (*aX >= screenRect.XMost() - kWindowPositionSlop)
1054
      *aX = screenRect.XMost() - kWindowPositionSlop;
1055
1056
    if (*aY < screenRect.y - logHeight + kWindowPositionSlop)
1057
      *aY = screenRect.y - logHeight + kWindowPositionSlop;
1058
    else if (*aY >= screenRect.YMost() - kWindowPositionSlop)
1059
      *aY = screenRect.YMost() - kWindowPositionSlop;
1060
  } else {
1061
    if (*aX < screenRect.x)
1062
      *aX = screenRect.x;
1063
    else if (*aX >= screenRect.XMost() - logWidth)
1064
      *aX = screenRect.XMost() - logWidth;
1065
1066
    if (*aY < screenRect.y)
1067
      *aY = screenRect.y;
1068
    else if (*aY >= screenRect.YMost() - logHeight)
1069
      *aY = screenRect.YMost() - logHeight;
1070
  }
1071
}
1072
1073
void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) {
1074
  mSizeConstraints.mMinSize = GetSafeWindowSize(aConstraints.mMinSize);
1075
  mSizeConstraints.mMaxSize = GetSafeWindowSize(aConstraints.mMaxSize);
1076
1077
  ApplySizeConstraints();
1078
}
1079
1080
void nsWindow::AddCSDDecorationSize(int* aWidth, int* aHeight) {
1081
  if (mSizeState == nsSizeMode_Normal &&
1082
      mGtkWindowDecoration == GTK_DECORATION_CLIENT && mDrawInTitlebar) {
1083
    GtkBorder decorationSize = GetCSDDecorationSize(!mIsTopLevel);
1084
    *aWidth += decorationSize.left + decorationSize.right;
1085
    *aHeight += decorationSize.top + decorationSize.bottom;
1086
  }
1087
}
1088
1089
#ifdef MOZ_WAYLAND
1090
bool nsWindow::GetCSDDecorationOffset(int* aDx, int* aDy) {
1091
  if (mSizeState == nsSizeMode_Normal &&
1092
      mGtkWindowDecoration == GTK_DECORATION_CLIENT && mDrawInTitlebar) {
1093
    GtkBorder decorationSize = GetCSDDecorationSize(!mIsTopLevel);
1094
    *aDx = decorationSize.left;
1095
    *aDy = decorationSize.top;
1096
    return true;
1097
  } else {
1098
    return false;
1099
  }
1100
}
1101
#endif
1102
1103
void nsWindow::ApplySizeConstraints(void) {
1104
  if (mShell) {
1105
    GdkGeometry geometry;
1106
    geometry.min_width =
1107
        DevicePixelsToGdkCoordRoundUp(mSizeConstraints.mMinSize.width);
1108
    geometry.min_height =
1109
        DevicePixelsToGdkCoordRoundUp(mSizeConstraints.mMinSize.height);
1110
    geometry.max_width =
1111
        DevicePixelsToGdkCoordRoundDown(mSizeConstraints.mMaxSize.width);
1112
    geometry.max_height =
1113
        DevicePixelsToGdkCoordRoundDown(mSizeConstraints.mMaxSize.height);
1114
1115
    uint32_t hints = 0;
1116
    if (mSizeConstraints.mMinSize != LayoutDeviceIntSize(0, 0)) {
1117
      if (!mIsX11Display) {
1118
        gtk_widget_set_size_request(GTK_WIDGET(mContainer), geometry.min_width,
1119
                                    geometry.min_height);
1120
      }
1121
      AddCSDDecorationSize(&geometry.min_width, &geometry.min_height);
1122
      hints |= GDK_HINT_MIN_SIZE;
1123
      LOG(("nsWindow::ApplySizeConstraints [%p] min size %d %d\n", (void*)this,
1124
           geometry.min_width, geometry.min_height));
1125
    }
1126
    if (mSizeConstraints.mMaxSize !=
1127
        LayoutDeviceIntSize(NS_MAXSIZE, NS_MAXSIZE)) {
1128
      AddCSDDecorationSize(&geometry.max_width, &geometry.max_height);
1129
      hints |= GDK_HINT_MAX_SIZE;
1130
      LOG(("nsWindow::ApplySizeConstraints [%p] max size %d %d\n", (void*)this,
1131
           geometry.max_width, geometry.max_height));
1132
    }
1133
1134
    if (mAspectRatio != 0.0f) {
1135
      geometry.min_aspect = mAspectRatio;
1136
      geometry.max_aspect = mAspectRatio;
1137
      hints |= GDK_HINT_ASPECT;
1138
    }
1139
1140
    gtk_window_set_geometry_hints(GTK_WINDOW(mShell), nullptr, &geometry,
1141
                                  GdkWindowHints(hints));
1142
  }
1143
}
1144
1145
void nsWindow::Show(bool aState) {
1146
  if (aState == mIsShown) return;
1147
1148
  // Clear our cached resources when the window is hidden.
1149
  if (mIsShown && !aState) {
1150
    ClearCachedResources();
1151
  }
1152
1153
  mIsShown = aState;
1154
1155
  LOG(("nsWindow::Show [%p] state %d\n", (void*)this, aState));
1156
1157
  if (aState) {
1158
    // Now that this window is shown, mHasMappedToplevel needs to be
1159
    // tracked on viewable descendants.
1160
    SetHasMappedToplevel(mHasMappedToplevel);
1161
  }
1162
1163
  // Ok, someone called show on a window that isn't sized to a sane
1164
  // value.  Mark this window as needing to have Show() called on it
1165
  // and return.
1166
  if ((aState && !AreBoundsSane()) || !mCreated) {
1167
    LOG(("\tbounds are insane or window hasn't been created yet\n"));
1168
    mNeedsShow = true;
1169
    return;
1170
  }
1171
1172
  // If someone is hiding this widget, clear any needing show flag.
1173
  if (!aState) mNeedsShow = false;
1174
1175
#ifdef ACCESSIBILITY
1176
  if (aState && a11y::ShouldA11yBeEnabled()) CreateRootAccessible();
1177
#endif
1178
1179
  NativeShow(aState);
1180
}
1181
1182
void nsWindow::ResizeInt(int aX, int aY, int aWidth, int aHeight, bool aMove,
1183
                         bool aRepaint) {
1184
  LOG(("nsWindow::ResizeInt [%p] x:%d y:%d -> w:%d h:%d repaint %d aMove %d\n",
1185
       (void*)this, aX, aY, aWidth, aHeight, aRepaint, aMove));
1186
1187
  ConstrainSize(&aWidth, &aHeight);
1188
1189
  LOG(("  ConstrainSize: w:%d h;%d\n", aWidth, aHeight));
1190
1191
  // If we used to have insane bounds, we may have skipped actually positioning
1192
  // the widget in NativeMoveResizeWaylandPopup, in which case we need to
1193
  // actually position it now as well.
1194
  const bool hadInsaneWaylandPopupDimensions =
1195
      !AreBoundsSane() && IsWaylandPopup();
1196
1197
  if (aMove) {
1198
    mBounds.x = aX;
1199
    mBounds.y = aY;
1200
  }
1201
1202
  // For top-level windows, aWidth and aHeight should possibly be
1203
  // interpreted as frame bounds, but NativeResize treats these as window
1204
  // bounds (Bug 581866).
1205
  mBounds.SizeTo(aWidth, aHeight);
1206
1207
  // We set correct mBounds in advance here. This can be invalided by state
1208
  // event.
1209
  mBoundsAreValid = true;
1210
1211
  // Recalculate aspect ratio when resized from DOM
1212
  if (mAspectRatio != 0.0) {
1213
    LockAspectRatio(true);
1214
  }
1215
1216
  if (!mCreated) return;
1217
1218
  if (aMove || mPreferredPopupRectFlushed || hadInsaneWaylandPopupDimensions) {
1219
    LOG(("  Need also to move, flushed? %d, bounds were insane: %d\n",
1220
         mPreferredPopupRectFlushed, hadInsaneWaylandPopupDimensions));
1221
    NativeMoveResize();
1222
  } else {
1223
    NativeResize();
1224
  }
1225
1226
  NotifyRollupGeometryChange();
1227
1228
  // send a resize notification if this is a toplevel
1229
  if (mIsTopLevel || mListenForResizes) {
1230
    DispatchResized();
1231
  }
1232
}
1233
1234
void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
1235
  LOG(("nsWindow::Resize [%p] %f %f\n", (void*)this, aWidth, aHeight));
1236
1237
  double scale =
1238
      BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1239
  int32_t width = NSToIntRound(scale * aWidth);
1240
  int32_t height = NSToIntRound(scale * aHeight);
1241
1242
  ResizeInt(0, 0, width, height, /* aMove */ false, aRepaint);
1243
}
1244
1245
void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
1246
                      bool aRepaint) {
1247
  LOG(("nsWindow::Resize [%p] %f %f repaint %d\n", (void*)this, aWidth, aHeight,
1248
       aRepaint));
1249
1250
  double scale =
1251
      BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1252
  int32_t width = NSToIntRound(scale * aWidth);
1253
  int32_t height = NSToIntRound(scale * aHeight);
1254
1255
  int32_t x = NSToIntRound(scale * aX);
1256
  int32_t y = NSToIntRound(scale * aY);
1257
1258
  ResizeInt(x, y, width, height, /* aMove */ true, aRepaint);
1259
}
1260
1261
void nsWindow::Enable(bool aState) { mEnabled = aState; }
1262
1263
bool nsWindow::IsEnabled() const { return mEnabled; }
1264
1265
void nsWindow::Move(double aX, double aY) {
1266
  LOG(("nsWindow::Move [%p] %f %f\n", (void*)this, aX, aY));
1267
1268
  double scale =
1269
      BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1270
  int32_t x = NSToIntRound(aX * scale);
1271
  int32_t y = NSToIntRound(aY * scale);
1272
1273
  if (mWindowType == eWindowType_toplevel ||
1274
      mWindowType == eWindowType_dialog) {
1275
    SetSizeMode(nsSizeMode_Normal);
1276
  }
1277
1278
  // Since a popup window's x/y coordinates are in relation to to
1279
  // the parent, the parent might have moved so we always move a
1280
  // popup window.
1281
  if (x == mBounds.x && y == mBounds.y && mWindowType != eWindowType_popup)
1282
    return;
1283
1284
  // XXX Should we do some AreBoundsSane check here?
1285
1286
  mBounds.x = x;
1287
  mBounds.y = y;
1288
1289
  if (!mCreated) return;
1290
1291
  if (IsWaylandPopup()) {
1292
    int32_t p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
1293
    if (mPreferredPopupRect.x != mBounds.x * p2a &&
1294
        mPreferredPopupRect.y != mBounds.y * p2a) {
1295
      NativeMove();
1296
      NotifyRollupGeometryChange();
1297
    } else {
1298
      LOG(("  mBounds same as mPreferredPopupRect, no need to move"));
1299
    }
1300
  } else {
1301
    NativeMove();
1302
    NotifyRollupGeometryChange();
1303
  }
1304
}
1305
1306
bool nsWindow::IsPopup() {
1307
  return mIsTopLevel && mWindowType == eWindowType_popup;
1308
}
1309
1310
bool nsWindow::IsWaylandPopup() { return !mIsX11Display && IsPopup(); }
1311
1312
void nsWindow::HideWaylandTooltips() {
1313
  while (gVisibleWaylandPopupWindows) {
1314
    nsWindow* window =
1315
        static_cast<nsWindow*>(gVisibleWaylandPopupWindows->data);
1316
    if (window->mPopupType != ePopupTypeTooltip) break;
1317
    LOG(("nsWindow::HideWaylandTooltips [%p] hidding tooltip [%p].\n",
1318
         (void*)this, window));
1319
    window->HideWaylandWindow();
1320
  }
1321
}
1322
1323
void nsWindow::HideWaylandOpenedPopups() {
1324
  while (gVisibleWaylandPopupWindows) {
1325
    nsWindow* window =
1326
        static_cast<nsWindow*>(gVisibleWaylandPopupWindows->data);
1327
    window->HideWaylandWindow();
1328
  }
1329
}
1330
1331
// Hide popup nsWindows which are no longer in the nsXULPopupManager widget
1332
// chain list.
1333
void nsWindow::CleanupWaylandPopups() {
1334
  LOG(("nsWindow::CleanupWaylandPopups...\n"));
1335
  nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
1336
  AutoTArray<nsIWidget*, 5> widgetChain;
1337
  pm->GetSubmenuWidgetChain(&widgetChain);
1338
  GList* popupList = gVisibleWaylandPopupWindows;
1339
  while (popupList) {
1340
    LOG(("  Looking for %p [nsWindow]\n", popupList->data));
1341
    nsWindow* waylandWnd = static_cast<nsWindow*>(popupList->data);
1342
    // Remove only menu popups or empty frames - they are most likely
1343
    // already rolledup popups
1344
    if (waylandWnd->IsMainMenuWindow() || !waylandWnd->GetFrame()) {
1345
      bool popupFound = false;
1346
      for (unsigned long i = 0; i < widgetChain.Length(); i++) {
1347
        if (waylandWnd == widgetChain[i]) {
1348
          popupFound = true;
1349
          break;
1350
        }
1351
      }
1352
      if (!popupFound) {
1353
        LOG(("    nsWindow [%p] not found in PopupManager, hiding it.\n",
1354
             waylandWnd));
1355
        waylandWnd->HideWaylandWindow();
1356
        popupList = gVisibleWaylandPopupWindows;
1357
      } else {
1358
        LOG(("    nsWindow [%p] is still open.\n", waylandWnd));
1359
        popupList = popupList->next;
1360
      }
1361
    } else {
1362
      popupList = popupList->next;
1363
    }
1364
  }
1365
}
1366
1367
static nsMenuPopupFrame* GetMenuPopupFrame(nsIFrame* aFrame) {
1368
  if (aFrame) {
1369
    nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(aFrame);
1370
    return menuPopupFrame;
1371
  }
1372
  return nullptr;
1373
}
1374
1375
// The MenuList popups are used as dropdown menus for example in WebRTC
1376
// microphone/camera chooser or autocomplete widgets.
1377
bool nsWindow::IsMainMenuWindow() {
1378
  nsMenuPopupFrame* menuPopupFrame = GetMenuPopupFrame(GetFrame());
1379
  if (menuPopupFrame) {
1380
    LOG(("  nsMenuPopupFrame [%p] type: %d IsMenu: %d, IsMenuList: %d\n",
1381
         menuPopupFrame, menuPopupFrame->PopupType(), menuPopupFrame->IsMenu(),
1382
         menuPopupFrame->IsMenuList()));
1383
    return mPopupType == ePopupTypeMenu && !menuPopupFrame->IsMenuList();
1384
  }
1385
  return false;
1386
}
1387
1388
GtkWindow* nsWindow::GetTopmostWindow() {
1389
  nsView* view = nsView::GetViewFor(this);
1390
  if (view) {
1391
    nsView* parentView = view->GetParent();
1392
    if (parentView) {
1393
      nsIWidget* parentWidget = parentView->GetNearestWidget(nullptr);
1394
      if (parentWidget) {
1395
        nsWindow* parentnsWindow = static_cast<nsWindow*>(parentWidget);
1396
        LOG(("  Topmost window: %p [nsWindow]\n", parentnsWindow));
1397
        return GTK_WINDOW(parentnsWindow->mShell);
1398
      }
1399
    }
1400
  }
1401
  return nullptr;
1402
}
1403
1404
GtkWindow* nsWindow::GetCurrentWindow() {
1405
  GtkWindow* parentGtkWindow = nullptr;
1406
  // get the last opened window from gVisibleWaylandPopupWindows
1407
  if (gVisibleWaylandPopupWindows) {
1408
    nsWindow* parentnsWindow =
1409
        static_cast<nsWindow*>(gVisibleWaylandPopupWindows->data);
1410
    if (parentnsWindow) {
1411
      LOG(("  Setting parent to last opened window: %p [nsWindow]\n",
1412
           parentnsWindow));
1413
      parentGtkWindow = GTK_WINDOW(parentnsWindow->GetGtkWidget());
1414
    }
1415
  }
1416
  // get the topmost window if the last opened windows are empty
1417
  if (!parentGtkWindow) {
1418
    parentGtkWindow = GetTopmostWindow();
1419
  }
1420
  if (parentGtkWindow && GTK_IS_WINDOW(parentGtkWindow)) {
1421
    return GTK_WINDOW(parentGtkWindow);
1422
  } else {
1423
    LOG(("  Failed to get current window for %p: %p\n", this, parentGtkWindow));
1424
  }
1425
  return nullptr;
1426
}
1427
1428
bool nsWindow::IsWidgetOverflowWindow() {
1429
  if (this->GetFrame() && this->GetFrame()->GetContent()->GetID()) {
1430
    nsCString nodeId;
1431
    this->GetFrame()->GetContent()->GetID()->ToUTF8String(nodeId);
1432
    return nodeId.Equals("widget-overflow");
1433
  }
1434
  return false;
1435
}
1436
1437
// Wayland keeps strong popup window hierarchy. We need to track active
1438
// (visible) popup windows and make sure we hide popup on the same level
1439
// before we open another one on that level. It means that every open
1440
// popup needs to have an unique parent.
1441
GtkWidget* nsWindow::ConfigureWaylandPopupWindows() {
1442
  MOZ_ASSERT(this->mWindowType == eWindowType_popup);
1443
  LOG(
1444
      ("nsWindow::ConfigureWaylandPopupWindows [%p], frame %p hasRemoteContent "
1445
       "%d\n",
1446
       (void*)this, this->GetFrame(), this->HasRemoteContent()));
1447
#if DEBUG
1448
  if (this->GetFrame() && this->GetFrame()->GetContent()->GetID()) {
1449
    nsCString nodeId;
1450
    this->GetFrame()->GetContent()->GetID()->ToUTF8String(nodeId);
1451
    LOG(("  [%p] popup node id=%s\n", this, nodeId.get()));
1452
  }
1453
#endif
1454
1455
  if (!GetFrame()) {
1456
    LOG(("  Window without frame cannot be configured.\n"));
1457
    return nullptr;
1458
  }
1459
1460
  // Check if we're already configured.
1461
  if (gVisibleWaylandPopupWindows &&
1462
      g_list_find(gVisibleWaylandPopupWindows, this)) {
1463
    LOG(("  [%p] is already configured.\n", (void*)this));
1464
    return GTK_WIDGET(gtk_window_get_transient_for(GTK_WINDOW(mShell)));
1465
  }
1466
1467
  // If we're opening a new window we don't want to attach it to a tooltip
1468
  // as it's short lived temporary window.
1469
  HideWaylandTooltips();
1470
1471
  // Cleanup already closed menus
1472
  CleanupWaylandPopups();
1473
1474
  if (gVisibleWaylandPopupWindows &&
1475
      (HasRemoteContent() || IsWidgetOverflowWindow())) {
1476
    nsWindow* openedWindow =
1477
        static_cast<nsWindow*>(gVisibleWaylandPopupWindows->data);
1478
    LOG(("  this [%p], lastOpenedWindow [%p]", this, openedWindow));
1479
    if (openedWindow != this) {
1480
      LOG(
1481
          ("  Hiding all opened popups because the window is remote content or "
1482
           "overflow-widget"));
1483
      HideWaylandOpenedPopups();
1484
    }
1485
  }
1486
1487
  GtkWindow* parentGtkWindow = GetCurrentWindow();
1488
  if (parentGtkWindow) {
1489
    MOZ_ASSERT(parentGtkWindow != GTK_WINDOW(this->GetGtkWidget()),
1490
               "Cannot set self as parent");
1491
    gtk_window_set_transient_for(GTK_WINDOW(mShell),
1492
                                 GTK_WINDOW(parentGtkWindow));
1493
    // Add current window to the visible popup list
1494
    gVisibleWaylandPopupWindows =
1495
        g_list_prepend(gVisibleWaylandPopupWindows, this);
1496
    LOG(("  Parent window for %p: %p [GtkWindow]", this, parentGtkWindow));
1497
  }
1498
1499
  MOZ_ASSERT(parentGtkWindow, "NO parent window for %p: expect popup glitches");
1500
  return GTK_WIDGET(parentGtkWindow);
1501
}
1502
1503
static void NativeMoveResizeWaylandPopupCallback(
1504
    GdkWindow* window, const GdkRectangle* flipped_rect,
1505
    const GdkRectangle* final_rect, gboolean flipped_x, gboolean flipped_y,
1506
    void* aWindow) {
1507
  LOG(("NativeMoveResizeWaylandPopupCallback [%p] flipped_x %d flipped_y %d\n",
1508
       aWindow, flipped_x, flipped_y));
1509
1510
  LOG(("  flipped_rect x=%d y=%d width=%d height=%d\n", flipped_rect->x,
1511
       flipped_rect->y, flipped_rect->width, flipped_rect->height));
1512
  LOG(("  final_rect   x=%d y=%d width=%d height=%d\n", final_rect->x,
1513
       final_rect->y, final_rect->width, final_rect->height));
1514
  nsWindow* wnd = get_window_for_gdk_window(window);
1515
1516
  wnd->NativeMoveResizeWaylandPopupCB(final_rect, flipped_x, flipped_y);
1517
}
1518
1519
void nsWindow::NativeMoveResizeWaylandPopupCB(const GdkRectangle* aFinalSize,
1520
                                              bool aFlippedX, bool aFlippedY) {
1521
  LOG(("  orig mBounds x=%d y=%d width=%d height=%d\n", mBounds.x, mBounds.y,
1522
       mBounds.width, mBounds.height));
1523
1524
  mWaitingForMoveToRectCB = false;
1525
1526
  // We ignore the callback position data because the another resize has been
1527
  // called before the callback have been triggered.
1528
  if (mPendingSizeRect.height > 0 || mPendingSizeRect.width > 0) {
1529
    LOG(
1530
        ("  Another resize called during waiting for callback, calling "
1531
         "Resize(%d, %d)\n",
1532
         mPendingSizeRect.width, mPendingSizeRect.height));
1533
    // Set the preferred size to zero to avoid wrong size of popup because the
1534
    // mPreferredPopupRect is used in nsMenuPopupFrame to set dimensions
1535
    mPreferredPopupRect = nsRect(0, 0, 0, 0);
1536
1537
    // We need to schedule another resize because the window has been resized
1538
    // again before callback was called.
1539
    Resize(mPendingSizeRect.width, mPendingSizeRect.height, true);
1540
    DispatchResized();
1541
    mPendingSizeRect.width = mPendingSizeRect.height = 0;
1542
    return;
1543
  }
1544
1545
  GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell));
1546
  if (!parentGtkWindow || !GTK_IS_WIDGET(parentGtkWindow)) {
1547
    NS_WARNING("Popup has no parent!");
1548
    return;
1549
  }
1550
1551
  // The position of the menu in GTK is relative to it's parent window while
1552
  // in mBounds we have position relative to toplevel window. We need to check
1553
  // and update mBounds in the toplevel coordinates.
1554
  int x_parent, y_parent;
1555
  GetWindowOrigin(gtk_widget_get_window(GTK_WIDGET(parentGtkWindow)), &x_parent,
1556
                  &y_parent);
1557
1558
  LayoutDeviceIntRect newBounds(aFinalSize->x, aFinalSize->y, aFinalSize->width,
1559
                                aFinalSize->height);
1560
1561
  newBounds.x = GdkCoordToDevicePixels(newBounds.x);
1562
  newBounds.y = GdkCoordToDevicePixels(newBounds.y);
1563
1564
  double scale =
1565
      BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
1566
  int32_t newWidth = NSToIntRound(scale * newBounds.width);
1567
  int32_t newHeight = NSToIntRound(scale * newBounds.height);
1568
1569
  // Convert newBounds to "absolute" coordinates (relative to toplevel)
1570
  newBounds.x += x_parent * GdkScaleFactor();
1571
  newBounds.y += y_parent * GdkScaleFactor();
1572
1573
  LOG(("  new mBounds  x=%d y=%d width=%d height=%d x_parent=%d y_parent=%d\n",
1574
       newBounds.x, newBounds.y, newWidth, newHeight, x_parent, y_parent));
1575
1576
  bool needsPositionUpdate =
1577
      (newBounds.x != mBounds.x || newBounds.y != mBounds.y);
1578
  bool needsSizeUpdate =
1579
      (newWidth != mBounds.width || newHeight != mBounds.height);
1580
  // Update view
1581
1582
  if (needsSizeUpdate) {
1583
    LOG(("  needSizeUpdate\n"));
1584
    int32_t p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
1585
    mPreferredPopupRect = nsRect(NSIntPixelsToAppUnits(newBounds.x, p2a),
1586
                                 NSIntPixelsToAppUnits(newBounds.y, p2a),
1587
                                 NSIntPixelsToAppUnits(newBounds.width, p2a),
1588
                                 NSIntPixelsToAppUnits(newBounds.height, p2a));
1589
    mPreferredPopupRectFlushed = false;
1590
    Resize(newBounds.width, newBounds.height, true);
1591
    DispatchResized();
1592
1593
    nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
1594
    if (popupFrame) {
1595
      RefPtr<PresShell> presShell = popupFrame->PresShell();
1596
      presShell->FrameNeedsReflow(popupFrame, IntrinsicDirty::Resize,
1597
                                  NS_FRAME_IS_DIRTY);
1598
      // Force to trigger popup crop to fit the screen
1599
      popupFrame->SetPopupPosition(nullptr, true, false);
1600
    }
1601
  }
1602
1603
  if (needsPositionUpdate) {
1604
    LOG(("  needPositionUpdate\n"));
1605
    // The newBounds are in coordinates relative to the parent window/popup.
1606
    // The NotifyWindowMoved requires the coordinates relative to the toplevel.
1607
    // We use the gdk_window_get_origin to get correct coordinates.
1608
    gint x = 0, y = 0;
1609
    GetWindowOrigin(gtk_widget_get_window(GTK_WIDGET(mShell)), &x, &y);
1610
    NotifyWindowMoved(GdkCoordToDevicePixels(x), GdkCoordToDevicePixels(y));
1611
  }
1612
}
1613
1614
#ifdef MOZ_WAYLAND
1615
static GdkGravity PopupAlignmentToGdkGravity(int8_t aAlignment) {
1616
  switch (aAlignment) {
1617
    case POPUPALIGNMENT_NONE:
1618
      return GDK_GRAVITY_NORTH_WEST;
1619
      break;
1620
    case POPUPALIGNMENT_TOPLEFT:
1621
      return GDK_GRAVITY_NORTH_WEST;
1622
      break;
1623
    case POPUPALIGNMENT_TOPRIGHT:
1624
      return GDK_GRAVITY_NORTH_EAST;
1625
      break;
1626
    case POPUPALIGNMENT_BOTTOMLEFT:
1627
      return GDK_GRAVITY_SOUTH_WEST;
1628
      break;
1629
    case POPUPALIGNMENT_BOTTOMRIGHT:
1630
      return GDK_GRAVITY_SOUTH_EAST;
1631
      break;
1632
    case POPUPALIGNMENT_LEFTCENTER:
1633
      return GDK_GRAVITY_WEST;
1634
      break;
1635
    case POPUPALIGNMENT_RIGHTCENTER:
1636
      return GDK_GRAVITY_EAST;
1637
      break;
1638
    case POPUPALIGNMENT_TOPCENTER:
1639
      return GDK_GRAVITY_NORTH;
1640
      break;
1641
    case POPUPALIGNMENT_BOTTOMCENTER:
1642
      return GDK_GRAVITY_SOUTH;
1643
      break;
1644
  }
1645
  return GDK_GRAVITY_STATIC;
1646
}
1647
#endif
1648
1649
void nsWindow::NativeMoveResizeWaylandPopup(GdkPoint* aPosition,
1650
                                            GdkRectangle* aSize) {
1651
  // Available as of GTK 3.24+
1652
  static auto sGdkWindowMoveToRect = (void (*)(
1653
      GdkWindow*, const GdkRectangle*, GdkGravity, GdkGravity, GdkAnchorHints,
1654
      gint, gint))dlsym(RTLD_DEFAULT, "gdk_window_move_to_rect");
1655
  LOG(("nsWindow::NativeMoveResizeWaylandPopup [%p]\n", (void*)this));
1656
1657
  // Compositor may be confused by windows with width/height = 0
1658
  // and positioning such windows leads to Bug 1555866.
1659
  if (!AreBoundsSane()) {
1660
    LOG(("  Bounds are not sane (width: %d height: %d)\n", mBounds.width,
1661
         mBounds.height));
1662
    return;
1663
  }
1664
1665
  if (aSize) {
1666
    gtk_window_resize(GTK_WINDOW(mShell), aSize->width, aSize->height);
1667
  }
1668
1669
  GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(mShell));
1670
1671
  // Use standard gtk_window_move() instead of gdk_window_move_to_rect() when:
1672
  // - gdk_window_move_to_rect() is not available
1673
  // - the widget doesn't have a valid GdkWindow
1674
  if (!sGdkWindowMoveToRect || !gdkWindow) {
1675
    LOG(("  use gtk_window_move(%d, %d)\n", aPosition->x, aPosition->y));
1676
    gtk_window_move(GTK_WINDOW(mShell), aPosition->x, aPosition->y);
1677
    return;
1678
  }
1679
1680
  GtkWidget* parentWindow = ConfigureWaylandPopupWindows();
1681
  LOG(("nsWindow::NativeMoveResizeWaylandPopup: Set popup parent %p\n",
1682
       parentWindow));
1683
1684
  // Get anchor rectangle
1685
  LayoutDeviceIntRect anchorRect(0, 0, 0, 0);
1686
  nsMenuPopupFrame* popupFrame = GetMenuPopupFrame(GetFrame());
1687
1688
  int32_t p2a;
1689
  double devPixelsPerCSSPixel = StaticPrefs::layout_css_devPixelsPerPx();
1690
  if (devPixelsPerCSSPixel > 0.0) {
1691
    p2a = AppUnitsPerCSSPixel() / devPixelsPerCSSPixel * GdkScaleFactor();
1692
  } else {
1693
    p2a = AppUnitsPerCSSPixel() / gfxPlatformGtk::GetFontScaleFactor();
1694
  }
1695
  if (popupFrame) {
1696
#ifdef MOZ_WAYLAND
1697
    nsRect anchorRectAppUnits = popupFrame->GetAnchorRect();
1698
    anchorRect = LayoutDeviceIntRect::FromUnknownRect(
1699
        anchorRectAppUnits.ToNearestPixels(p2a));
1700
1701
#endif
1702
  }
1703
1704
#ifdef MOZ_WAYLAND
1705
  bool hasAnchorRect = true;
1706
#endif
1707
  if (anchorRect.width == 0) {
1708
    LOG(("  No anchor rect given, use aPosition for anchor"));
1709
    anchorRect.SetRect(aPosition->x, aPosition->y, 1, 1);
1710
#ifdef MOZ_WAYLAND
1711
    hasAnchorRect = false;
1712
#endif
1713
  }
1714
  LOG(("  anchor x %d y %d width %d height %d (absolute coords)\n",
1715
       anchorRect.x, anchorRect.y, anchorRect.width, anchorRect.height));
1716
1717
  // Anchor rect is in the toplevel coordinates but we need to transfer it to
1718
  // the coordinates relative to the popup parent for the
1719
  // gdk_window_move_to_rect
1720
  int x_parent = 0, y_parent = 0;
1721
  GtkWindow* parentGtkWindow = gtk_window_get_transient_for(GTK_WINDOW(mShell));
1722
  if (parentGtkWindow) {
1723
    GetWindowOrigin(gtk_widget_get_window(GTK_WIDGET(parentGtkWindow)),
1724
                    &x_parent, &y_parent);
1725
  }
1726
  LOG(("  x_parent    %d   y_parent    %d\n", x_parent, y_parent));
1727
  anchorRect.MoveBy(-x_parent, -y_parent);
1728
  GdkRectangle rect = {anchorRect.x, anchorRect.y, anchorRect.width,
1729
                       anchorRect.height};
1730
1731
  // Get gravity and flip type
1732
  GdkGravity rectAnchor = GDK_GRAVITY_NORTH_WEST;
1733
  GdkGravity menuAnchor = GDK_GRAVITY_NORTH_WEST;
1734
  FlipType flipType = FlipType_Default;
1735
  int8_t position = -1;
1736
  if (popupFrame) {
1737
#ifdef MOZ_WAYLAND
1738
    rectAnchor = PopupAlignmentToGdkGravity(popupFrame->GetPopupAnchor());
1739
    menuAnchor = PopupAlignmentToGdkGravity(popupFrame->GetPopupAlignment());
1740
    flipType = popupFrame->GetFlipType();
1741
    position = popupFrame->GetAlignmentPosition();
1742
#endif
1743
  } else {
1744
    LOG(("  NO ANCHOR INFO"));
1745
    if (GetTextDirection() == GTK_TEXT_DIR_RTL) {
1746
      rectAnchor = GDK_GRAVITY_NORTH_EAST;
1747
      menuAnchor = GDK_GRAVITY_NORTH_EAST;
1748
    }
1749
  }
1750
  LOG((" parentRect gravity: %d anchor gravity: %d\n", rectAnchor, menuAnchor));
1751
1752
  GdkAnchorHints hints = GdkAnchorHints(GDK_ANCHOR_RESIZE);
1753
1754
  // slideHorizontal from nsMenuPopupFrame::SetPopupPosition
1755
  if (position >= POPUPPOSITION_BEFORESTART &&
1756
      position <= POPUPPOSITION_AFTEREND) {
1757
    hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE_X);
1758
  }
1759
  // slideVertical from nsMenuPopupFrame::SetPopupPosition
1760
  if (position >= POPUPPOSITION_STARTBEFORE &&
1761
      position <= POPUPPOSITION_ENDAFTER) {
1762
    hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE_Y);
1763
  }
1764
1765
  if (popupFrame && rectAnchor == GDK_GRAVITY_CENTER &&
1766
      menuAnchor == GDK_GRAVITY_CENTER) {
1767
    // only slide
1768
    hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE);
1769
  } else {
1770
    switch (flipType) {
1771
      case FlipType_Both:
1772
        hints = GdkAnchorHints(hints | GDK_ANCHOR_FLIP);
1773
        break;
1774
      case FlipType_Slide:
1775
        hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE);
1776
        break;
1777
      case FlipType_Default:
1778
        hints = GdkAnchorHints(hints | GDK_ANCHOR_FLIP);
1779
        break;
1780
      default:
1781
        break;
1782
    }
1783
  }
1784
  if (!IsMainMenuWindow()) {
1785
    // we don't want to slide menus to fit the screen rather resize them
1786
    hints = GdkAnchorHints(hints | GDK_ANCHOR_SLIDE);
1787
  }
1788
1789
  // A workaround for https://gitlab.gnome.org/GNOME/gtk/issues/1986
1790
  // gdk_window_move_to_rect() does not reposition visible windows.
1791
  static auto sGtkWidgetIsVisible =
1792
      (gboolean(*)(GtkWidget*))dlsym(RTLD_DEFAULT, "gtk_widget_is_visible");
1793
1794
  bool isWidgetVisible =
1795
      (sGtkWidgetIsVisible != nullptr) && sGtkWidgetIsVisible(mShell);
1796
  if (isWidgetVisible) {
1797
    LOG(
1798
        ("  temporary hide popup due to "
1799
         "https://gitlab.gnome.org/GNOME/gtk/issues/1986\n"));
1800
    PauseRemoteRenderer();
1801
    gtk_widget_hide(mShell);
1802
  }
1803
1804
  LOG(("  requested rect: x: %d y: %d width: %d height: %d\n", rect.x, rect.y,
1805
       rect.width, rect.height));
1806
  if (aSize) {
1807
    LOG(("  aSize: x%d y%d w%d h%d\n", aSize->x, aSize->y, aSize->width,
1808
         aSize->height));
1809
  } else {
1810
    LOG(("  No aSize given"));
1811
  }
1812
1813
  // Inspired by nsMenuPopupFrame::AdjustPositionForAnchorAlign
1814
  nsPoint cursorOffset(0, 0);
1815
#ifdef MOZ_WAYLAND
1816
  // Offset is already computed to the tooltips
1817
  if (hasAnchorRect && popupFrame && mPopupType != ePopupTypeTooltip) {
1818
    nsMargin margin(0, 0, 0, 0);
1819
    popupFrame->StyleMargin()->GetMargin(margin);
1820
    switch (popupFrame->GetPopupAlignment()) {
1821
      case POPUPALIGNMENT_TOPRIGHT:
1822
        cursorOffset.MoveBy(-margin.right, margin.top);
1823
        break;
1824
      case POPUPALIGNMENT_BOTTOMLEFT:
1825
        cursorOffset.MoveBy(margin.left, -margin.bottom);
1826
        break;
1827
      case POPUPALIGNMENT_BOTTOMRIGHT:
1828
        cursorOffset.MoveBy(-margin.right, -margin.bottom);
1829
        break;
1830
      case POPUPALIGNMENT_TOPLEFT:
1831
      default:
1832
        cursorOffset.MoveBy(margin.left, margin.top);
1833
        break;
1834
    }
1835
  }
1836
#endif
1837
1838
  if (!g_signal_handler_find(
1839
          gdkWindow, G_SIGNAL_MATCH_FUNC, 0, 0, nullptr,
1840
          FuncToGpointer(NativeMoveResizeWaylandPopupCallback), this)) {
1841
    g_signal_connect(gdkWindow, "moved-to-rect",
1842
                     G_CALLBACK(NativeMoveResizeWaylandPopupCallback), this);
1843
  }
1844
1845
  LOG(("  popup window cursor offset x: %d y: %d\n", cursorOffset.x / p2a,
1846
       cursorOffset.y / p2a));
1847
  mWaitingForMoveToRectCB = true;
1848
  sGdkWindowMoveToRect(gdkWindow, &rect, rectAnchor, menuAnchor, hints,
1849
                       cursorOffset.x / p2a, cursorOffset.y / p2a);
1850
1851
  if (isWidgetVisible) {
1852
    // We show the popup with the same configuration so no need to call
1853
    // ConfigureWaylandPopupWindows() before gtk_widget_show().
1854
    LOG(
1855
        ("  show popup due to "
1856
         "https://gitlab.gnome.org/GNOME/gtk/issues/1986\n"));
1857
    gtk_widget_show(mShell);
1858
  }
1859
}
1860
1861
void nsWindow::NativeMove() {
1862
  GdkPoint point = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
1863
1864
  LOG(("nsWindow::NativeMove [%p] %d %d\n", (void*)this, point.x, point.y));
1865
1866
  if (IsWaylandPopup()) {
1867
    GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
1868
    NativeMoveResizeWaylandPopup(&point, &size);
1869
  } else if (mIsTopLevel) {
1870
    gtk_window_move(GTK_WINDOW(mShell), point.x, point.y);
1871
  } else if (mGdkWindow) {
1872
    gdk_window_move(mGdkWindow, point.x, point.y);
1873
  }
1874
}
1875
1876
void nsWindow::SetZIndex(int32_t aZIndex) {
1877
  nsIWidget* oldPrev = GetPrevSibling();
1878
1879
  nsBaseWidget::SetZIndex(aZIndex);
1880
1881
  if (GetPrevSibling() == oldPrev) {
1882
    return;
1883
  }
1884
1885
  NS_ASSERTION(!mContainer, "Expected Mozilla child widget");
1886
1887
  // We skip the nsWindows that don't have mGdkWindows.
1888
  // These are probably in the process of being destroyed.
1889
1890
  if (!GetNextSibling()) {
1891
    // We're to be on top.
1892
    if (mGdkWindow) gdk_window_raise(mGdkWindow);
1893
  } else {
1894
    // All the siblings before us need to be below our widget.
1895
    for (nsWindow* w = this; w;
1896
         w = static_cast<nsWindow*>(w->GetPrevSibling())) {
1897
      if (w->mGdkWindow) gdk_window_lower(w->mGdkWindow);
1898
    }
1899
  }
1900
}
1901
1902
void nsWindow::SetSizeMode(nsSizeMode aMode) {
1903
  LOG(("nsWindow::SetSizeMode [%p] %d\n", (void*)this, aMode));
1904
1905
  // Save the requested state.
1906
  nsBaseWidget::SetSizeMode(aMode);
1907
1908
  // return if there's no shell or our current state is the same as
1909
  // the mode we were just set to.
1910
  if (!mShell || mSizeState == mSizeMode) {
1911
    LOG(("    already set"));
1912
    return;
1913
  }
1914
1915
  switch (aMode) {
1916
    case nsSizeMode_Maximized:
1917
      LOG(("    set maximized"));
1918
      gtk_window_maximize(GTK_WINDOW(mShell));
1919
      break;
1920
    case nsSizeMode_Minimized:
1921
      LOG(("    set minimized"));
1922
      gtk_window_iconify(GTK_WINDOW(mShell));
1923
      break;
1924
    case nsSizeMode_Fullscreen:
1925
      LOG(("    set fullscreen"));
1926
      MakeFullScreen(true);
1927
      break;
1928
1929
    default:
1930
      LOG(("    set normal"));
1931
      // nsSizeMode_Normal, really.
1932
      if (mSizeState == nsSizeMode_Minimized)
1933
        gtk_window_deiconify(GTK_WINDOW(mShell));
1934
      else if (mSizeState == nsSizeMode_Maximized)
1935
        gtk_window_unmaximize(GTK_WINDOW(mShell));
1936
      break;
1937
  }
1938
1939
  // Request mBounds update from configure event as we may not get
1940
  // OnSizeAllocate for size state changes (Bug 1489463).
1941
  mBoundsAreValid = false;
1942
1943
  mSizeState = mSizeMode;
1944
}
1945
1946
static bool GetWindowManagerName(GdkWindow* gdk_window, nsACString& wmName) {
1947
  if (!gfxPlatformGtk::GetPlatform()->IsX11Display()) {
1948
    return false;
1949
  }
1950
1951
  Display* xdisplay = gdk_x11_get_default_xdisplay();
1952
  GdkScreen* screen = gdk_window_get_screen(gdk_window);
1953
  Window root_win = GDK_WINDOW_XID(gdk_screen_get_root_window(screen));
1954
1955
  int actual_format_return;
1956
  Atom actual_type_return;
1957
  unsigned long nitems_return;
1958
  unsigned long bytes_after_return;
1959
  unsigned char* prop_return = nullptr;
1960
  auto releaseXProperty = MakeScopeExit([&] {
1961
    if (prop_return) {
1962
      XFree(prop_return);
1963
    }
1964
  });
1965
1966
  Atom property = XInternAtom(xdisplay, "_NET_SUPPORTING_WM_CHECK", true);
1967
  Atom req_type = XInternAtom(xdisplay, "WINDOW", true);
1968
  if (!property || !req_type) {
1969
    return false;
1970
  }
1971
  int result =
1972
      XGetWindowProperty(xdisplay, root_win, property,
1973
                         0L,                  // offset
1974
                         sizeof(Window) / 4,  // length
1975
                         false,               // delete
1976
                         req_type, &actual_type_return, &actual_format_return,
1977
                         &nitems_return, &bytes_after_return, &prop_return);
1978
1979
  if (result != Success || bytes_after_return != 0 || nitems_return != 1) {
1980
    return false;
1981
  }
1982
1983
  Window wmWindow = reinterpret_cast<Window*>(prop_return)[0];
1984
  if (!wmWindow) {
1985
    return false;
1986
  }
1987
1988
  XFree(prop_return);
1989
  prop_return = nullptr;
1990
1991
  property = XInternAtom(xdisplay, "_NET_WM_NAME", true);
1992
  req_type = XInternAtom(xdisplay, "UTF8_STRING", true);
1993
  if (!property || !req_type) {
1994
    return false;
1995
  }
1996
  {
1997
    // Suppress fatal errors for a missing window.
1998
    ScopedXErrorHandler handler;
1999
    result =
2000
        XGetWindowProperty(xdisplay, wmWindow, property,
2001
                           0L,         // offset
2002
                           INT32_MAX,  // length
2003
                           false,      // delete
2004
                           req_type, &actual_type_return, &actual_format_return,
2005
                           &nitems_return, &bytes_after_return, &prop_return);
2006
  }
2007
2008
  if (result != Success || bytes_after_return != 0) {
2009
    return false;
2010
  }
2011
2012
  wmName = reinterpret_cast<const char*>(prop_return);
2013
  return true;
2014
}
2015
2016
#define kDesktopMutterSchema "org.gnome.mutter"
2017
#define kDesktopDynamicWorkspacesKey "dynamic-workspaces"
2018
2019
static bool WorkspaceManagementDisabled(GdkWindow* gdk_window) {
2020
  if (Preferences::GetBool("widget.disable-workspace-management", false)) {
2021
    return true;
2022
  }
2023
  if (Preferences::HasUserValue("widget.workspace-management")) {
2024
    return Preferences::GetBool("widget.workspace-management");
2025
  }
2026
2027
  static const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
2028
  if (currentDesktop && strstr(currentDesktop, "GNOME")) {
2029
    // Gnome uses dynamic workspaces by default so disable workspace management
2030
    // in that case.
2031
    bool usesDynamicWorkspaces = true;
2032
    nsCOMPtr<nsIGSettingsService> gsettings =
2033
        do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
2034
    if (gsettings) {
2035
      nsCOMPtr<nsIGSettingsCollection> mutterSettings;
2036
      gsettings->GetCollectionForSchema(nsLiteralCString(kDesktopMutterSchema),
2037
                                        getter_AddRefs(mutterSettings));
2038
      if (mutterSettings) {
2039
        if (NS_SUCCEEDED(mutterSettings->GetBoolean(
2040
                nsLiteralCString(kDesktopDynamicWorkspacesKey),
2041
                &usesDynamicWorkspaces))) {
2042
        }
2043
      }
2044
    }
2045
    return usesDynamicWorkspaces;
2046
  }
2047
2048
  // When XDG_CURRENT_DESKTOP is missing, try to get window manager name.
2049
  if (!currentDesktop) {
2050
    nsAutoCString wmName;
2051
    if (GetWindowManagerName(gdk_window, wmName)) {
2052
      if (wmName.EqualsLiteral("bspwm")) {
2053
        return true;
2054
      }
2055
      if (wmName.EqualsLiteral("i3")) {
2056
        return true;
2057
      }
2058
    }
2059
  }
2060
2061
  return false;
2062
}
2063
2064
void nsWindow::GetWorkspaceID(nsAString& workspaceID) {
2065
  workspaceID.Truncate();
2066
2067
  if (!mIsX11Display || !mShell) {
2068
    return;
2069
  }
2070
  // Get the gdk window for this widget.
2071
  GdkWindow* gdk_window = gtk_widget_get_window(mShell);
2072
  if (!gdk_window) {
2073
    return;
2074
  }
2075
2076
  if (WorkspaceManagementDisabled(gdk_window)) {
2077
    return;
2078
  }
2079
2080
  GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
2081
  GdkAtom type_returned;
2082
  int format_returned;
2083
  int length_returned;
2084
  long* wm_desktop;
2085
2086
  if (!gdk_property_get(gdk_window, gdk_atom_intern("_NET_WM_DESKTOP", FALSE),
2087
                        cardinal_atom,
2088
                        0,          // offset
2089
                        INT32_MAX,  // length
2090
                        FALSE,      // delete
2091
                        &type_returned, &format_returned, &length_returned,
2092
                        (guchar**)&wm_desktop)) {
2093
    return;
2094
  }
2095
2096
  workspaceID.AppendInt((int32_t)wm_desktop[0]);
2097
  g_free(wm_desktop);
2098
}
2099
2100
void nsWindow::MoveToWorkspace(const nsAString& workspaceIDStr) {
2101
  nsresult rv = NS_OK;
2102
  int32_t workspaceID = workspaceIDStr.ToInteger(&rv);
2103
  if (NS_FAILED(rv) || !workspaceID || !mIsX11Display || !mShell) {
2104
    return;
2105
  }
2106
2107
  // Get the gdk window for this widget.
2108
  GdkWindow* gdk_window = gtk_widget_get_window(mShell);
2109
  if (!gdk_window) {
2110
    return;
2111
  }
2112
2113
  // This code is inspired by some found in the 'gxtuner' project.
2114
  // https://github.com/brummer10/gxtuner/blob/792d453da0f3a599408008f0f1107823939d730d/deskpager.cpp#L50
2115
  XEvent xevent;
2116
  Display* xdisplay = gdk_x11_get_default_xdisplay();
2117
  GdkScreen* screen = gdk_window_get_screen(gdk_window);
2118
  Window root_win = GDK_WINDOW_XID(gdk_screen_get_root_window(screen));
2119
  GdkDisplay* display = gdk_window_get_display(gdk_window);
2120
  Atom type = gdk_x11_get_xatom_by_name_for_display(display, "_NET_WM_DESKTOP");
2121
2122
  xevent.type = ClientMessage;
2123
  xevent.xclient.type = ClientMessage;
2124
  xevent.xclient.serial = 0;
2125
  xevent.xclient.send_event = TRUE;
2126
  xevent.xclient.display = xdisplay;
2127
  xevent.xclient.window = GDK_WINDOW_XID(gdk_window);
2128
  xevent.xclient.message_type = type;
2129
  xevent.xclient.format = 32;
2130
  xevent.xclient.data.l[0] = workspaceID;
2131
  xevent.xclient.data.l[1] = X11CurrentTime;
2132
  xevent.xclient.data.l[2] = 0;
2133
  xevent.xclient.data.l[3] = 0;
2134
  xevent.xclient.data.l[4] = 0;
2135
2136
  XSendEvent(xdisplay, root_win, FALSE,
2137
             SubstructureNotifyMask | SubstructureRedirectMask, &xevent);
2138
2139
  XFlush(xdisplay);
2140
}
2141
2142
typedef void (*SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp);
2143
2144
static void SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow) {
2145
  nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
2146
  if (!GTKToolkit) return;
2147
2148
  nsAutoCString desktopStartupID;
2149
  GTKToolkit->GetDesktopStartupID(&desktopStartupID);
2150
  if (desktopStartupID.IsEmpty()) {
2151
    // We don't have the data we need. Fall back to an
2152
    // approximation ... using the timestamp of the remote command
2153
    // being received as a guess for the timestamp of the user event
2154
    // that triggered it.
2155
    uint32_t timestamp = GTKToolkit->GetFocusTimestamp();
2156
    if (timestamp) {
2157
      gdk_window_focus(gtk_widget_get_window(aWindow), timestamp);
2158
      GTKToolkit->SetFocusTimestamp(0);
2159
    }
2160
    return;
2161
  }
2162
2163
  gtk_window_set_startup_id(GTK_WINDOW(aWindow), desktopStartupID.get());
2164
2165
  // If we used the startup ID, that already contains the focus timestamp;
2166
  // we don't want to reuse the timestamp next time we raise the window
2167
  GTKToolkit->SetFocusTimestamp(0);
2168
  GTKToolkit->SetDesktopStartupID(""_ns);
2169
}
2170
2171
/* static */
2172
guint32 nsWindow::GetLastUserInputTime() {
2173
  // gdk_x11_display_get_user_time/gtk_get_current_event_time tracks
2174
  // button and key presses, DESKTOP_STARTUP_ID used to start the app,
2175
  // drop events from external drags,
2176
  // WM_DELETE_WINDOW delete events, but not usually mouse motion nor
2177
  // button and key releases.  Therefore use the most recent of
2178
  // gdk_x11_display_get_user_time and the last time that we have seen.
2179
  GdkDisplay* gdkDisplay = gdk_display_get_default();
2180
  guint32 timestamp = GdkIsX11Display(gdkDisplay)
2181
                          ? gdk_x11_display_get_user_time(gdkDisplay)
2182
                          : gtk_get_current_event_time();
2183
2184
  if (sLastUserInputTime != GDK_CURRENT_TIME &&
2185
      TimestampIsNewerThan(sLastUserInputTime, timestamp)) {
2186
    return sLastUserInputTime;
2187
  }
2188
2189
  return timestamp;
2190
}
2191
2192
void nsWindow::SetFocus(Raise aRaise, mozilla::dom::CallerType aCallerType) {
2193
  // Make sure that our owning widget has focus.  If it doesn't try to
2194
  // grab it.  Note that we don't set our focus flag in this case.
2195
2196
  LOGFOCUS(("  SetFocus %d [%p]\n", aRaise == Raise::Yes, (void*)this));
2197
2198
  GtkWidget* owningWidget = GetMozContainerWidget();
2199
  if (!owningWidget) return;
2200
2201
  // Raise the window if someone passed in true and the prefs are
2202
  // set properly.
2203
  GtkWidget* toplevelWidget = gtk_widget_get_toplevel(owningWidget);
2204
2205
  if (gRaiseWindows && aRaise == Raise::Yes && toplevelWidget &&
2206
      !gtk_widget_has_focus(owningWidget) &&
2207
      !gtk_widget_has_focus(toplevelWidget)) {
2208
    GtkWidget* top_window = GetToplevelWidget();
2209
    if (top_window && (gtk_widget_get_visible(top_window))) {
2210
      gdk_window_show_unraised(gtk_widget_get_window(top_window));
2211
      // Unset the urgency hint if possible.
2212
      SetUrgencyHint(top_window, false);
2213
    }
2214
  }
2215
2216
  RefPtr<nsWindow> owningWindow = get_window_for_gtk_widget(owningWidget);
2217
  if (!owningWindow) return;
2218
2219
  if (aRaise == Raise::Yes) {
2220
    // means request toplevel activation.
2221
2222
    // This is asynchronous.
2223
    // If and when the window manager accepts the request, then the focus
2224
    // widget will get a focus-in-event signal.
2225
    if (gRaiseWindows && owningWindow->mIsShown && owningWindow->mShell &&
2226
        !gtk_window_is_active(GTK_WINDOW(owningWindow->mShell))) {
2227
      if (!mIsX11Display &&
2228
          Preferences::GetBool("widget.wayland.test-workarounds.enabled",
2229
                               false)) {
2230
        // Wayland does not support focus changes so we need to workaround it
2231
        // by window hide/show sequence.
2232
        owningWindow->NativeShow(false);
2233
        RefPtr<nsWindow> self(owningWindow);
2234
        NS_DispatchToMainThread(NS_NewRunnableFunction(
2235
            "nsWindow::NativeShow()",
2236
            [self]() -> void { self->NativeShow(true); }));
2237
        return;
2238
      }
2239
2240
      uint32_t timestamp = GDK_CURRENT_TIME;
2241
2242
      nsGTKToolkit* GTKToolkit = nsGTKToolkit::GetToolkit();
2243
      if (GTKToolkit) timestamp = GTKToolkit->GetFocusTimestamp();
2244
2245
      LOGFOCUS(("  requesting toplevel activation [%p]\n", (void*)this));
2246
      NS_ASSERTION(owningWindow->mWindowType != eWindowType_popup || mParent,
2247
                   "Presenting an override-redirect window");
2248
      gtk_window_present_with_time(GTK_WINDOW(owningWindow->mShell), timestamp);
2249
2250
      if (GTKToolkit) GTKToolkit->SetFocusTimestamp(0);
2251
    }
2252
    return;
2253
  }
2254
2255
  // aRaise == No means that keyboard events should be dispatched from this
2256
  // widget.
2257
2258
  // Ensure owningWidget is the focused GtkWidget within its toplevel window.
2259
  //
2260
  // For eWindowType_popup, this GtkWidget may not actually be the one that
2261
  // receives the key events as it may be the parent window that is active.
2262
  if (!gtk_widget_is_focus(owningWidget)) {
2263
    // This is synchronous.  It takes focus from a plugin or from a widget
2264
    // in an embedder.  The focus manager already knows that this window
2265
    // is active so gBlockActivateEvent avoids another (unnecessary)
2266
    // activate notification.
2267
    gBlockActivateEvent = true;
2268
    gtk_widget_grab_focus(owningWidget);
2269
    gBlockActivateEvent = false;
2270
  }
2271
2272
  // If this is the widget that already has focus, return.
2273
  if (gFocusWindow == this) {
2274
    LOGFOCUS(("  already have focus [%p]\n", (void*)this));
2275
    return;
2276
  }
2277
2278
  // Set this window to be the focused child window
2279
  gFocusWindow = this;
2280
2281
  if (mIMContext) {
2282
    mIMContext->OnFocusWindow(this);
2283
  }
2284
2285
  LOGFOCUS(("  widget now has focus in SetFocus() [%p]\n", (void*)this));
2286
}
2287
2288
LayoutDeviceIntRect nsWindow::GetScreenBounds() {
2289
  LayoutDeviceIntRect rect;
2290
  if (mIsTopLevel && mContainer) {
2291
    // use the point including window decorations
2292
    gint x, y;
2293
    gdk_window_get_root_origin(gtk_widget_get_window(GTK_WIDGET(mContainer)),
2294
                               &x, &y);
2295
    rect.MoveTo(GdkPointToDevicePixels({x, y}));
2296
  } else {
2297
    rect.MoveTo(WidgetToScreenOffset());
2298
  }
2299
  // mBounds.Size() is the window bounds, not the window-manager frame
2300
  // bounds (bug 581863).  gdk_window_get_frame_extents would give the
2301
  // frame bounds, but mBounds.Size() is returned here for consistency
2302
  // with Resize.
2303
  rect.SizeTo(mBounds.Size());
2304
#if MOZ_LOGGING
2305
  gint scale = GdkScaleFactor();
2306
  LOG(("GetScreenBounds [%p] %d,%d -> %d x %d, unscaled %d,%d -> %d x %d\n",
2307
       this, rect.x, rect.y, rect.width, rect.height, rect.x / scale,
2308
       rect.y / scale, rect.width / scale, rect.height / scale));
2309
#endif
2310
  return rect;
2311
}
2312
2313
LayoutDeviceIntSize nsWindow::GetClientSize() {
2314
  return LayoutDeviceIntSize(mBounds.width, mBounds.height);
2315
}
2316
2317
LayoutDeviceIntRect nsWindow::GetClientBounds() {
2318
  // GetBounds returns a rect whose top left represents the top left of the
2319
  // outer bounds, but whose width/height represent the size of the inner
2320
  // bounds (which is messed up).
2321
  LayoutDeviceIntRect rect = GetBounds();
2322
  rect.MoveBy(GetClientOffset());
2323
  return rect;
2324
}
2325
2326
void nsWindow::UpdateClientOffsetFromFrameExtents() {
2327
  AUTO_PROFILER_LABEL("nsWindow::UpdateClientOffsetFromFrameExtents", OTHER);
2328
2329
  if (mGtkWindowDecoration == GTK_DECORATION_CLIENT && mDrawInTitlebar) {
2330
    return;
2331
  }
2332
2333
  if (!mIsTopLevel || !mShell ||
2334
      gtk_window_get_window_type(GTK_WINDOW(mShell)) == GTK_WINDOW_POPUP) {
2335
    mClientOffset = nsIntPoint(0, 0);
2336
    return;
2337
  }
2338
2339
  GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
2340
2341
  GdkAtom type_returned;
2342
  int format_returned;
2343
  int length_returned;
2344
  long* frame_extents;
2345
2346
  if (!gdk_property_get(gtk_widget_get_window(mShell),
2347
                        gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE),
2348
                        cardinal_atom,
2349
                        0,      // offset
2350
                        4 * 4,  // length
2351
                        FALSE,  // delete
2352
                        &type_returned, &format_returned, &length_returned,
2353
                        (guchar**)&frame_extents) ||
2354
      length_returned / sizeof(glong) != 4) {
2355
    mClientOffset = nsIntPoint(0, 0);
2356
  } else {
2357
    // data returned is in the order left, right, top, bottom
2358
    auto left = int32_t(frame_extents[0]);
2359
    auto top = int32_t(frame_extents[2]);
2360
    g_free(frame_extents);
2361
2362
    mClientOffset = nsIntPoint(left, top);
2363
  }
2364
2365
  // Send a WindowMoved notification. This ensures that BrowserParent
2366
  // picks up the new client offset and sends it to the child process
2367
  // if appropriate.
2368
  NotifyWindowMoved(mBounds.x, mBounds.y);
2369
2370
  LOG(("nsWindow::UpdateClientOffsetFromFrameExtents [%p] %d,%d\n", (void*)this,
2371
       mClientOffset.x, mClientOffset.y));
2372
}
2373
2374
LayoutDeviceIntPoint nsWindow::GetClientOffset() {
2375
  return mIsX11Display ? LayoutDeviceIntPoint::FromUnknownPoint(mClientOffset)
2376
                       : LayoutDeviceIntPoint(0, 0);
2377
}
2378
2379
gboolean nsWindow::OnPropertyNotifyEvent(GtkWidget* aWidget,
2380
                                         GdkEventProperty* aEvent) {
2381
  if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) {
2382
    UpdateClientOffsetFromFrameExtents();
2383
    return FALSE;
2384
  }
2385
2386
  if (GetCurrentTimeGetter()->PropertyNotifyHandler(aWidget, aEvent)) {
2387
    return TRUE;
2388
  }
2389
2390
  return FALSE;
2391
}
2392
2393
static GdkCursor* GetCursorForImage(const nsIWidget::Cursor& aCursor) {
2394
  if (!aCursor.IsCustom()) {
2395
    return nullptr;
2396
  }
2397
  nsIntSize size = nsIWidget::CustomCursorSize(aCursor);
2398
2399
  // NOTE: GTK only allows integer scale factors, so we ceil to the closest
2400
  // scale factor and then tell gtk to scale it down.
2401
  int32_t gtkScale = std::ceil(aCursor.mResolution);
2402
2403
  // Reject cursors greater than 128 pixels in some direction, to prevent
2404
  // spoofing.
2405
  // XXX ideally we should rescale. Also, we could modify the API to
2406
  // allow trusted content to set larger cursors.
2407
  //
2408
  // TODO(emilio, bug 1445844): Unify the solution for this with other
2409
  // platforms.
2410
  if (size.width > 128 || size.height > 128) {
2411
    return nullptr;
2412
  }
2413
2414
  nsIntSize rasterSize = size * gtkScale;
2415
  GdkPixbuf* pixbuf =
2416
      nsImageToPixbuf::ImageToPixbuf(aCursor.mContainer, Some(rasterSize));
2417
  if (!pixbuf) {
2418
    return nullptr;
2419
  }
2420
2421
  // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This
2422
  // is of course not documented anywhere...
2423
  // So add one if there isn't one yet
2424
  if (!gdk_pixbuf_get_has_alpha(pixbuf)) {
2425
    GdkPixbuf* alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
2426
    g_object_unref(pixbuf);
2427
    pixbuf = alphaBuf;
2428
    if (!alphaBuf) {
2429
      return nullptr;
2430
    }
2431
  }
2432
2433
  auto CleanupPixBuf =
2434
      mozilla::MakeScopeExit([&]() { g_object_unref(pixbuf); });
2435
2436
  cairo_surface_t* surface =
2437
      gdk_cairo_surface_create_from_pixbuf(pixbuf, gtkScale, nullptr);
2438
  if (!surface) {
2439
    return nullptr;
2440
  }
2441
2442
  auto CleanupSurface =
2443
      mozilla::MakeScopeExit([&]() { cairo_surface_destroy(surface); });
2444
2445
  return gdk_cursor_new_from_surface(gdk_display_get_default(), surface,
2446
                                     aCursor.mHotspotX, aCursor.mHotspotY);
2447
}
2448
2449
void nsWindow::SetCursor(const Cursor& aCursor) {
2450
  // if we're not the toplevel window pass up the cursor request to
2451
  // the toplevel window to handle it.
2452
  if (!mContainer && mGdkWindow) {
2453
    if (nsWindow* window = GetContainerWindow()) {
2454
      window->SetCursor(aCursor);
2455
    }
2456
    return;
2457
  }
2458
2459
  // Only change cursor if it's actually been changed
2460
  if (!mUpdateCursor && mCursor == aCursor) {
2461
    return;
2462
  }
2463
2464
  mUpdateCursor = false;
2465
  mCursor = aCursor;
2466
2467
  // Try to set the cursor image first, and fall back to the numeric cursor.
2468
  bool fromImage = true;
2469
  GdkCursor* newCursor = GetCursorForImage(aCursor);
2470
  if (!newCursor) {
2471
    fromImage = false;
2472
    newCursor = get_gtk_cursor(aCursor.mDefaultCursor);
2473
  }
2474
2475
  auto CleanupCursor = mozilla::MakeScopeExit([&]() {
2476
    // get_gtk_cursor returns a weak reference, which we shouldn't unref.
2477
    if (fromImage) {
2478
      g_object_unref(newCursor);
2479
    }
2480
  });
2481
2482
  if (!newCursor || !mContainer) {
2483
    return;
2484
  }
2485
2486
  gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)),
2487
                        newCursor);
2488
}
2489
2490
void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) {
2491
  if (!mGdkWindow) return;
2492
2493
  GdkRectangle rect = DevicePixelsToGdkRectRoundOut(aRect);
2494
  gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
2495
2496
  LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void*)this, rect.x, rect.y,
2497
           rect.width, rect.height));
2498
}
2499
2500
void* nsWindow::GetNativeData(uint32_t aDataType) {
2501
  switch (aDataType) {
2502
    case NS_NATIVE_WINDOW:
2503
    case NS_NATIVE_WIDGET: {
2504
      if (!mGdkWindow) return nullptr;
2505
2506
      return mGdkWindow;
2507
    }
2508
2509
    case NS_NATIVE_DISPLAY: {
2510
#ifdef MOZ_X11
2511
      GdkDisplay* gdkDisplay = gdk_display_get_default();
2512
      if (GdkIsX11Display(gdkDisplay)) {
2513
        return GDK_DISPLAY_XDISPLAY(gdkDisplay);
2514
      }
2515
#endif /* MOZ_X11 */
2516
      // Don't bother to return native display on Wayland as it's for
2517
      // X11 only NPAPI plugins.
2518
      return nullptr;
2519
    }
2520
    case NS_NATIVE_SHELLWIDGET:
2521
      return GetToplevelWidget();
2522
2523
    case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID:
2524
    case NS_NATIVE_SHAREABLE_WINDOW:
2525
      if (mIsX11Display) {
2526
        return (void*)GDK_WINDOW_XID(gdk_window_get_toplevel(mGdkWindow));
2527
      }
2528
      NS_WARNING(
2529
          "nsWindow::GetNativeData(): "
2530
          "NS_NATIVE_SHAREABLE_WINDOW / NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID is "
2531
          "not "
2532
          "handled on Wayland!");
2533
      return nullptr;
2534
    case NS_RAW_NATIVE_IME_CONTEXT: {
2535
      void* pseudoIMEContext = GetPseudoIMEContext();
2536
      if (pseudoIMEContext) {
2537
        return pseudoIMEContext;
2538
      }
2539
      // If IME context isn't available on this widget, we should set |this|
2540
      // instead of nullptr.
2541
      if (!mIMContext) {
2542
        return this;
2543
      }
2544
      return mIMContext.get();
2545
    }
2546
    case NS_NATIVE_OPENGL_CONTEXT:
2547
      return nullptr;
2548
    case NS_NATIVE_EGL_WINDOW: {
2549
      if (mIsX11Display) {
2550
        return mGdkWindow ? (void*)GDK_WINDOW_XID(mGdkWindow) : nullptr;
2551
      }
2552
#ifdef MOZ_WAYLAND
2553
      if (mContainer) {
2554
        return moz_container_wayland_get_egl_window(mContainer,
2555
                                                    GdkScaleFactor());
2556
      }
2557
#endif
2558
      return nullptr;
2559
    }
2560
    default:
2561
      NS_WARNING("nsWindow::GetNativeData called with bad value");
2562
      return nullptr;
2563
  }
2564
}
2565
2566
nsresult nsWindow::SetTitle(const nsAString& aTitle) {
2567
  if (!mShell) return NS_OK;
2568
2569
    // convert the string into utf8 and set the title.
2570
#define UTF8_FOLLOWBYTE(ch) (((ch)&0xC0) == 0x80)
2571
  NS_ConvertUTF16toUTF8 titleUTF8(aTitle);
2572
  if (titleUTF8.Length() > NS_WINDOW_TITLE_MAX_LENGTH) {
2573
    // Truncate overlong titles (bug 167315). Make sure we chop after a
2574
    // complete sequence by making sure the next char isn't a follow-byte.
2575
    uint32_t len = NS_WINDOW_TITLE_MAX_LENGTH;
2576
    while (UTF8_FOLLOWBYTE(titleUTF8[len])) --len;
2577
    titleUTF8.Truncate(len);
2578
  }
2579
  gtk_window_set_title(GTK_WINDOW(mShell), (const char*)titleUTF8.get());
2580
2581
  return NS_OK;
2582
}
2583
2584
void nsWindow::SetIcon(const nsAString& aIconSpec) {
2585
  if (!mShell) return;
2586
2587
  nsAutoCString iconName;
2588
2589
  if (aIconSpec.EqualsLiteral("default")) {
2590
    nsAutoString brandName;
2591
    WidgetUtils::GetBrandShortName(brandName);
2592
    if (brandName.IsEmpty()) {
2593
      brandName.AssignLiteral(u"Mozilla");
2594
    }
2595
    AppendUTF16toUTF8(brandName, iconName);
2596
    ToLowerCase(iconName);
2597
  } else {
2598
    AppendUTF16toUTF8(aIconSpec, iconName);
2599
  }
2600
2601
  nsCOMPtr<nsIFile> iconFile;
2602
  nsAutoCString path;
2603
2604
  gint* iconSizes = gtk_icon_theme_get_icon_sizes(gtk_icon_theme_get_default(),
2605
                                                  iconName.get());
2606
  bool foundIcon = (iconSizes[0] != 0);
2607
  g_free(iconSizes);
2608
2609
  if (!foundIcon) {
2610
    // Look for icons with the following suffixes appended to the base name
2611
    // The last two entries (for the old XPM format) will be ignored unless
2612
    // no icons are found using other suffixes. XPM icons are deprecated.
2613
2614
    const char16_t extensions[9][8] = {u".png",    u"16.png", u"32.png",
2615
                                       u"48.png",  u"64.png", u"128.png",
2616
                                       u"256.png", u".xpm",   u"16.xpm"};
2617
2618
    for (uint32_t i = 0; i < ArrayLength(extensions); i++) {
2619
      // Don't bother looking for XPM versions if we found a PNG.
2620
      if (i == ArrayLength(extensions) - 2 && foundIcon) break;
2621
2622
      ResolveIconName(aIconSpec, nsDependentString(extensions[i]),
2623
                      getter_AddRefs(iconFile));
2624
      if (iconFile) {
2625
        iconFile->GetNativePath(path);
2626
        GdkPixbuf* icon = gdk_pixbuf_new_from_file(path.get(), nullptr);
2627
        if (icon) {
2628
          gtk_icon_theme_add_builtin_icon(iconName.get(),
2629
                                          gdk_pixbuf_get_height(icon), icon);
2630
          g_object_unref(icon);
2631
          foundIcon = true;
2632
        }
2633
      }
2634
    }
2635
  }
2636
2637
  // leave the default icon intact if no matching icons were found
2638
  if (foundIcon) {
2639
    gtk_window_set_icon_name(GTK_WINDOW(mShell), iconName.get());
2640
  }
2641
}
2642
2643
LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
2644
  nsIntPoint origin(0, 0);
2645
  GetWindowOrigin(mGdkWindow, &origin.x, &origin.y);
2646
2647
  return GdkPointToDevicePixels({origin.x, origin.y});
2648
}
2649
2650
void nsWindow::CaptureMouse(bool aCapture) {
2651
  LOG(("CaptureMouse %p\n", (void*)this));
2652
2653
  if (!mGdkWindow) return;
2654
2655
  if (!mContainer) return;
2656
2657
  if (aCapture) {
2658
    gtk_grab_add(GTK_WIDGET(mContainer));
2659
    GrabPointer(GetLastUserInputTime());
2660
  } else {
2661
    ReleaseGrabs();
2662
    gtk_grab_remove(GTK_WIDGET(mContainer));
2663
  }
2664
}
2665
2666
void nsWindow::CaptureRollupEvents(nsIRollupListener* aListener,
2667
                                   bool aDoCapture) {
2668
  if (!mGdkWindow) return;
2669
2670
  if (!mContainer) return;
2671
2672
  LOG(("CaptureRollupEvents %p %i\n", this, int(aDoCapture)));
2673
2674
  if (aDoCapture) {
2675
    gRollupListener = aListener;
2676
    // Don't add a grab if a drag is in progress, or if the widget is a drag
2677
    // feedback popup. (panels with type="drag").
2678
    if (!mIsDragPopup && !nsWindow::DragInProgress()) {
2679
      gtk_grab_add(GTK_WIDGET(mContainer));
2680
      GrabPointer(GetLastUserInputTime());
2681
    }
2682
  } else {
2683
    if (!nsWindow::DragInProgress()) {
2684
      ReleaseGrabs();
2685
    }
2686
    // There may not have been a drag in process when aDoCapture was set,
2687
    // so make sure to remove any added grab.  This is a no-op if the grab
2688
    // was not added to this widget.
2689
    gtk_grab_remove(GTK_WIDGET(mContainer));
2690
    gRollupListener = nullptr;
2691
  }
2692
}
2693
2694
nsresult nsWindow::GetAttention(int32_t aCycleCount) {
2695
  LOG(("nsWindow::GetAttention [%p]\n", (void*)this));
2696
2697
  GtkWidget* top_window = GetToplevelWidget();
2698
  GtkWidget* top_focused_window =
2699
      gFocusWindow ? gFocusWindow->GetToplevelWidget() : nullptr;
2700
2701
  // Don't get attention if the window is focused anyway.
2702
  if (top_window && (gtk_widget_get_visible(top_window)) &&
2703
      top_window != top_focused_window) {
2704
    SetUrgencyHint(top_window, true);
2705
  }
2706
2707
  return NS_OK;
2708
}
2709
2710
bool nsWindow::HasPendingInputEvent() {
2711
  // This sucks, but gtk/gdk has no way to answer the question we want while
2712
  // excluding paint events, and there's no X API that will let us peek
2713
  // without blocking or removing.  To prevent event reordering, peek
2714
  // anything except expose events.  Reordering expose and others should be
2715
  // ok, hopefully.
2716
  bool haveEvent = false;
2717
#ifdef MOZ_X11
2718
  XEvent ev;
2719
  if (mIsX11Display) {
2720
    Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2721
    haveEvent = XCheckMaskEvent(
2722
        display,
2723
        KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
2724
            EnterWindowMask | LeaveWindowMask | PointerMotionMask |
2725
            PointerMotionHintMask | Button1MotionMask | Button2MotionMask |
2726
            Button3MotionMask | Button4MotionMask | Button5MotionMask |
2727
            ButtonMotionMask | KeymapStateMask | VisibilityChangeMask |
2728
            StructureNotifyMask | ResizeRedirectMask | SubstructureNotifyMask |
2729
            SubstructureRedirectMask | FocusChangeMask | PropertyChangeMask |
2730
            ColormapChangeMask | OwnerGrabButtonMask,
2731
        &ev);
2732
    if (haveEvent) {
2733
      XPutBackEvent(display, &ev);
2734
    }
2735
  }
2736
#endif
2737
  return haveEvent;
2738
}
2739
2740
#if 0
2741
#  ifdef DEBUG
2742
// Paint flashing code (disabled for cairo - see below)
2743
2744
#    define CAPS_LOCK_IS_ON \
2745
      (KeymapWrapper::AreModifiersCurrentlyActive(KeymapWrapper::CAPS_LOCK))
2746
2747
#    define WANT_PAINT_FLASHING (debug_WantPaintFlashing() && CAPS_LOCK_IS_ON)
2748
2749
#    ifdef MOZ_X11
2750
static void
2751
gdk_window_flash(GdkWindow *    aGdkWindow,
2752
                 unsigned int   aTimes,
2753
                 unsigned int   aInterval,  // Milliseconds
2754
                 GdkRegion *    aRegion)
2755
{
2756
  gint         x;
2757
  gint         y;
2758
  gint         width;
2759
  gint         height;
2760
  guint        i;
2761
  GdkGC *      gc = 0;
2762
  GdkColor     white;
2763
2764
  gdk_window_get_geometry(aGdkWindow,nullptr,nullptr,&width,&height);
2765
2766
  gdk_window_get_origin (aGdkWindow,
2767
                         &x,
2768
                         &y);
2769
2770
  gc = gdk_gc_new(gdk_get_default_root_window());
2771
2772
  white.pixel = WhitePixel(gdk_display,DefaultScreen(gdk_display));
2773
2774
  gdk_gc_set_foreground(gc,&white);
2775
  gdk_gc_set_function(gc,GDK_XOR);
2776
  gdk_gc_set_subwindow(gc,GDK_INCLUDE_INFERIORS);
2777
2778
  gdk_region_offset(aRegion, x, y);
2779
  gdk_gc_set_clip_region(gc, aRegion);
2780
2781
  /*
2782
   * Need to do this twice so that the XOR effect can replace
2783
   * the original window contents.
2784
   */
2785
  for (i = 0; i < aTimes * 2; i++)
2786
  {
2787
    gdk_draw_rectangle(gdk_get_default_root_window(),
2788
                       gc,
2789
                       TRUE,
2790
                       x,
2791
                       y,
2792
                       width,
2793
                       height);
2794
2795
    gdk_flush();
2796
2797
    PR_Sleep(PR_MillisecondsToInterval(aInterval));
2798
  }
2799
2800
  gdk_gc_destroy(gc);
2801
2802
  gdk_region_offset(aRegion, -x, -y);
2803
}
2804
#    endif /* MOZ_X11 */
2805
#  endif   // DEBUG
2806
#endif
2807
2808
#ifdef cairo_copy_clip_rectangle_list
2809
#  error "Looks like we're including Mozilla's cairo instead of system cairo"
2810
#endif
2811
static bool ExtractExposeRegion(LayoutDeviceIntRegion& aRegion, cairo_t* cr) {
2812
  cairo_rectangle_list_t* rects = cairo_copy_clip_rectangle_list(cr);
2813
  if (rects->status != CAIRO_STATUS_SUCCESS) {
2814
    NS_WARNING("Failed to obtain cairo rectangle list.");
2815
    return false;
2816
  }
2817
2818
  for (int i = 0; i < rects->num_rectangles; i++) {
2819
    const cairo_rectangle_t& r = rects->rectangles[i];
2820
    aRegion.Or(aRegion,
2821
               LayoutDeviceIntRect::Truncate(r.x, r.y, r.width, r.height));
2822
    LOGDRAW(("\t%f %f %f %f\n", r.x, r.y, r.width, r.height));
2823
  }
2824
2825
  cairo_rectangle_list_destroy(rects);
2826
  return true;
2827
}
2828
2829
#ifdef MOZ_WAYLAND
2830
void nsWindow::MaybeResumeCompositor() {
2831
  MOZ_RELEASE_ASSERT(NS_IsMainThread());
2832
2833
  if (mIsDestroyed || !mNeedsCompositorResume) {
2834
    return;
2835
  }
2836
2837
  if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) {
2838
    MOZ_ASSERT(mCompositorWidgetDelegate);
2839
    if (mCompositorWidgetDelegate) {
2840
      mCompositorInitiallyPaused = false;
2841
      mNeedsCompositorResume = false;
2842
      remoteRenderer->SendResumeAsync();
2843
    }
2844
    remoteRenderer->SendForcePresent();
2845
  }
2846
}
2847
2848
void nsWindow::CreateCompositorVsyncDispatcher() {
2849
  if (!mWaylandVsyncSource) {
2850
    nsBaseWidget::CreateCompositorVsyncDispatcher();
2851
    return;
2852
  }
2853
2854
  if (XRE_IsParentProcess()) {
2855
    if (!mCompositorVsyncDispatcherLock) {
2856
      mCompositorVsyncDispatcherLock =
2857
          MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
2858
    }
2859
    MutexAutoLock lock(*mCompositorVsyncDispatcherLock);
2860
    if (!mCompositorVsyncDispatcher) {
2861
      mCompositorVsyncDispatcher =
2862
          new CompositorVsyncDispatcher(mWaylandVsyncSource);
2863
    }
2864
  }
2865
}
2866
#endif
2867
2868
gboolean nsWindow::OnExposeEvent(cairo_t* cr) {
2869
  // Send any pending resize events so that layout can update.
2870
  // May run event loop.
2871
  MaybeDispatchResized();
2872
2873
  if (mIsDestroyed) {
2874
    return FALSE;
2875
  }
2876
2877
  // Windows that are not visible will be painted after they become visible.
2878
  if (!mGdkWindow || !mHasMappedToplevel) {
2879
    return FALSE;
2880
  }
2881
#ifdef MOZ_WAYLAND
2882
  if (!mIsX11Display && !moz_container_wayland_can_draw(mContainer)) {
2883
    return FALSE;
2884
  }
2885
#endif
2886
2887
  nsIWidgetListener* listener = GetListener();
2888
  if (!listener) return FALSE;
2889
2890
  LOGDRAW(("received expose event [%p] %p 0x%lx (rects follow):\n", this,
2891
           mGdkWindow, mIsX11Display ? gdk_x11_window_get_xid(mGdkWindow) : 0));
2892
  LayoutDeviceIntRegion exposeRegion;
2893
  if (!ExtractExposeRegion(exposeRegion, cr)) {
2894
    return FALSE;
2895
  }
2896
2897
  gint scale = GdkScaleFactor();
2898
  LayoutDeviceIntRegion region = exposeRegion;
2899
  region.ScaleRoundOut(scale, scale);
2900
2901
  if (GetLayerManager()->AsKnowsCompositor() && mCompositorSession) {
2902
    // We need to paint to the screen even if nothing changed, since if we
2903
    // don't have a compositing window manager, our pixels could be stale.
2904
    GetLayerManager()->SetNeedsComposite(true);
2905
    GetLayerManager()->SendInvalidRegion(region.ToUnknownRegion());
2906
  }
2907
2908
  RefPtr<nsWindow> strongThis(this);
2909
2910
  // Dispatch WillPaintWindow notification to allow scripts etc. to run
2911
  // before we paint
2912
  {
2913
    listener->WillPaintWindow(this);
2914
2915
    // If the window has been destroyed during the will paint notification,
2916
    // there is nothing left to do.
2917
    if (!mGdkWindow) return TRUE;
2918
2919
    // Re-get the listener since the will paint notification might have
2920
    // killed it.
2921
    listener = GetListener();
2922
    if (!listener) return FALSE;
2923
  }
2924
2925
  if (GetLayerManager()->AsKnowsCompositor() &&
2926
      GetLayerManager()->NeedsComposite()) {
2927
    GetLayerManager()->ScheduleComposite();
2928
    GetLayerManager()->SetNeedsComposite(false);
2929
  }
2930
2931
  // Our bounds may have changed after calling WillPaintWindow.  Clip
2932
  // to the new bounds here.  The region is relative to this
2933
  // window.
2934
  region.And(region, LayoutDeviceIntRect(0, 0, mBounds.width, mBounds.height));
2935
2936
  bool shaped = false;
2937
  if (eTransparencyTransparent == GetTransparencyMode()) {
2938
    auto window = static_cast<nsWindow*>(GetTopLevelWidget());
2939
    if (mTransparencyBitmapForTitlebar) {
2940
      if (mSizeState == nsSizeMode_Normal) {
2941
        window->UpdateTitlebarTransparencyBitmap();
2942
      } else {
2943
        window->ClearTransparencyBitmap();
2944
      }
2945
    } else {
2946
      if (mHasAlphaVisual) {
2947
        // Remove possible shape mask from when window manger was not
2948
        // previously compositing.
2949
        window->ClearTransparencyBitmap();
2950
      } else {
2951
        shaped = true;
2952
      }
2953
    }
2954
  }
2955
2956
  if (!shaped) {
2957
    GList* children = gdk_window_peek_children(mGdkWindow);
2958
    while (children) {
2959
      GdkWindow* gdkWin = GDK_WINDOW(children->data);
2960
      nsWindow* kid = get_window_for_gdk_window(gdkWin);
2961
      if (kid && gdk_window_is_visible(gdkWin)) {
2962
        AutoTArray<LayoutDeviceIntRect, 1> clipRects;
2963
        kid->GetWindowClipRegion(&clipRects);
2964
        LayoutDeviceIntRect bounds = kid->GetBounds();
2965
        for (uint32_t i = 0; i < clipRects.Length(); ++i) {
2966
          LayoutDeviceIntRect r = clipRects[i] + bounds.TopLeft();
2967
          region.Sub(region, r);
2968
        }
2969
      }
2970
      children = children->next;
2971
    }
2972
  }
2973
2974
  if (region.IsEmpty()) {
2975
    return TRUE;
2976
  }
2977
2978
  // If this widget uses OMTC...
2979
  if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT ||
2980
      GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR) {
2981
    listener->PaintWindow(this, region);
2982
2983
    // Re-get the listener since the will paint notification might have
2984
    // killed it.
2985
    listener = GetListener();
2986
    if (!listener) return TRUE;
2987
2988
    listener->DidPaintWindow();
2989
    return TRUE;
2990
  }
2991
2992
  BufferMode layerBuffering = BufferMode::BUFFERED;
2993
  RefPtr<DrawTarget> dt = StartRemoteDrawingInRegion(region, &layerBuffering);
2994
  if (!dt || !dt->IsValid()) {
2995
    return FALSE;
2996
  }
2997
  RefPtr<gfxContext> ctx;
2998
  IntRect boundsRect = region.GetBounds().ToUnknownRect();
2999
  IntPoint offset(0, 0);
3000
  if (dt->GetSize() == boundsRect.Size()) {
3001
    offset = boundsRect.TopLeft();
3002
    dt->SetTransform(Matrix::Translation(-offset));
3003
  }
3004
3005
#ifdef MOZ_X11
3006
  if (shaped) {
3007
    // Collapse update area to the bounding box. This is so we only have to
3008
    // call UpdateTranslucentWindowAlpha once. After we have dropped
3009
    // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
3010
    // our private interface so we can rework things to avoid this.
3011
    dt->PushClipRect(Rect(boundsRect));
3012
3013
    // The double buffering is done here to extract the shape mask.
3014
    // (The shape mask won't be necessary when a visual with an alpha
3015
    // channel is used on compositing window managers.)
3016
    layerBuffering = BufferMode::BUFFER_NONE;
3017
    RefPtr<DrawTarget> destDT =
3018
        dt->CreateSimilarDrawTarget(boundsRect.Size(), SurfaceFormat::B8G8R8A8);
3019
    if (!destDT || !destDT->IsValid()) {
3020
      return FALSE;
3021
    }
3022
    destDT->SetTransform(Matrix::Translation(-boundsRect.TopLeft()));
3023
    ctx = gfxContext::CreatePreservingTransformOrNull(destDT);
3024
  } else {
3025
    gfxUtils::ClipToRegion(dt, region.ToUnknownRegion());
3026
    ctx = gfxContext::CreatePreservingTransformOrNull(dt);
3027
  }
3028
  MOZ_ASSERT(ctx);  // checked both dt and destDT valid draw target above
3029
3030
#  if 0
3031
    // NOTE: Paint flashing region would be wrong for cairo, since
3032
    // cairo inflates the update region, etc.  So don't paint flash
3033
    // for cairo.
3034
#    ifdef DEBUG
3035
    // XXX aEvent->region may refer to a newly-invalid area.  FIXME
3036
    if (0 && WANT_PAINT_FLASHING && gtk_widget_get_window(aEvent))
3037
        gdk_window_flash(mGdkWindow, 1, 100, aEvent->region);
3038
#    endif
3039
#  endif
3040
3041
#endif  // MOZ_X11
3042
3043
  bool painted = false;
3044
  {
3045
    if (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) {
3046
      if (GetTransparencyMode() == eTransparencyTransparent &&
3047
          layerBuffering == BufferMode::BUFFER_NONE && mHasAlphaVisual) {
3048
        // If our draw target is unbuffered and we use an alpha channel,
3049
        // clear the image beforehand to ensure we don't get artifacts from a
3050
        // reused SHM image. See bug 1258086.
3051
        dt->ClearRect(Rect(boundsRect));
3052
      }
3053
      AutoLayerManagerSetup setupLayerManager(this, ctx, layerBuffering);
3054
      painted = listener->PaintWindow(this, region);
3055
3056
      // Re-get the listener since the will paint notification might have
3057
      // killed it.
3058
      listener = GetListener();
3059
      if (!listener) return TRUE;
3060
    }
3061
  }
3062
3063
#ifdef MOZ_X11
3064
  // PaintWindow can Destroy us (bug 378273), avoid doing any paint
3065
  // operations below if that happened - it will lead to XError and exit().
3066
  if (shaped) {
3067
    if (MOZ_LIKELY(!mIsDestroyed)) {
3068
      if (painted) {
3069
        RefPtr<SourceSurface> surf = ctx->GetDrawTarget()->Snapshot();
3070
3071
        UpdateAlpha(surf, boundsRect);
3072
3073
        dt->DrawSurface(surf, Rect(boundsRect),
3074
                        Rect(0, 0, boundsRect.width, boundsRect.height),
3075
                        DrawSurfaceOptions(SamplingFilter::POINT),
3076
                        DrawOptions(1.0f, CompositionOp::OP_SOURCE));
3077
      }
3078
    }
3079
  }
3080
3081
  ctx = nullptr;
3082
  dt->PopClip();
3083
3084
#endif  // MOZ_X11
3085
3086
  EndRemoteDrawingInRegion(dt, region);
3087
3088
  listener->DidPaintWindow();
3089
3090
  // Synchronously flush any new dirty areas
3091
  cairo_region_t* dirtyArea = gdk_window_get_update_area(mGdkWindow);
3092
3093
  if (dirtyArea) {
3094
    gdk_window_invalidate_region(mGdkWindow, dirtyArea, false);
3095
    cairo_region_destroy(dirtyArea);
3096
    gdk_window_process_updates(mGdkWindow, false);
3097
  }
3098
3099
  // check the return value!
3100
  return TRUE;
3101
}
3102
3103
void nsWindow::UpdateAlpha(SourceSurface* aSourceSurface,
3104
                           nsIntRect aBoundsRect) {
3105
  // We need to create our own buffer to force the stride to match the
3106
  // expected stride.
3107
  int32_t stride =
3108
      GetAlignedStride<4>(aBoundsRect.width, BytesPerPixel(SurfaceFormat::A8));
3109
  if (stride == 0) {
3110
    return;
3111
  }
3112
  int32_t bufferSize = stride * aBoundsRect.height;
3113
  auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize);
3114
  {
3115
    RefPtr<DrawTarget> drawTarget = gfxPlatform::CreateDrawTargetForData(
3116
        imageBuffer.get(), aBoundsRect.Size(), stride, SurfaceFormat::A8);
3117
3118
    if (drawTarget) {
3119
      drawTarget->DrawSurface(aSourceSurface,
3120
                              Rect(0, 0, aBoundsRect.width, aBoundsRect.height),
3121
                              Rect(0, 0, aSourceSurface->GetSize().width,
3122
                                   aSourceSurface->GetSize().height),
3123
                              DrawSurfaceOptions(SamplingFilter::POINT),
3124
                              DrawOptions(1.0f, CompositionOp::OP_SOURCE));
3125
    }
3126
  }
3127
  UpdateTranslucentWindowAlphaInternal(aBoundsRect, imageBuffer.get(), stride);
3128
}
3129
3130
gboolean nsWindow::OnConfigureEvent(GtkWidget* aWidget,
3131
                                    GdkEventConfigure* aEvent) {
3132
  // These events are only received on toplevel windows.
3133
  //
3134
  // GDK ensures that the coordinates are the client window top-left wrt the
3135
  // root window.
3136
  //
3137
  //   GDK calculates the cordinates for real ConfigureNotify events on
3138
  //   managed windows (that would normally be relative to the parent
3139
  //   window).
3140
  //
3141
  //   Synthetic ConfigureNotify events are from the window manager and
3142
  //   already relative to the root window.  GDK creates all X windows with
3143
  //   border_width = 0, so synthetic events also indicate the top-left of
3144
  //   the client window.
3145
  //
3146
  //   Override-redirect windows are children of the root window so parent
3147
  //   coordinates are root coordinates.
3148
3149
  LOG(("configure event [%p] %d %d %d %d\n", (void*)this, aEvent->x, aEvent->y,
3150
       aEvent->width, aEvent->height));
3151
3152
  if (mPendingConfigures > 0) {
3153
    mPendingConfigures--;
3154
  }
3155
3156
  LayoutDeviceIntRect screenBounds = GetScreenBounds();
3157
3158
  if (mWindowType == eWindowType_toplevel ||
3159
      mWindowType == eWindowType_dialog) {
3160
    // This check avoids unwanted rollup on spurious configure events from
3161
    // Cygwin/X (bug 672103).
3162
    if (mBounds.x != screenBounds.x || mBounds.y != screenBounds.y) {
3163
      CheckForRollup(0, 0, false, true);
3164
    }
3165
  }
3166
3167
  NS_ASSERTION(GTK_IS_WINDOW(aWidget),
3168
               "Configure event on widget that is not a GtkWindow");
3169
  if (gtk_window_get_window_type(GTK_WINDOW(aWidget)) == GTK_WINDOW_POPUP) {
3170
    // Override-redirect window
3171
    //
3172
    // These windows should not be moved by the window manager, and so any
3173
    // change in position is a result of our direction.  mBounds has
3174
    // already been set in std::move() or Resize(), and that is more
3175
    // up-to-date than the position in the ConfigureNotify event if the
3176
    // event is from an earlier window move.
3177
    //
3178
    // Skipping the WindowMoved call saves context menus from an infinite
3179
    // loop when nsXULPopupManager::PopupMoved moves the window to the new
3180
    // position and nsMenuPopupFrame::SetPopupPosition adds
3181
    // offsetForContextMenu on each iteration.
3182
3183
    // Our back buffer might have been invalidated while we drew the last
3184
    // frame, and its contents might be incorrect. See bug 1280653 comment 7
3185
    // and comment 10. Specifically we must ensure we recomposite the frame
3186
    // as soon as possible to avoid the corrupted frame being displayed.
3187
    GetLayerManager()->FlushRendering();
3188
    return FALSE;
3189
  }
3190
3191
  mBounds.MoveTo(screenBounds.TopLeft());
3192
3193
  // XXX mozilla will invalidate the entire window after this move
3194
  // complete.  wtf?
3195
  NotifyWindowMoved(mBounds.x, mBounds.y);
3196
3197
  // A GTK app would usually update its client area size in response to
3198
  // a "size-allocate" signal.
3199
  // However, we need to set mBounds in advance at Resize()
3200
  // as JS code expects immediate window size change.
3201
  // If Gecko requests a resize from GTK, but subsequently,
3202
  // before a corresponding "size-allocate" signal is emitted, the window is
3203
  // resized to its former size via other means, such as maximizing,
3204
  // then there is no "size-allocate" signal from which to update
3205
  // the value of mBounds. Similarly, if Gecko's resize request is refused
3206
  // by the window manager, then there will be no "size-allocate" signal.
3207
  // In the refused request case, the window manager is required to dispatch
3208
  // a ConfigureNotify event. mBounds can then be updated here.
3209
  // This seems to also be sufficient to update mBounds when Gecko resizes
3210
  // the window from maximized size and then immediately maximizes again.
3211
  if (!mBoundsAreValid) {
3212
    GtkAllocation allocation = {-1, -1, 0, 0};
3213
    gtk_window_get_size(GTK_WINDOW(mShell), &allocation.width,
3214
                        &allocation.height);
3215
    OnSizeAllocate(&allocation);
3216
  }
3217
3218
  return FALSE;
3219
}
3220
3221
void nsWindow::OnContainerUnrealize() {
3222
  // The GdkWindows are about to be destroyed (but not deleted), so remove
3223
  // their references back to their container widget while the GdkWindow
3224
  // hierarchy is still available.
3225
3226
  if (mGdkWindow) {
3227
    DestroyChildWindows();
3228
3229
    g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
3230
    mGdkWindow = nullptr;
3231
  }
3232
}
3233
3234
void nsWindow::OnSizeAllocate(GtkAllocation* aAllocation) {
3235
  LOG(("nsWindow::OnSizeAllocate [%p] %d,%d -> %d x %d\n", (void*)this,
3236
       aAllocation->x, aAllocation->y, aAllocation->width,
3237
       aAllocation->height));
3238
3239
  // Client offset are updated by _NET_FRAME_EXTENTS on X11 when system titlebar
3240
  // is enabled. In either cases (Wayland or system titlebar is off on X11)
3241
  // we don't get _NET_FRAME_EXTENTS X11 property notification so we derive
3242
  // it from mContainer position.
3243
  if (mGtkWindowDecoration == GTK_DECORATION_CLIENT) {
3244
    if (!mIsX11Display || (mIsX11Display && mDrawInTitlebar)) {
3245
      UpdateClientOffsetFromCSDWindow();
3246
    }
3247
  }
3248
3249
  mBoundsAreValid = true;
3250
3251
  LayoutDeviceIntSize size = GdkRectToDevicePixels(*aAllocation).Size();
3252
  if (mBounds.Size() == size) {
3253
    LOG(("  Already the same size"));
3254
    // We were already resized at nsWindow::OnConfigureEvent() so skip it.
3255
    return;
3256
  }
3257
3258
  // Invalidate the new part of the window now for the pending paint to
3259
  // minimize background flashes (GDK does not do this for external resizes
3260
  // of toplevels.)
3261
  if (mBounds.width < size.width) {
3262
    GdkRectangle rect = DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(
3263
        mBounds.width, 0, size.width - mBounds.width, size.height));
3264
    gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
3265
  }
3266
  if (mBounds.height < size.height) {
3267
    GdkRectangle rect = DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect(
3268
        0, mBounds.height, size.width, size.height - mBounds.height));
3269
    gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
3270
  }
3271
3272
  mBounds.SizeTo(size);
3273
3274
#ifdef MOZ_X11
3275
  // Notify the GtkCompositorWidget of a ClientSizeChange
3276
  if (mCompositorWidgetDelegate) {
3277
    mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
3278
  }
3279
#endif
3280
3281
  // Gecko permits running nested event loops during processing of events,
3282
  // GtkWindow callers of gtk_widget_size_allocate expect the signal
3283
  // handlers to return sometime in the near future.
3284
  mNeedsDispatchResized = true;
3285
  NS_DispatchToCurrentThread(NewRunnableMethod(
3286
      "nsWindow::MaybeDispatchResized", this, &nsWindow::MaybeDispatchResized));
3287
}
3288
3289
void nsWindow::OnDeleteEvent() {
3290
  if (mWidgetListener) mWidgetListener->RequestWindowClose(this);
3291
}
3292
3293
void nsWindow::OnEnterNotifyEvent(GdkEventCrossing* aEvent) {
3294
  // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events
3295
  // when the pointer enters a child window.  If the destination window is a
3296
  // Gecko window then we'll catch the corresponding event on that window,
3297
  // but we won't notice when the pointer directly enters a foreign (plugin)
3298
  // child window without passing over a visible portion of a Gecko window.
3299
  if (aEvent->subwindow != nullptr) return;
3300
3301
  // Check before is_parent_ungrab_enter() as the button state may have
3302
  // changed while a non-Gecko ancestor window had a pointer grab.
3303
  DispatchMissedButtonReleases(aEvent);
3304
3305
  if (is_parent_ungrab_enter(aEvent)) return;
3306
3307
  WidgetMouseEvent event(true, eMouseEnterIntoWidget, this,
3308
                         WidgetMouseEvent::eReal);
3309
3310
  event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
3311
  event.AssignEventTime(GetWidgetEventTime(aEvent->time));
3312
3313
  LOG(("OnEnterNotify: %p\n", (void*)this));
3314
3315
  DispatchInputEvent(&event);
3316
}
3317
3318
// XXX Is this the right test for embedding cases?
3319
static bool is_top_level_mouse_exit(GdkWindow* aWindow,
3320
                                    GdkEventCrossing* aEvent) {
3321
  auto x = gint(aEvent->x_root);
3322
  auto y = gint(aEvent->y_root);
3323
  GdkDisplay* display = gdk_window_get_display(aWindow);
3324
  GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
3325
  if (!winAtPt) return true;
3326
  GdkWindow* topLevelAtPt = gdk_window_get_toplevel(winAtPt);
3327
  GdkWindow* topLevelWidget = gdk_window_get_toplevel(aWindow);
3328
  return topLevelAtPt != topLevelWidget;
3329
}
3330
3331
void nsWindow::OnLeaveNotifyEvent(GdkEventCrossing* aEvent) {
3332
  // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify
3333
  // events when the pointer leaves a child window.  If the destination
3334
  // window is a Gecko window then we'll catch the corresponding event on
3335
  // that window.
3336
  //
3337
  // XXXkt However, we will miss toplevel exits when the pointer directly
3338
  // leaves a foreign (plugin) child window without passing over a visible
3339
  // portion of a Gecko window.
3340
  if (aEvent->subwindow != nullptr) return;
3341
3342
  WidgetMouseEvent event(true, eMouseExitFromWidget, this,
3343
                         WidgetMouseEvent::eReal);
3344
3345
  event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
3346
  event.AssignEventTime(GetWidgetEventTime(aEvent->time));
3347
3348
  event.mExitFrom = Some(is_top_level_mouse_exit(mGdkWindow, aEvent)
3349
                             ? WidgetMouseEvent::ePlatformTopLevel
3350
                             : WidgetMouseEvent::ePlatformChild);
3351
3352
  LOG(("OnLeaveNotify: %p\n", (void*)this));
3353
3354
  DispatchInputEvent(&event);
3355
}
3356
3357
bool nsWindow::CheckResizerEdge(LayoutDeviceIntPoint aPoint,
3358
                                GdkWindowEdge& aOutEdge) {
3359
  // We only need to handle resizers for PIP window.
3360
  if (!mIsPIPWindow) {
3361
    return false;
3362
  }
3363
3364
  // Don't allow resizing maximized windows.
3365
  if (mSizeState != nsSizeMode_Normal) {
3366
    return false;
3367
  }
3368
3369
#define RESIZER_SIZE 15
3370
  int resizerSize = RESIZER_SIZE * GdkScaleFactor();
3371
  int topDist = aPoint.y;
3372
  int leftDist = aPoint.x;
3373
  int rightDist = mBounds.width - aPoint.x;
3374
  int bottomDist = mBounds.height - aPoint.y;
3375
3376
  if (leftDist <= resizerSize && topDist <= resizerSize) {
3377
    aOutEdge = GDK_WINDOW_EDGE_NORTH_WEST;
3378
  } else if (rightDist <= resizerSize && topDist <= resizerSize) {
3379
    aOutEdge = GDK_WINDOW_EDGE_NORTH_EAST;
3380
  } else if (leftDist <= resizerSize && bottomDist <= resizerSize) {
3381
    aOutEdge = GDK_WINDOW_EDGE_SOUTH_WEST;
3382
  } else if (rightDist <= resizerSize && bottomDist <= resizerSize) {
3383
    aOutEdge = GDK_WINDOW_EDGE_SOUTH_EAST;
3384
  } else if (topDist <= resizerSize) {
3385
    aOutEdge = GDK_WINDOW_EDGE_NORTH;
3386
  } else if (leftDist <= resizerSize) {
3387
    aOutEdge = GDK_WINDOW_EDGE_WEST;
3388
  } else if (rightDist <= resizerSize) {
3389
    aOutEdge = GDK_WINDOW_EDGE_EAST;
3390
  } else if (bottomDist <= resizerSize) {
3391
    aOutEdge = GDK_WINDOW_EDGE_SOUTH;
3392
  } else {
3393
    return false;
3394
  }
3395
  return true;
3396
}
3397
3398
template <typename Event>
3399
static LayoutDeviceIntPoint GetRefPoint(nsWindow* aWindow, Event* aEvent) {
3400
  if (aEvent->window == aWindow->GetGdkWindow()) {
3401
    // we are the window that the event happened on so no need for expensive
3402
    // WidgetToScreenOffset
3403
    return aWindow->GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
3404
  }
3405
  // XXX we're never quite sure which GdkWindow the event came from due to our
3406
  // custom bubbling in scroll_event_cb(), so use ScreenToWidget to translate
3407
  // the screen root coordinates into coordinates relative to this widget.
3408
  return aWindow->GdkEventCoordsToDevicePixels(aEvent->x_root, aEvent->y_root) -
3409
         aWindow->WidgetToScreenOffset();
3410
}
3411
3412
void nsWindow::OnMotionNotifyEvent(GdkEventMotion* aEvent) {
3413
  if (mWindowShouldStartDragging) {
3414
    mWindowShouldStartDragging = false;
3415
    // find the top-level window
3416
    GdkWindow* gdk_window = gdk_window_get_toplevel(mGdkWindow);
3417
    MOZ_ASSERT(gdk_window, "gdk_window_get_toplevel should not return null");
3418
3419
    bool canDrag = true;
3420
    if (mIsX11Display) {
3421
      // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=789054
3422
      // To avoid crashes disable double-click on WM without _NET_WM_MOVERESIZE.
3423
      // See _should_perform_ewmh_drag() at gdkwindow-x11.c
3424
      GdkScreen* screen = gdk_window_get_screen(gdk_window);
3425
      GdkAtom atom = gdk_atom_intern("_NET_WM_MOVERESIZE", FALSE);
3426
      if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) {
3427
        canDrag = false;
3428
      }
3429
    }
3430
3431
    if (canDrag) {
3432
      gdk_window_begin_move_drag(gdk_window, 1, aEvent->x_root, aEvent->y_root,
3433
                                 aEvent->time);
3434
      return;
3435
    }
3436
  }
3437
3438
  // see if we can compress this event
3439
  // XXXldb Why skip every other motion event when we have multiple,
3440
  // but not more than that?
3441
  bool synthEvent = false;
3442
#ifdef MOZ_X11
3443
  XEvent xevent;
3444
3445
  if (mIsX11Display) {
3446
    while (XPending(GDK_WINDOW_XDISPLAY(aEvent->window))) {
3447
      XEvent peeked;
3448
      XPeekEvent(GDK_WINDOW_XDISPLAY(aEvent->window), &peeked);
3449
      if (peeked.xany.window != gdk_x11_window_get_xid(aEvent->window) ||
3450
          peeked.type != MotionNotify)
3451
        break;
3452
3453
      synthEvent = true;
3454
      XNextEvent(GDK_WINDOW_XDISPLAY(aEvent->window), &xevent);
3455
    }
3456
  }
3457
#endif /* MOZ_X11 */
3458
3459
  GdkWindowEdge edge;
3460
  if (CheckResizerEdge(GetRefPoint(this, aEvent), edge)) {
3461
    nsCursor cursor = eCursor_none;
3462
    switch (edge) {
3463
      case GDK_WINDOW_EDGE_NORTH:
3464
        cursor = eCursor_n_resize;
3465
        break;
3466
      case GDK_WINDOW_EDGE_NORTH_WEST:
3467
        cursor = eCursor_nw_resize;
3468
        break;
3469
      case GDK_WINDOW_EDGE_NORTH_EAST:
3470
        cursor = eCursor_ne_resize;
3471
        break;
3472
      case GDK_WINDOW_EDGE_WEST:
3473
        cursor = eCursor_w_resize;
3474
        break;
3475
      case GDK_WINDOW_EDGE_EAST:
3476
        cursor = eCursor_e_resize;
3477
        break;
3478
      case GDK_WINDOW_EDGE_SOUTH:
3479
        cursor = eCursor_s_resize;
3480
        break;
3481
      case GDK_WINDOW_EDGE_SOUTH_WEST:
3482
        cursor = eCursor_sw_resize;
3483
        break;
3484
      case GDK_WINDOW_EDGE_SOUTH_EAST:
3485
        cursor = eCursor_se_resize;
3486
        break;
3487
    }
3488
    SetCursor(Cursor{cursor});
3489
    return;
3490
  }
3491
3492
  WidgetMouseEvent event(true, eMouseMove, this, WidgetMouseEvent::eReal);
3493
3494
  gdouble pressure = 0;
3495
  gdk_event_get_axis((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
3496
  // Sometime gdk generate 0 pressure value between normal values
3497
  // We have to ignore that and use last valid value
3498
  if (pressure) mLastMotionPressure = pressure;
3499
  event.mPressure = mLastMotionPressure;
3500
3501
  guint modifierState;
3502
  if (synthEvent) {
3503
#ifdef MOZ_X11
3504
    event.mRefPoint.x = nscoord(xevent.xmotion.x);
3505
    event.mRefPoint.y = nscoord(xevent.xmotion.y);
3506
3507
    modifierState = xevent.xmotion.state;
3508
3509
    event.AssignEventTime(GetWidgetEventTime(xevent.xmotion.time));
3510
#else
3511
    event.mRefPoint = GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
3512
3513
    modifierState = aEvent->state;
3514
3515
    event.AssignEventTime(GetWidgetEventTime(aEvent->time));
3516
#endif /* MOZ_X11 */
3517
  } else {
3518
    event.mRefPoint = GetRefPoint(this, aEvent);
3519
3520
    modifierState = aEvent->state;
3521
3522
    event.AssignEventTime(GetWidgetEventTime(aEvent->time));
3523
  }
3524
3525
  KeymapWrapper::InitInputEvent(event, modifierState);
3526
3527
  DispatchInputEvent(&event);
3528
}
3529
3530
// If the automatic pointer grab on ButtonPress has deactivated before
3531
// ButtonRelease, and the mouse button is released while the pointer is not
3532
// over any a Gecko window, then the ButtonRelease event will not be received.
3533
// (A similar situation exists when the pointer is grabbed with owner_events
3534
// True as the ButtonRelease may be received on a foreign [plugin] window).
3535
// Use this method to check for released buttons when the pointer returns to a
3536
// Gecko window.
3537
void nsWindow::DispatchMissedButtonReleases(GdkEventCrossing* aGdkEvent) {
3538
  guint changed = aGdkEvent->state ^ gButtonState;
3539
  // Only consider button releases.
3540
  // (Ignore button presses that occurred outside Gecko.)
3541
  guint released = changed & gButtonState;
3542
  gButtonState = aGdkEvent->state;
3543
3544
  // Loop over each button, excluding mouse wheel buttons 4 and 5 for which
3545
  // GDK ignores releases.
3546
  for (guint buttonMask = GDK_BUTTON1_MASK; buttonMask <= GDK_BUTTON3_MASK;
3547
       buttonMask <<= 1) {
3548
    if (released & buttonMask) {
3549
      int16_t buttonType;
3550
      switch (buttonMask) {
3551
        case GDK_BUTTON1_MASK:
3552
          buttonType = MouseButton::ePrimary;
3553
          break;
3554
        case GDK_BUTTON2_MASK:
3555
          buttonType = MouseButton::eMiddle;
3556
          break;
3557
        default:
3558
          NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK,
3559
                       "Unexpected button mask");
3560
          buttonType = MouseButton::eSecondary;
3561
      }
3562
3563
      LOG(("Synthesized button %u release on %p\n", guint(buttonType + 1),
3564
           (void*)this));
3565
3566
      // Dispatch a synthesized button up event to tell Gecko about the
3567
      // change in state.  This event is marked as synthesized so that
3568
      // it is not dispatched as a DOM event, because we don't know the
3569
      // position, widget, modifiers, or time/order.
3570
      WidgetMouseEvent synthEvent(true, eMouseUp, this,
3571
                                  WidgetMouseEvent::eSynthesized);
3572
      synthEvent.mButton = buttonType;
3573
      DispatchInputEvent(&synthEvent);
3574
    }
3575
  }
3576
}
3577
3578
void nsWindow::InitButtonEvent(WidgetMouseEvent& aEvent,
3579
                               GdkEventButton* aGdkEvent) {
3580
  aEvent.mRefPoint = GetRefPoint(this, aGdkEvent);
3581
3582
  guint modifierState = aGdkEvent->state;
3583
  // aEvent's state includes the button state from immediately before this
3584
  // event.  If aEvent is a mousedown or mouseup event, we need to update
3585
  // the button state.
3586
  guint buttonMask = 0;
3587
  switch (aGdkEvent->button) {
3588
    case 1:
3589
      buttonMask = GDK_BUTTON1_MASK;
3590
      break;
3591
    case 2:
3592
      buttonMask = GDK_BUTTON2_MASK;
3593
      break;
3594
    case 3:
3595
      buttonMask = GDK_BUTTON3_MASK;
3596
      break;
3597
  }
3598
  if (aGdkEvent->type == GDK_BUTTON_RELEASE) {
3599
    modifierState &= ~buttonMask;
3600
  } else {
3601
    modifierState |= buttonMask;
3602
  }
3603
3604
  KeymapWrapper::InitInputEvent(aEvent, modifierState);
3605
3606
  aEvent.AssignEventTime(GetWidgetEventTime(aGdkEvent->time));
3607
3608
  switch (aGdkEvent->type) {
3609
    case GDK_2BUTTON_PRESS:
3610
      aEvent.mClickCount = 2;
3611
      break;
3612
    case GDK_3BUTTON_PRESS:
3613
      aEvent.mClickCount = 3;
3614
      break;
3615
      // default is one click
3616
    default:
3617
      aEvent.mClickCount = 1;
3618
  }
3619
}
3620
3621
static guint ButtonMaskFromGDKButton(guint button) {
3622
  return GDK_BUTTON1_MASK << (button - 1);
3623
}
3624
3625
void nsWindow::DispatchContextMenuEventFromMouseEvent(uint16_t domButton,
3626
                                                      GdkEventButton* aEvent) {
3627
  if (domButton == MouseButton::eSecondary && MOZ_LIKELY(!mIsDestroyed)) {
3628
    WidgetMouseEvent contextMenuEvent(true, eContextMenu, this,
3629
                                      WidgetMouseEvent::eReal);
3630
    InitButtonEvent(contextMenuEvent, aEvent);
3631
    contextMenuEvent.mPressure = mLastMotionPressure;
3632
    DispatchInputEvent(&contextMenuEvent);
3633
  }
3634
}
3635
3636
void nsWindow::OnButtonPressEvent(GdkEventButton* aEvent) {
3637
  LOG(("Button %u press on %p\n", aEvent->button, (void*)this));
3638
3639
  // If you double click in GDK, it will actually generate a second
3640
  // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is
3641
  // different than the DOM spec.  GDK puts this in the queue
3642
  // programatically, so it's safe to assume that if there's a
3643
  // double click in the queue, it was generated so we can just drop
3644
  // this click.
3645
  GdkEvent* peekedEvent = gdk_event_peek();
3646
  if (peekedEvent) {
3647
    GdkEventType type = peekedEvent->any.type;
3648
    gdk_event_free(peekedEvent);
3649
    if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS) return;
3650
  }
3651
3652
  nsWindow* containerWindow = GetContainerWindow();
3653
  if (!gFocusWindow && containerWindow) {
3654
    containerWindow->DispatchActivateEvent();
3655
  }
3656
3657
  // check to see if we should rollup
3658
  if (CheckForRollup(aEvent->x_root, aEvent->y_root, false, false)) return;
3659
3660
  // Check to see if the event is within our window's resize region
3661
  GdkWindowEdge edge;
3662
  if (CheckResizerEdge(GetRefPoint(this, aEvent), edge)) {
3663
    gdk_window_begin_resize_drag(gtk_widget_get_window(mShell), edge,
3664
                                 aEvent->button, aEvent->x_root, aEvent->y_root,
3665
                                 aEvent->time);
3666
    return;
3667
  }
3668
3669
  gdouble pressure = 0;
3670
  gdk_event_get_axis((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
3671
  mLastMotionPressure = pressure;
3672
3673
  uint16_t domButton;
3674
  switch (aEvent->button) {
3675
    case 1:
3676
      domButton = MouseButton::ePrimary;
3677
      break;
3678
    case 2:
3679
      domButton = MouseButton::eMiddle;
3680
      break;
3681
    case 3:
3682
      domButton = MouseButton::eSecondary;
3683
      break;
3684
    // These are mapped to horizontal scroll
3685
    case 6:
3686
    case 7:
3687
      NS_WARNING("We're not supporting legacy horizontal scroll event");
3688
      return;
3689
    // Map buttons 8-9 to back/forward
3690
    case 8:
3691
      if (!Preferences::GetBool("mousebutton.4th.enabled", true)) {
3692
        return;
3693
      }
3694
      DispatchCommandEvent(nsGkAtoms::Back);
3695
      return;
3696
    case 9:
3697
      if (!Preferences::GetBool("mousebutton.5th.enabled", true)) {
3698
        return;
3699
      }
3700
      DispatchCommandEvent(nsGkAtoms::Forward);
3701
      return;
3702
    default:
3703
      return;
3704
  }
3705
3706
  gButtonState |= ButtonMaskFromGDKButton(aEvent->button);
3707
3708
  WidgetMouseEvent event(true, eMouseDown, this, WidgetMouseEvent::eReal);
3709
  event.mButton = domButton;
3710
  InitButtonEvent(event, aEvent);
3711
  event.mPressure = mLastMotionPressure;
3712
3713
  nsIWidget::ContentAndAPZEventStatus eventStatus = DispatchInputEvent(&event);
3714
3715
  LayoutDeviceIntPoint refPoint =
3716
      GdkEventCoordsToDevicePixels(aEvent->x, aEvent->y);
3717
  if ((mIsWaylandPanelWindow || mDraggableRegion.Contains(refPoint.x, refPoint.y)) &&
3718
      domButton == MouseButton::ePrimary &&
3719
      eventStatus.mContentStatus != nsEventStatus_eConsumeNoDefault) {
3720
    mWindowShouldStartDragging = true;
3721
  }
3722
3723
  // right menu click on linux should also pop up a context menu
3724
  if (!StaticPrefs::ui_context_menus_after_mouseup() &&
3725
      eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
3726
    DispatchContextMenuEventFromMouseEvent(domButton, aEvent);
3727
  }
3728
}
3729
3730
void nsWindow::OnButtonReleaseEvent(GdkEventButton* aEvent) {
3731
  LOG(("Button %u release on %p\n", aEvent->button, (void*)this));
3732
3733
  if (mWindowShouldStartDragging) {
3734
    mWindowShouldStartDragging = false;
3735
  }
3736
3737
  uint16_t domButton;
3738
  switch (aEvent->button) {
3739
    case 1:
3740
      domButton = MouseButton::ePrimary;
3741
      break;
3742
    case 2:
3743
      domButton = MouseButton::eMiddle;
3744
      break;
3745
    case 3:
3746
      domButton = MouseButton::eSecondary;
3747
      break;
3748
    default:
3749
      return;
3750
  }
3751
3752
  gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button);
3753
3754
  WidgetMouseEvent event(true, eMouseUp, this, WidgetMouseEvent::eReal);
3755
  event.mButton = domButton;
3756
  InitButtonEvent(event, aEvent);
3757
  gdouble pressure = 0;
3758
  gdk_event_get_axis((GdkEvent*)aEvent, GDK_AXIS_PRESSURE, &pressure);
3759
  event.mPressure = pressure ? pressure : mLastMotionPressure;
3760
3761
  // The mRefPoint is manipulated in DispatchInputEvent, we're saving it
3762
  // to use it for the doubleclick position check.
3763
  LayoutDeviceIntPoint pos = event.mRefPoint;
3764
3765
  nsIWidget::ContentAndAPZEventStatus eventStatus = DispatchInputEvent(&event);
3766
3767
  bool defaultPrevented =
3768
      (eventStatus.mContentStatus == nsEventStatus_eConsumeNoDefault);
3769
  // Check if mouse position in titlebar and doubleclick happened to
3770
  // trigger restore/maximize.
3771
  if (!defaultPrevented && mDrawInTitlebar &&
3772
      event.mButton == MouseButton::ePrimary && event.mClickCount == 2 &&
3773
      mDraggableRegion.Contains(pos.x, pos.y)) {
3774
    if (mSizeState == nsSizeMode_Maximized) {
3775
      SetSizeMode(nsSizeMode_Normal);
3776
    } else {
3777
      SetSizeMode(nsSizeMode_Maximized);
3778
    }
3779
  }
3780
  mLastMotionPressure = pressure;
3781
3782
  // right menu click on linux should also pop up a context menu
3783
  if (StaticPrefs::ui_context_menus_after_mouseup() &&
3784
      eventStatus.mApzStatus != nsEventStatus_eConsumeNoDefault) {
3785
    DispatchContextMenuEventFromMouseEvent(domButton, aEvent);
3786
  }
3787
3788
  // Open window manager menu on PIP window to allow user
3789
  // to place it on top / all workspaces.
3790
  if (mIsPIPWindow && aEvent->button == 3) {
3791
    static auto sGdkWindowShowWindowMenu =
3792
        (gboolean(*)(GdkWindow * window, GdkEvent*))
3793
            dlsym(RTLD_DEFAULT, "gdk_window_show_window_menu");
3794
    if (sGdkWindowShowWindowMenu) {
3795
      sGdkWindowShowWindowMenu(mGdkWindow, (GdkEvent*)aEvent);
3796
    }
3797
  }
3798
}
3799
3800
void nsWindow::OnContainerFocusInEvent(GdkEventFocus* aEvent) {
3801
  LOGFOCUS(("OnContainerFocusInEvent [%p]\n", (void*)this));
3802
3803
  // Unset the urgency hint, if possible
3804
  GtkWidget* top_window = GetToplevelWidget();
3805
  if (top_window && (gtk_widget_get_visible(top_window)))
3806
    SetUrgencyHint(top_window, false);
3807
3808
  // Return if being called within SetFocus because the focus manager
3809
  // already knows that the window is active.
3810
  if (gBlockActivateEvent) {
3811
    LOGFOCUS(("activated notification is blocked [%p]\n", (void*)this));
3812
    return;
3813
  }
3814
3815
  // If keyboard input will be accepted, the focus manager will call
3816
  // SetFocus to set the correct window.
3817
  gFocusWindow = nullptr;
3818
3819
  DispatchActivateEvent();
3820
3821
  if (!gFocusWindow) {
3822
    // We don't really have a window for dispatching key events, but
3823
    // setting a non-nullptr value here prevents OnButtonPressEvent() from
3824
    // dispatching an activation notification if the widget is already
3825
    // active.
3826
    gFocusWindow = this;
3827
  }
3828
3829
  LOGFOCUS(("Events sent from focus in event [%p]\n", (void*)this));
3830
}
3831
3832
void nsWindow::OnContainerFocusOutEvent(GdkEventFocus* aEvent) {
3833
  LOGFOCUS(("OnContainerFocusOutEvent [%p]\n", (void*)this));
3834
3835
  if (mWindowType == eWindowType_toplevel ||
3836
      mWindowType == eWindowType_dialog) {
3837
    nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
3838
    nsCOMPtr<nsIDragSession> dragSession;
3839
    dragService->GetCurrentSession(getter_AddRefs(dragSession));
3840
3841
    // Rollup popups when a window is focused out unless a drag is occurring.
3842
    // This check is because drags grab the keyboard and cause a focus out on
3843
    // versions of GTK before 2.18.
3844
    bool shouldRollup = !dragSession;
3845
    if (!shouldRollup) {
3846
      // we also roll up when a drag is from a different application
3847
      nsCOMPtr<nsINode> sourceNode;
3848
      dragSession->GetSourceNode(getter_AddRefs(sourceNode));
3849
      shouldRollup = (sourceNode == nullptr);
3850
    }
3851
3852
    if (shouldRollup) {
3853
      CheckForRollup(0, 0, false, true);
3854
    }
3855
  }
3856
3857
  if (gFocusWindow) {
3858
    RefPtr<nsWindow> kungFuDeathGrip = gFocusWindow;
3859
    if (gFocusWindow->mIMContext) {
3860
      gFocusWindow->mIMContext->OnBlurWindow(gFocusWindow);
3861
    }
3862
    gFocusWindow = nullptr;
3863
  }
3864
3865
  DispatchDeactivateEvent();
3866
3867
  if (IsChromeWindowTitlebar()) {
3868
    // DispatchDeactivateEvent() ultimately results in a call to
3869
    // BrowsingContext::SetIsActiveBrowserWindow(), which resets
3870
    // the state.  We call UpdateMozWindowActive() to keep it in
3871
    // sync with GDK_WINDOW_STATE_FOCUSED.
3872
    UpdateMozWindowActive();
3873
  }
3874
3875
  LOGFOCUS(("Done with container focus out [%p]\n", (void*)this));
3876
}
3877
3878
bool nsWindow::DispatchCommandEvent(nsAtom* aCommand) {
3879
  nsEventStatus status;
3880
  WidgetCommandEvent appCommandEvent(true, aCommand, this);
3881
  DispatchEvent(&appCommandEvent, status);
3882
  return TRUE;
3883
}
3884
3885
bool nsWindow::DispatchContentCommandEvent(EventMessage aMsg) {
3886
  nsEventStatus status;
3887
  WidgetContentCommandEvent event(true, aMsg, this);
3888
  DispatchEvent(&event, status);
3889
  return TRUE;
3890
}
3891
3892
WidgetEventTime nsWindow::GetWidgetEventTime(guint32 aEventTime) {
3893
  return WidgetEventTime(aEventTime, GetEventTimeStamp(aEventTime));
3894
}
3895
3896
TimeStamp nsWindow::GetEventTimeStamp(guint32 aEventTime) {
3897
  if (MOZ_UNLIKELY(!mGdkWindow)) {
3898
    // nsWindow has been Destroy()ed.
3899
    return TimeStamp::Now();
3900
  }
3901
  if (aEventTime == 0) {
3902
    // Some X11 and GDK events may be received with a time of 0 to indicate
3903
    // that they are synthetic events. Some input method editors do this.
3904
    // In this case too, just return the current timestamp.
3905
    return TimeStamp::Now();
3906
  }
3907
3908
  TimeStamp eventTimeStamp;
3909
3910
  if (!mIsX11Display) {
3911
    // Wayland compositors use monotonic time to set timestamps.
3912
    int64_t timestampTime = g_get_monotonic_time() / 1000;
3913
    guint32 refTimeTruncated = guint32(timestampTime);
3914
3915
    timestampTime -= refTimeTruncated - aEventTime;
3916
    int64_t tick =
3917
        BaseTimeDurationPlatformUtils::TicksFromMilliseconds(timestampTime);
3918
    eventTimeStamp = TimeStamp::FromSystemTime(tick);
3919
  } else {
3920
    CurrentX11TimeGetter* getCurrentTime = GetCurrentTimeGetter();
3921
    MOZ_ASSERT(getCurrentTime,
3922
               "Null current time getter despite having a window");
3923
    eventTimeStamp =
3924
        TimeConverter().GetTimeStampFromSystemTime(aEventTime, *getCurrentTime);
3925
  }
3926
  return eventTimeStamp;
3927
}
3928
3929
mozilla::CurrentX11TimeGetter* nsWindow::GetCurrentTimeGetter() {
3930
  MOZ_ASSERT(mGdkWindow, "Expected mGdkWindow to be set");
3931
  if (MOZ_UNLIKELY(!mCurrentTimeGetter)) {
3932
    mCurrentTimeGetter = MakeUnique<CurrentX11TimeGetter>(mGdkWindow);
3933
  }
3934
  return mCurrentTimeGetter.get();
3935
}
3936
3937
gboolean nsWindow::OnKeyPressEvent(GdkEventKey* aEvent) {
3938
  LOGFOCUS(("OnKeyPressEvent [%p]\n", (void*)this));
3939
3940
  RefPtr<nsWindow> self(this);
3941
  KeymapWrapper::HandleKeyPressEvent(self, aEvent);
3942
  return TRUE;
3943
}
3944
3945
gboolean nsWindow::OnKeyReleaseEvent(GdkEventKey* aEvent) {
3946
  LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void*)this));
3947
3948
  RefPtr<nsWindow> self(this);
3949
  if (NS_WARN_IF(!KeymapWrapper::HandleKeyReleaseEvent(self, aEvent))) {
3950
    return FALSE;
3951
  }
3952
  return TRUE;
3953
}
3954
3955
void nsWindow::OnScrollEvent(GdkEventScroll* aEvent) {
3956
  // check to see if we should rollup
3957
  if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false)) return;
3958
  // check for duplicate legacy scroll event, see GNOME bug 726878
3959
  if (aEvent->direction != GDK_SCROLL_SMOOTH &&
3960
      mLastScrollEventTime == aEvent->time) {
3961
    LOG(("[%d] duplicate legacy scroll event %d\n", aEvent->time,
3962
         aEvent->direction));
3963
    return;
3964
  }
3965
  WidgetWheelEvent wheelEvent(true, eWheel, this);
3966
  wheelEvent.mDeltaMode = dom::WheelEvent_Binding::DOM_DELTA_LINE;
3967
  switch (aEvent->direction) {
3968
    case GDK_SCROLL_SMOOTH: {
3969
      // As of GTK 3.4, all directional scroll events are provided by
3970
      // the GDK_SCROLL_SMOOTH direction on XInput2 and Wayland devices.
3971
      mLastScrollEventTime = aEvent->time;
3972
3973
      // Special handling for touchpads to support flings
3974
      // (also known as kinetic/inertial/momentum scrolling)
3975
      GdkDevice* device = gdk_event_get_source_device((GdkEvent*)aEvent);
3976
      GdkInputSource source = gdk_device_get_source(device);
3977
      if (source == GDK_SOURCE_TOUCHSCREEN || source == GDK_SOURCE_TOUCHPAD) {
3978
        if (StaticPrefs::apz_gtk_kinetic_scroll_enabled() &&
3979
            gtk_check_version(3, 20, 0) == nullptr) {
3980
          static auto sGdkEventIsScrollStopEvent =
3981
              (gboolean(*)(const GdkEvent*))dlsym(
3982
                  RTLD_DEFAULT, "gdk_event_is_scroll_stop_event");
3983
3984
          LOG(("[%d] pan smooth event dx=%f dy=%f inprogress=%d\n",
3985
               aEvent->time, aEvent->delta_x, aEvent->delta_y, mPanInProgress));
3986
          PanGestureInput::PanGestureType eventType =
3987
              PanGestureInput::PANGESTURE_PAN;
3988
          if (sGdkEventIsScrollStopEvent((GdkEvent*)aEvent)) {
3989
            eventType = PanGestureInput::PANGESTURE_END;
3990
            mPanInProgress = false;
3991
          } else if (!mPanInProgress) {
3992
            eventType = PanGestureInput::PANGESTURE_START;
3993
            mPanInProgress = true;
3994
          }
3995
3996
          LayoutDeviceIntPoint touchPoint = GetRefPoint(this, aEvent);
3997
          PanGestureInput panEvent(
3998
              eventType, aEvent->time, GetEventTimeStamp(aEvent->time),
3999
              ScreenPoint(touchPoint.x, touchPoint.y),
4000
              ScreenPoint(aEvent->delta_x, aEvent->delta_y),
4001
              KeymapWrapper::ComputeKeyModifiers(aEvent->state));
4002
          panEvent.mDeltaType = PanGestureInput::PANDELTA_PAGE;
4003
          panEvent.mSimulateMomentum = true;
4004
4005
          DispatchPanGestureInput(panEvent);
4006
4007
          return;
4008
        }
4009
4010
        // Older GTK doesn't support stop events, so we can't support fling
4011
        wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;
4012
      }
4013
4014
      // TODO - use a more appropriate scrolling unit than lines.
4015
      // Multiply event deltas by 3 to emulate legacy behaviour.
4016
      wheelEvent.mDeltaX = aEvent->delta_x * 3;
4017
      wheelEvent.mDeltaY = aEvent->delta_y * 3;
4018
      wheelEvent.mIsNoLineOrPageDelta = true;
4019
4020
      break;
4021
    }
4022
    case GDK_SCROLL_UP:
4023
      wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = -3;
4024
      break;
4025
    case GDK_SCROLL_DOWN:
4026
      wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = 3;
4027
      break;
4028
    case GDK_SCROLL_LEFT:
4029
      wheelEvent.mDeltaX = wheelEvent.mLineOrPageDeltaX = -1;
4030
      break;
4031
    case GDK_SCROLL_RIGHT:
4032
      wheelEvent.mDeltaX = wheelEvent.mLineOrPageDeltaX = 1;
4033
      break;
4034
  }
4035
4036
  wheelEvent.mRefPoint = GetRefPoint(this, aEvent);
4037
4038
  KeymapWrapper::InitInputEvent(wheelEvent, aEvent->state);
4039
4040
  wheelEvent.AssignEventTime(GetWidgetEventTime(aEvent->time));
4041
4042
  DispatchInputEvent(&wheelEvent);
4043
}
4044
4045
void nsWindow::OnWindowStateEvent(GtkWidget* aWidget,
4046
                                  GdkEventWindowState* aEvent) {
4047
  LOG(
4048
      ("nsWindow::OnWindowStateEvent [%p] for %p changed 0x%x new_window_state "
4049
       "0x%x\n",
4050
       (void*)this, aWidget, aEvent->changed_mask, aEvent->new_window_state));
4051
4052
  if (IS_MOZ_CONTAINER(aWidget)) {
4053
    // This event is notifying the container widget of changes to the
4054
    // toplevel window.  Just detect changes affecting whether windows are
4055
    // viewable.
4056
    //
4057
    // (A visibility notify event is sent to each window that becomes
4058
    // viewable when the toplevel is mapped, but we can't rely on that for
4059
    // setting mHasMappedToplevel because these toplevel window state
4060
    // events are asynchronous.  The windows in the hierarchy now may not
4061
    // be the same windows as when the toplevel was mapped, so they may
4062
    // not get VisibilityNotify events.)
4063
    bool mapped = !(aEvent->new_window_state &
4064
                    (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_WITHDRAWN));
4065
    if (mHasMappedToplevel != mapped) {
4066
      SetHasMappedToplevel(mapped);
4067
    }
4068
    LOG(("\tquick return because IS_MOZ_CONTAINER(aWidget) is true\n"));
4069
    return;
4070
  }
4071
  // else the widget is a shell widget.
4072
4073
  // The block below is a bit evil.
4074
  //
4075
  // When a window is resized before it is shown, gtk_window_resize() delays
4076
  // resizes until the window is shown.  If gtk_window_state_event() sees a
4077
  // GDK_WINDOW_STATE_MAXIMIZED change [1] before the window is shown, then
4078
  // gtk_window_compute_configure_request_size() ignores the values from the
4079
  // resize [2].  See bug 1449166 for an example of how this could happen.
4080
  //
4081
  // [1] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L7967
4082
  // [2] https://gitlab.gnome.org/GNOME/gtk/blob/3.22.30/gtk/gtkwindow.c#L9377
4083
  //
4084
  // In order to provide a sensible size for the window when the user exits
4085
  // maximized state, we hide the GDK_WINDOW_STATE_MAXIMIZED change from
4086
  // gtk_window_state_event() so as to trick GTK into using the values from
4087
  // gtk_window_resize() in its configure request.
4088
  //
4089
  // We instead notify gtk_window_state_event() of the maximized state change
4090
  // once the window is shown.
4091
  //
4092
  // See https://gitlab.gnome.org/GNOME/gtk/issues/1044
4093
  //
4094
  // This may be fixed in Gtk 3.24+ but some DE still have this issue
4095
  // (Bug 1624199) so let's remove it for Wayland only.
4096
  if (mIsX11Display) {
4097
    if (!mIsShown) {
4098
      aEvent->changed_mask = static_cast<GdkWindowState>(
4099
          aEvent->changed_mask & ~GDK_WINDOW_STATE_MAXIMIZED);
4100
    } else if (aEvent->changed_mask & GDK_WINDOW_STATE_WITHDRAWN &&
4101
               aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
4102
      aEvent->changed_mask = static_cast<GdkWindowState>(
4103
          aEvent->changed_mask | GDK_WINDOW_STATE_MAXIMIZED);
4104
    }
4105
  }
4106
4107
  // This is a workaround for https://gitlab.gnome.org/GNOME/gtk/issues/1395
4108
  // Gtk+ controls window active appearance by window-state-event signal.
4109
  if (IsChromeWindowTitlebar() &&
4110
      (aEvent->changed_mask & GDK_WINDOW_STATE_FOCUSED)) {
4111
    // Emulate what Gtk+ does at gtk_window_state_event().
4112
    // We can't check GTK_STATE_FLAG_BACKDROP directly as it's set by Gtk+
4113
    // *after* this window-state-event handler.
4114
    mTitlebarBackdropState =
4115
        !(aEvent->new_window_state & GDK_WINDOW_STATE_FOCUSED);
4116
4117
    // keep IsActiveBrowserWindow in sync with GDK_WINDOW_STATE_FOCUSED
4118
    UpdateMozWindowActive();
4119
4120
    ForceTitlebarRedraw();
4121
  }
4122
4123
  // We don't care about anything but changes in the maximized/icon/fullscreen
4124
  // states but we need a workaround for bug in Wayland:
4125
  // https://gitlab.gnome.org/GNOME/gtk/issues/67
4126
  // Under wayland the gtk_window_iconify implementation does NOT synthetize
4127
  // window_state_event where the GDK_WINDOW_STATE_ICONIFIED is set.
4128
  // During restore we  won't get aEvent->changed_mask with
4129
  // the GDK_WINDOW_STATE_ICONIFIED so to detect that change we use the stored
4130
  // mSizeState and obtaining a focus.
4131
  bool waylandWasIconified =
4132
      (!mIsX11Display && aEvent->changed_mask & GDK_WINDOW_STATE_FOCUSED &&
4133
       aEvent->new_window_state & GDK_WINDOW_STATE_FOCUSED &&
4134
       mSizeState == nsSizeMode_Minimized);
4135
  if (!waylandWasIconified &&
4136
      (aEvent->changed_mask &
4137
       (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_MAXIMIZED |
4138
        GDK_WINDOW_STATE_TILED | GDK_WINDOW_STATE_FULLSCREEN)) == 0) {
4139
    LOG(("\tearly return because no interesting bits changed\n"));
4140
    return;
4141
  }
4142
4143
  if (aEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED) {
4144
    LOG(("\tIconified\n"));
4145
    mSizeState = nsSizeMode_Minimized;
4146
#ifdef ACCESSIBILITY
4147
    DispatchMinimizeEventAccessible();
4148
#endif  // ACCESSIBILITY
4149
  } else if (aEvent->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) {
4150
    LOG(("\tFullscreen\n"));
4151
    mSizeState = nsSizeMode_Fullscreen;
4152
  } else if (aEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) {
4153
    LOG(("\tMaximized\n"));
4154
    mSizeState = nsSizeMode_Maximized;
4155
#ifdef ACCESSIBILITY
4156
    DispatchMaximizeEventAccessible();
4157
#endif  // ACCESSIBILITY
4158
  } else {
4159
    LOG(("\tNormal\n"));
4160
    mSizeState = nsSizeMode_Normal;
4161
#ifdef ACCESSIBILITY
4162
    DispatchRestoreEventAccessible();
4163
#endif  // ACCESSIBILITY
4164
  }
4165
4166
  if (aEvent->new_window_state & GDK_WINDOW_STATE_TILED) {
4167
    LOG(("\tTiled\n"));
4168
    mIsTiled = true;
4169
  } else {
4170
    LOG(("\tNot tiled\n"));
4171
    mIsTiled = false;
4172
  }
4173
4174
  if (mWidgetListener) {
4175
    mWidgetListener->SizeModeChanged(mSizeState);
4176
    if (aEvent->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
4177
      mWidgetListener->FullscreenChanged(aEvent->new_window_state &
4178
                                         GDK_WINDOW_STATE_FULLSCREEN);
4179
    }
4180
  }
4181
4182
  if (mDrawInTitlebar && mTransparencyBitmapForTitlebar) {
4183
    if (mSizeState == nsSizeMode_Normal && !mIsTiled) {
4184
      UpdateTitlebarTransparencyBitmap();
4185
    } else {
4186
      ClearTransparencyBitmap();
4187
    }
4188
  }
4189
}
4190
4191
void nsWindow::ThemeChanged() {
4192
  // Everything could've changed.
4193
  NotifyThemeChanged(ThemeChangeKind::StyleAndLayout);
4194
4195
  if (!mGdkWindow || MOZ_UNLIKELY(mIsDestroyed)) return;
4196
4197
  // Dispatch theme change notification to all child windows
4198
  GList* children = gdk_window_peek_children(mGdkWindow);
4199
  while (children) {
4200
    GdkWindow* gdkWin = GDK_WINDOW(children->data);
4201
4202
    auto* win = (nsWindow*)g_object_get_data(G_OBJECT(gdkWin), "nsWindow");
4203
4204
    if (win && win != this) {  // guard against infinite recursion
4205
      RefPtr<nsWindow> kungFuDeathGrip = win;
4206
      win->ThemeChanged();
4207
    }
4208
4209
    children = children->next;
4210
  }
4211
4212
  IMContextWrapper::OnThemeChanged();
4213
}
4214
4215
void nsWindow::OnDPIChanged() {
4216
  if (mWidgetListener) {
4217
    if (PresShell* presShell = mWidgetListener->GetPresShell()) {
4218
      presShell->BackingScaleFactorChanged();
4219
      // Update menu's font size etc.
4220
      // This affects style / layout because it affects system font sizes.
4221
      presShell->ThemeChanged(ThemeChangeKind::StyleAndLayout);
4222
    }
4223
    mWidgetListener->UIResolutionChanged();
4224
  }
4225
}
4226
4227
void nsWindow::OnCheckResize() { mPendingConfigures++; }
4228
4229
void nsWindow::OnCompositedChanged() {
4230
  // Update CSD after the change in alpha visibility. This only affects
4231
  // system metrics, not other theme shenanigans.
4232
  NotifyThemeChanged(ThemeChangeKind::MediaQueriesOnly);
4233
  mCompositedScreen = gdk_screen_is_composited(gdk_screen_get_default());
4234
}
4235
4236
void nsWindow::OnScaleChanged(GtkAllocation* aAllocation) {
4237
  LOG(("nsWindow::OnScaleChanged [%p] %d,%d -> %d x %d\n", (void*)this,
4238
       aAllocation->x, aAllocation->y, aAllocation->width,
4239
       aAllocation->height));
4240
4241
  // Force scale factor recalculation
4242
  mWindowScaleFactorChanged = true;
4243
4244
  // This eventually propagate new scale to the PuppetWidgets
4245
  OnDPIChanged();
4246
4247
  // configure_event is already fired before scale-factor signal,
4248
  // but size-allocate isn't fired by changing scale
4249
  OnSizeAllocate(aAllocation);
4250
4251
  // Client offset are updated by _NET_FRAME_EXTENTS on X11 when system titlebar
4252
  // is enabled. In ither cases (Wayland or system titlebar is off on X11)
4253
  // we don't get _NET_FRAME_EXTENTS X11 property notification so we derive
4254
  // it from mContainer position.
4255
  if (mGtkWindowDecoration == GTK_DECORATION_CLIENT) {
4256
    if (!mIsX11Display || (mIsX11Display && mDrawInTitlebar)) {
4257
      UpdateClientOffsetFromCSDWindow();
4258
    }
4259
  }
4260
4261
#ifdef MOZ_WAYLAND
4262
  // We need to update scale when scale of egl window is changed.
4263
  if (mContainer && moz_container_wayland_has_egl_window(mContainer)) {
4264
    moz_container_wayland_set_scale_factor(mContainer);
4265
  }
4266
#endif
4267
}
4268
4269
void nsWindow::DispatchDragEvent(EventMessage aMsg,
4270
                                 const LayoutDeviceIntPoint& aRefPoint,
4271
                                 guint aTime) {
4272
  WidgetDragEvent event(true, aMsg, this);
4273
4274
  InitDragEvent(event);
4275
4276
  event.mRefPoint = aRefPoint;
4277
  event.AssignEventTime(GetWidgetEventTime(aTime));
4278
4279
  DispatchInputEvent(&event);
4280
}
4281
4282
void nsWindow::OnDragDataReceivedEvent(GtkWidget* aWidget,
4283
                                       GdkDragContext* aDragContext, gint aX,
4284
                                       gint aY,
4285
                                       GtkSelectionData* aSelectionData,
4286
                                       guint aInfo, guint aTime,
4287
                                       gpointer aData) {
4288
  LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this));
4289
4290
  RefPtr<nsDragService> dragService = nsDragService::GetInstance();
4291
  dragService->TargetDataReceived(aWidget, aDragContext, aX, aY, aSelectionData,
4292
                                  aInfo, aTime);
4293
}
4294
4295
nsWindow* nsWindow::GetTransientForWindowIfPopup() {
4296
  if (mWindowType != eWindowType_popup) {
4297
    return nullptr;
4298
  }
4299
  GtkWindow* toplevel = gtk_window_get_transient_for(GTK_WINDOW(mShell));
4300
  if (toplevel) {
4301
    return get_window_for_gtk_widget(GTK_WIDGET(toplevel));
4302
  }
4303
  return nullptr;
4304
}
4305
4306
bool nsWindow::IsHandlingTouchSequence(GdkEventSequence* aSequence) {
4307
  return mHandleTouchEvent && mTouches.Contains(aSequence);
4308
}
4309
4310
gboolean nsWindow::OnTouchpadPinchEvent(GdkEventTouchpadPinch* aEvent) {
4311
  if (StaticPrefs::apz_gtk_touchpad_pinch_enabled()) {
4312
    // Do not respond to pinch gestures involving more than two fingers
4313
    // unless specifically preffed on. These are sometimes hooked up to other
4314
    // actions at the desktop environment level and having the browser also
4315
    // pinch can be undesirable.
4316
    if (aEvent->n_fingers > 2 &&
4317
        !StaticPrefs::apz_gtk_touchpad_pinch_three_fingers_enabled()) {
4318
      return FALSE;
4319
    }
4320
    PinchGestureInput::PinchGestureType pinchGestureType =
4321
        PinchGestureInput::PINCHGESTURE_SCALE;
4322
    ScreenCoord CurrentSpan;
4323
    ScreenCoord PreviousSpan;
4324
4325
    switch (aEvent->phase) {
4326
      case GDK_TOUCHPAD_GESTURE_PHASE_BEGIN:
4327
        pinchGestureType = PinchGestureInput::PINCHGESTURE_START;
4328
        CurrentSpan = aEvent->scale;
4329
4330
        // Assign PreviousSpan --> 0.999 to make mDeltaY field of the
4331
        // WidgetWheelEvent that this PinchGestureInput event will be converted
4332
        // to not equal Zero as our discussion because we observed that the
4333
        // scale of the PHASE_BEGIN event is 1.
4334
        PreviousSpan = 0.999;
4335
        break;
4336
4337
      case GDK_TOUCHPAD_GESTURE_PHASE_UPDATE:
4338
        pinchGestureType = PinchGestureInput::PINCHGESTURE_SCALE;
4339
        if (aEvent->scale == mLastPinchEventSpan) {
4340
          return FALSE;
4341
        }
4342
        CurrentSpan = aEvent->scale;
4343
        PreviousSpan = mLastPinchEventSpan;
4344
        break;
4345
4346
      case GDK_TOUCHPAD_GESTURE_PHASE_END:
4347
        pinchGestureType = PinchGestureInput::PINCHGESTURE_END;
4348
        CurrentSpan = aEvent->scale;
4349
        PreviousSpan = mLastPinchEventSpan;
4350
        break;
4351
4352
      default:
4353
        return FALSE;
4354
    }
4355
4356
    LayoutDeviceIntPoint touchpadPoint = GetRefPoint(this, aEvent);
4357
    PinchGestureInput event(
4358
        pinchGestureType, PinchGestureInput::TRACKPAD, aEvent->time,
4359
        GetEventTimeStamp(aEvent->time), ExternalPoint(0, 0),
4360
        ScreenPoint(touchpadPoint.x, touchpadPoint.y),
4361
        100.0 * ((aEvent->phase == GDK_TOUCHPAD_GESTURE_PHASE_END)
4362
                     ? ScreenCoord(1.f)
4363
                     : CurrentSpan),
4364
        100.0 * ((aEvent->phase == GDK_TOUCHPAD_GESTURE_PHASE_END)
4365
                     ? ScreenCoord(1.f)
4366
                     : PreviousSpan),
4367
        KeymapWrapper::ComputeKeyModifiers(aEvent->state));
4368
4369
    if (!event.SetLineOrPageDeltaY(this)) {
4370
      return FALSE;
4371
    }
4372
4373
    mLastPinchEventSpan = aEvent->scale;
4374
    DispatchPinchGestureInput(event);
4375
  }
4376
  return TRUE;
4377
}
4378
4379
gboolean nsWindow::OnTouchEvent(GdkEventTouch* aEvent) {
4380
  if (!mHandleTouchEvent) {
4381
    // If a popup window was spawned (e.g. as the result of a long-press)
4382
    // and touch events got diverted to that window within a touch sequence,
4383
    // ensure the touch event gets sent to the original window instead. We
4384
    // keep the checks here very conservative so that we only redirect
4385
    // events in this specific scenario.
4386
    nsWindow* targetWindow = GetTransientForWindowIfPopup();
4387
    if (targetWindow &&
4388
        targetWindow->IsHandlingTouchSequence(aEvent->sequence)) {
4389
      return targetWindow->OnTouchEvent(aEvent);
4390
    }
4391
4392
    return FALSE;
4393
  }
4394
4395
  EventMessage msg;
4396
  switch (aEvent->type) {
4397
    case GDK_TOUCH_BEGIN:
4398
      msg = eTouchStart;
4399
      break;
4400
    case GDK_TOUCH_UPDATE:
4401
      msg = eTouchMove;
4402
      break;
4403
    case GDK_TOUCH_END:
4404
      msg = eTouchEnd;
4405
      break;
4406
    case GDK_TOUCH_CANCEL:
4407
      msg = eTouchCancel;
4408
      break;
4409
    default:
4410
      return FALSE;
4411
  }
4412
4413
  LayoutDeviceIntPoint touchPoint = GetRefPoint(this, aEvent);
4414
4415
  int32_t id;
4416
  RefPtr<dom::Touch> touch;
4417
  if (mTouches.Remove(aEvent->sequence, getter_AddRefs(touch))) {
4418
    id = touch->mIdentifier;
4419
  } else {
4420
    id = ++gLastTouchID & 0x7FFFFFFF;
4421
  }
4422
4423
  touch =
4424
      new dom::Touch(id, touchPoint, LayoutDeviceIntPoint(1, 1), 0.0f, 0.0f);
4425
4426
  WidgetTouchEvent event(true, msg, this);
4427
  KeymapWrapper::InitInputEvent(event, aEvent->state);
4428
  event.mTime = aEvent->time;
4429
4430
  if (aEvent->type == GDK_TOUCH_BEGIN || aEvent->type == GDK_TOUCH_UPDATE) {
4431
    mTouches.InsertOrUpdate(aEvent->sequence, std::move(touch));
4432
    // add all touch points to event object
4433
    for (const auto& data : mTouches.Values()) {
4434
      event.mTouches.AppendElement(new dom::Touch(*data));
4435
    }
4436
  } else if (aEvent->type == GDK_TOUCH_END ||
4437
             aEvent->type == GDK_TOUCH_CANCEL) {
4438
    *event.mTouches.AppendElement() = std::move(touch);
4439
  }
4440
4441
  DispatchInputEvent(&event);
4442
  return TRUE;
4443
}
4444
4445
// Return true if toplevel window is transparent.
4446
// It's transparent when we're running on composited screens
4447
// and we can draw main window without system titlebar.
4448
bool nsWindow::IsToplevelWindowTransparent() {
4449
  static bool transparencyConfigured = false;
4450
4451
  if (!transparencyConfigured) {
4452
    if (gdk_screen_is_composited(gdk_screen_get_default())) {
4453
      // Some Gtk+ themes use non-rectangular toplevel windows. To fully
4454
      // support such themes we need to make toplevel window transparent
4455
      // with ARGB visual.
4456
      // It may cause performanance issue so make it configurable
4457
      // and enable it by default for selected window managers.
4458
      if (Preferences::HasUserValue("mozilla.widget.use-argb-visuals")) {
4459
        // argb visual is explicitly required so use it
4460
        sTransparentMainWindow =
4461
            Preferences::GetBool("mozilla.widget.use-argb-visuals");
4462
      } else {
4463
        // Enable transparent toplevel window if we can draw main window
4464
        // without system titlebar as Gtk+ themes use titlebar round corners.
4465
        sTransparentMainWindow =
4466
            GetSystemGtkWindowDecoration() != GTK_DECORATION_NONE;
4467
      }
4468
    }
4469
    transparencyConfigured = true;
4470
  }
4471
4472
  return sTransparentMainWindow;
4473
}
4474
4475
static GdkWindow* CreateGdkWindow(GdkWindow* parent, GtkWidget* widget) {
4476
  GdkWindowAttr attributes;
4477
  gint attributes_mask = GDK_WA_VISUAL;
4478
4479
  attributes.event_mask = kEvents;
4480
4481
  attributes.width = 1;
4482
  attributes.height = 1;
4483
  attributes.wclass = GDK_INPUT_OUTPUT;
4484
  attributes.visual = gtk_widget_get_visual(widget);
4485
  attributes.window_type = GDK_WINDOW_CHILD;
4486
4487
  GdkWindow* window = gdk_window_new(parent, &attributes, attributes_mask);
4488
  gdk_window_set_user_data(window, widget);
4489
4490
  return window;
4491
}
4492
4493
// Configure GL visual on X11. We add alpha silently
4494
// if we use WebRender to workaround NVIDIA specific Bug 1663273.
4495
bool nsWindow::ConfigureX11GLVisual(bool aUseAlpha) {
4496
  if (!mIsX11Display) {
4497
    return false;
4498
  }
4499
4500
  // If using WebRender on X11, we need to select a visual with a depth
4501
  // buffer, as well as an alpha channel if transparency is requested. This
4502
  // must be done before the widget is realized.
4503
  bool useWebRender = gfx::gfxVars::UseWebRender();
4504
  auto* screen = gtk_widget_get_screen(mShell);
4505
  int visualId = 0;
4506
  bool haveVisual;
4507
4508
  // See https://bugzilla.mozilla.org/show_bug.cgi?id=1663003
4509
  // We need to use GLX to get visual even on EGL until
4510
  // EGL can provide compositable visual:
4511
  // https://gitlab.freedesktop.org/mesa/mesa/-/issues/149
4512
  if ((true /* !gfx::gfxVars::UseEGL() */)) {
4513
    auto* display = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(mShell));
4514
    int screenNumber = GDK_SCREEN_XNUMBER(screen);
4515
    haveVisual = GLContextGLX::FindVisual(display, screenNumber, useWebRender,
4516
                                          aUseAlpha || useWebRender, &visualId);
4517
  } else {
4518
    haveVisual = GLContextEGL::FindVisual(useWebRender,
4519
                                          aUseAlpha || useWebRender, &visualId);
4520
  }
4521
4522
  GdkVisual* gdkVisual = nullptr;
4523
  if (haveVisual) {
4524
    // If we're using CSD, rendering will go through mContainer, but
4525
    // it will inherit this visual as it is a child of mShell.
4526
    gdkVisual = gdk_x11_screen_lookup_visual(screen, visualId);
4527
  }
4528
  if (!gdkVisual) {
4529
    NS_WARNING("We're missing X11 Visual!");
4530
    if (aUseAlpha || useWebRender) {
4531
      // We try to use a fallback alpha visual
4532
      GdkScreen* screen = gtk_widget_get_screen(mShell);
4533
      gdkVisual = gdk_screen_get_rgba_visual(screen);
4534
    }
4535
  }
4536
  if (gdkVisual) {
4537
    // TODO: We use alpha visual even on non-compositing screens (Bug 1479135).
4538
    gtk_widget_set_visual(mShell, gdkVisual);
4539
    mHasAlphaVisual = aUseAlpha;
4540
  }
4541
4542
  return true;
4543
}
4544
4545
nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
4546
                          const LayoutDeviceIntRect& aRect,
4547
                          nsWidgetInitData* aInitData) {
4548
#ifdef MOZ_LOGGING
4549
  if (this->GetFrame() && this->GetFrame()->GetContent()) {
4550
    nsCString nodeName =
4551
        NS_ConvertUTF16toUTF8(this->GetFrame()->GetContent()->NodeName());
4552
    LOG(("nsWindow::Create: creating [%p]: nodename %s\n", this,
4553
         nodeName.get()));
4554
  }
4555
#endif
4556
  // only set the base parent if we're going to be a dialog or a
4557
  // toplevel
4558
  nsIWidget* baseParent =
4559
      aInitData && (aInitData->mWindowType == eWindowType_dialog ||
4560
                    aInitData->mWindowType == eWindowType_toplevel ||
4561
                    aInitData->mWindowType == eWindowType_invisible)
4562
          ? nullptr
4563
          : aParent;
4564
4565
#ifdef ACCESSIBILITY
4566
  // Send a DBus message to check whether a11y is enabled
4567
  a11y::PreInit();
4568
#endif
4569
4570
  // Ensure that the toolkit is created.
4571
  nsGTKToolkit::GetToolkit();
4572
4573
  // initialize all the common bits of this class
4574
  BaseCreate(baseParent, aInitData);
4575
4576
  // Do we need to listen for resizes?
4577
  bool listenForResizes = false;
4578
  ;
4579
  if (aNativeParent || (aInitData && aInitData->mListenForResizes))
4580
    listenForResizes = true;
4581
4582
  // and do our common creation
4583
  CommonCreate(aParent, listenForResizes);
4584
4585
  // save our bounds
4586
  mBounds = aRect;
4587
  LOG(("  mBounds: x:%d y:%d w:%d h:%d\n", mBounds.x, mBounds.y, mBounds.width,
4588
       mBounds.height));
4589
4590
  mPreferredPopupRectFlushed = false;
4591
4592
  ConstrainSize(&mBounds.width, &mBounds.height);
4593
4594
  GtkWidget* eventWidget = nullptr;
4595
  bool popupNeedsAlphaVisual = (mWindowType == eWindowType_popup &&
4596
                                (aInitData && aInitData->mSupportTranslucency));
4597
4598
  // Figure out our parent window - only used for eWindowType_child
4599
  GtkWidget* parentMozContainer = nullptr;
4600
  GtkContainer* parentGtkContainer = nullptr;
4601
  GdkWindow* parentGdkWindow = nullptr;
4602
  nsWindow* parentnsWindow = nullptr;
4603
4604
  if (aParent) {
4605
    parentnsWindow = static_cast<nsWindow*>(aParent);
4606
    parentGdkWindow = parentnsWindow->mGdkWindow;
4607
  } else if (aNativeParent && GDK_IS_WINDOW(aNativeParent)) {
4608
    parentGdkWindow = GDK_WINDOW(aNativeParent);
4609
    parentnsWindow = get_window_for_gdk_window(parentGdkWindow);
4610
    if (!parentnsWindow) return NS_ERROR_FAILURE;
4611
4612
  } else if (aNativeParent && GTK_IS_CONTAINER(aNativeParent)) {
4613
    parentGtkContainer = GTK_CONTAINER(aNativeParent);
4614
  }
4615
4616
  if (parentGdkWindow) {
4617
    // get the widget for the window - it should be a moz container
4618
    parentMozContainer = parentnsWindow->GetMozContainerWidget();
4619
    if (!parentMozContainer) return NS_ERROR_FAILURE;
4620
  }
4621
  // ^^ only used for eWindowType_child
4622
4623
  if (!mIsX11Display) {
4624
    if (mWindowType == eWindowType_child) {
4625
      // eWindowType_child is not supported on Wayland. Just switch to toplevel
4626
      // as a workaround.
4627
      mWindowType = eWindowType_toplevel;
4628
    } else if (mWindowType == eWindowType_popup && !aNativeParent && !aParent) {
4629
      // mIsWaylandPanelWindow is a special toplevel window on Wayland which
4630
      // emulates X11 popup window without parent.
4631
      mIsWaylandPanelWindow = true;
4632
      mWindowType = eWindowType_toplevel;
4633
    }
4634
  }
4635
4636
  mAlwaysOnTop = aInitData && aInitData->mAlwaysOnTop;
4637
  mIsPIPWindow = aInitData && aInitData->mPIPWindow;
4638
4639
  // ok, create our windows
4640
  switch (mWindowType) {
4641
    case eWindowType_dialog:
4642
    case eWindowType_popup:
4643
    case eWindowType_toplevel:
4644
    case eWindowType_invisible: {
4645
      mIsTopLevel = true;
4646
4647
      // Popups that are not noautohide are only temporary. The are used
4648
      // for menus and the like and disappear when another window is used.
4649
      // For most popups, use the standard GtkWindowType GTK_WINDOW_POPUP,
4650
      // which will use a Window with the override-redirect attribute
4651
      // (for temporary windows).
4652
      // For long-lived windows, their stacking order is managed by the
4653
      // window manager, as indicated by GTK_WINDOW_TOPLEVEL.
4654
      // For Wayland we have to always use GTK_WINDOW_POPUP to control
4655
      // popup window position.
4656
      GtkWindowType type = GTK_WINDOW_TOPLEVEL;
4657
      if (mWindowType == eWindowType_popup) {
4658
        type = GTK_WINDOW_POPUP;
4659
        if (GdkIsX11Display() && aInitData->mNoAutoHide) {
4660
          type = GTK_WINDOW_TOPLEVEL;
4661
        }
4662
      }
4663
      mShell = gtk_window_new(type);
4664
4665
      // Ensure gfxPlatform is initialized, since that is what initializes
4666
      // gfxVars, used below.
4667
      Unused << gfxPlatform::GetPlatform();
4668
4669
      if (mWindowType == eWindowType_toplevel ||
4670
          mWindowType == eWindowType_dialog) {
4671
        mGtkWindowDecoration = GetSystemGtkWindowDecoration();
4672
      }
4673
4674
      // Don't use transparency for PictureInPicture windows.
4675
      bool toplevelNeedsAlphaVisual = false;
4676
      if (mWindowType == eWindowType_toplevel && !mIsPIPWindow) {
4677
        toplevelNeedsAlphaVisual = IsToplevelWindowTransparent();
4678
      }
4679
4680
      bool isGLVisualSet = false;
4681
      bool isAccelerated = ComputeShouldAccelerate();
4682
#ifdef MOZ_X11
4683
      if (isAccelerated) {
4684
        isGLVisualSet = ConfigureX11GLVisual(popupNeedsAlphaVisual ||
4685
                                             toplevelNeedsAlphaVisual);
4686
      }
4687
#endif
4688
      if (!isGLVisualSet &&
4689
          (popupNeedsAlphaVisual || toplevelNeedsAlphaVisual)) {
4690
        // We're running on composited screen so we can use alpha visual
4691
        // for both toplevel and popups.
4692
        if (mCompositedScreen) {
4693
          GdkVisual* visual =
4694
              gdk_screen_get_rgba_visual(gtk_widget_get_screen(mShell));
4695
          if (visual) {
4696
            gtk_widget_set_visual(mShell, visual);
4697
            mHasAlphaVisual = true;
4698
          }
4699
        }
4700
      }
4701
4702
      // Use X shape mask to draw round corners of Firefox titlebar.
4703
      // We don't use shape masks any more as we switched to ARGB visual
4704
      // by default and non-compositing screens use solid-csd decorations
4705
      // without round corners.
4706
      // Leave the shape mask code here as it can be used to draw round
4707
      // corners on EGL (https://gitlab.freedesktop.org/mesa/mesa/-/issues/149)
4708
      // or when custom titlebar theme is used.
4709
      mTransparencyBitmapForTitlebar = TitlebarUseShapeMask();
4710
4711
      // We have a toplevel window with transparency.
4712
      // Calls to UpdateTitlebarTransparencyBitmap() from OnExposeEvent()
4713
      // occur before SetTransparencyMode() receives eTransparencyTransparent
4714
      // from layout, so set mIsTransparent here.
4715
      if (mWindowType == eWindowType_toplevel &&
4716
          (mHasAlphaVisual || mTransparencyBitmapForTitlebar)) {
4717
        mIsTransparent = true;
4718
      }
4719
4720
      // We only move a general managed toplevel window if someone has
4721
      // actually placed the window somewhere.  If no placement has taken
4722
      // place, we just let the window manager Do The Right Thing.
4723
      NativeResize();
4724
4725
      if (mWindowType == eWindowType_dialog) {
4726
        mGtkWindowRoleName = "Dialog";
4727
4728
        SetDefaultIcon();
4729
        gtk_window_set_type_hint(GTK_WINDOW(mShell),
4730
                                 GDK_WINDOW_TYPE_HINT_DIALOG);
4731
        LOG(("nsWindow::Create(): dialog [%p]\n", this));
4732
        if (parentnsWindow) {
4733
          gtk_window_set_transient_for(
4734
              GTK_WINDOW(mShell), GTK_WINDOW(parentnsWindow->GetGtkWidget()));
4735
          LOG(("    parent window %p [GdkWindow]\n", this));
4736
        }
4737
4738
      } else if (mWindowType == eWindowType_popup) {
4739
        mGtkWindowRoleName = "Popup";
4740
4741
        if (aInitData->mNoAutoHide) {
4742
          // ... but the window manager does not decorate this window,
4743
          // nor provide a separate taskbar icon.
4744
          if (mBorderStyle == eBorderStyle_default) {
4745
            gtk_window_set_decorated(GTK_WINDOW(mShell), FALSE);
4746
          } else {
4747
            bool decorate = mBorderStyle & eBorderStyle_title;
4748
            gtk_window_set_decorated(GTK_WINDOW(mShell), decorate);
4749
            if (decorate) {
4750
              gtk_window_set_deletable(GTK_WINDOW(mShell),
4751
                                       mBorderStyle & eBorderStyle_close);
4752
            }
4753
          }
4754
          gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE);
4755
          // Element focus is managed by the parent window so the
4756
          // WM_HINTS input field is set to False to tell the window
4757
          // manager not to set input focus to this window ...
4758
          gtk_window_set_accept_focus(GTK_WINDOW(mShell), FALSE);
4759
#ifdef MOZ_X11
4760
          // ... but when the window manager offers focus through
4761
          // WM_TAKE_FOCUS, focus is requested on the parent window.
4762
          if (mIsX11Display) {
4763
            gtk_widget_realize(mShell);
4764
            gdk_window_add_filter(gtk_widget_get_window(mShell),
4765
                                  popup_take_focus_filter, nullptr);
4766
          }
4767
#endif
4768
        }
4769
4770
        GdkWindowTypeHint gtkTypeHint;
4771
        if (aInitData->mIsDragPopup) {
4772
          gtkTypeHint = GDK_WINDOW_TYPE_HINT_DND;
4773
          mIsDragPopup = true;
4774
        } else {
4775
          switch (aInitData->mPopupHint) {
4776
            case ePopupTypeMenu:
4777
              gtkTypeHint = GDK_WINDOW_TYPE_HINT_POPUP_MENU;
4778
              break;
4779
            case ePopupTypeTooltip:
4780
              gtkTypeHint = GDK_WINDOW_TYPE_HINT_TOOLTIP;
4781
              break;
4782
            default:
4783
              gtkTypeHint = GDK_WINDOW_TYPE_HINT_UTILITY;
4784
              break;
4785
          }
4786
        }
4787
        gtk_window_set_type_hint(GTK_WINDOW(mShell), gtkTypeHint);
4788
        LOG(("nsWindow::Create() popup [%p] type %s\n", this,
4789
             aInitData->mPopupHint == ePopupTypeMenu
4790
                 ? "Menu"
4791
                 : (aInitData->mPopupHint == ePopupTypeTooltip ? "Tooltip"
4792
                                                               : "Utility")));
4793
        if (parentnsWindow) {
4794
          LOG(("    parent window for popup: %p\n", parentnsWindow));
4795
          gtk_window_set_transient_for(
4796
              GTK_WINDOW(mShell), GTK_WINDOW(parentnsWindow->GetGtkWidget()));
4797
        }
4798
4799
        // We need realized mShell at NativeMove().
4800
        gtk_widget_realize(mShell);
4801
4802
        // With popup windows, we want to control their position, so don't
4803
        // wait for the window manager to place them (which wouldn't
4804
        // happen with override-redirect windows anyway).
4805
        NativeMove();
4806
      } else {  // must be eWindowType_toplevel
4807
        mGtkWindowRoleName = "Toplevel";
4808
        SetDefaultIcon();
4809
4810
        LOG(("nsWindow::Create() Toplevel [%p]\n", this));
4811
4812
        if (mIsPIPWindow) {
4813
          LOG(("    Is PIP Window\n"));
4814
          gtk_window_set_type_hint(GTK_WINDOW(mShell),
4815
                                   GDK_WINDOW_TYPE_HINT_UTILITY);
4816
        }
4817
4818
        // each toplevel window gets its own window group
4819
        GtkWindowGroup* group = gtk_window_group_new();
4820
        gtk_window_group_add_window(group, GTK_WINDOW(mShell));
4821
        g_object_unref(group);
4822
      }
4823
4824
      if (mAlwaysOnTop) {
4825
        gtk_window_set_keep_above(GTK_WINDOW(mShell), TRUE);
4826
      }
4827
4828
      // Create a container to hold child windows and child GtkWidgets.
4829
      GtkWidget* container = moz_container_new();
4830
      mContainer = MOZ_CONTAINER(container);
4831
#ifdef MOZ_WAYLAND
4832
      if (!mIsX11Display && isAccelerated) {
4833
        mCompositorInitiallyPaused = true;
4834
        RefPtr<nsWindow> self(this);
4835
        moz_container_wayland_add_initial_draw_callback(
4836
            mContainer, [self]() -> void {
4837
              self->mNeedsCompositorResume = true;
4838
              self->MaybeResumeCompositor();
4839
            });
4840
      }
4841
#endif
4842
4843
      // "csd" style is set when widget is realized so we need to call
4844
      // it explicitly now.
4845
      gtk_widget_realize(mShell);
4846
4847
      /* There are several cases here:
4848
       *
4849
       * 1) We're running on Gtk+ without client side decorations.
4850
       *    Content is rendered to mShell window and we listen
4851
       *    to the Gtk+ events on mShell
4852
       * 2) We're running on Gtk+ and client side decorations
4853
       *    are drawn by Gtk+ to mShell. Content is rendered to mContainer
4854
       *    and we listen to the Gtk+ events on mContainer.
4855
       * 3) We're running on Wayland. All gecko content is rendered
4856
       *    to mContainer and we listen to the Gtk+ events on mContainer.
4857
       */
4858
      GtkStyleContext* style = gtk_widget_get_style_context(mShell);
4859
      mDrawToContainer = !mIsX11Display ||
4860
                         (mGtkWindowDecoration == GTK_DECORATION_CLIENT) ||
4861
                         gtk_style_context_has_class(style, "csd");
4862
      eventWidget = (mDrawToContainer) ? container : mShell;
4863
4864
      // Prevent GtkWindow from painting a background to avoid flickering.
4865
      gtk_widget_set_app_paintable(eventWidget, TRUE);
4866
4867
      gtk_widget_add_events(eventWidget, kEvents);
4868
      if (mDrawToContainer) {
4869
        gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK);
4870
        gtk_widget_set_app_paintable(mShell, TRUE);
4871
      }
4872
      if (mTransparencyBitmapForTitlebar) {
4873
        moz_container_force_default_visual(mContainer);
4874
      }
4875
4876
      // If we draw to mContainer window then configure it now because
4877
      // gtk_container_add() realizes the child widget.
4878
      gtk_widget_set_has_window(container, mDrawToContainer);
4879
4880
      gtk_container_add(GTK_CONTAINER(mShell), container);
4881
4882
      // alwaysontop windows are generally used for peripheral indicators,
4883
      // so we don't focus them by default.
4884
      if (mAlwaysOnTop) {
4885
        gtk_window_set_focus_on_map(GTK_WINDOW(mShell), FALSE);
4886
      }
4887
4888
      gtk_widget_realize(container);
4889
4890
      // make sure this is the focus widget in the container
4891
      gtk_widget_show(container);
4892
4893
      if (!mAlwaysOnTop) {
4894
        gtk_widget_grab_focus(container);
4895
      }
4896
4897
      // the drawing window
4898
      mGdkWindow = gtk_widget_get_window(eventWidget);
4899
4900
#ifdef MOZ_WAYLAND
4901
      if (mIsX11Display && gfx::gfxVars::UseEGL() && isAccelerated) {
4902
        mCompositorInitiallyPaused = true;
4903
        mNeedsCompositorResume = true;
4904
        MaybeResumeCompositor();
4905
      }
4906
#endif
4907
4908
      if (mIsWaylandPanelWindow) {
4909
        gtk_window_set_decorated(GTK_WINDOW(mShell), false);
4910
      }
4911
4912
      if (mWindowType == eWindowType_popup) {
4913
        // gdk does not automatically set the cursor for "temporary"
4914
        // windows, which are what gtk uses for popups.
4915
4916
        // force SetCursor to actually set the cursor, even though our internal
4917
        // state indicates that we already have the standard cursor.
4918
        mUpdateCursor = true;
4919
        SetCursor(Cursor{eCursor_standard});
4920
4921
        if (aInitData->mNoAutoHide) {
4922
          gint wmd = ConvertBorderStyles(mBorderStyle);
4923
          if (wmd != -1)
4924
            gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration)wmd);
4925
        }
4926
4927
        // If the popup ignores mouse events, set an empty input shape.
4928
        SetWindowMouseTransparent(aInitData->mMouseTransparent);
4929
      }
4930
    } break;
4931
4932
    case eWindowType_plugin:
4933
    case eWindowType_plugin_ipc_chrome:
4934
    case eWindowType_plugin_ipc_content:
4935
      MOZ_ASSERT_UNREACHABLE("Unexpected eWindowType_plugin*");
4936
      return NS_ERROR_FAILURE;
4937
4938
    case eWindowType_child: {
4939
      if (parentMozContainer) {
4940
        mGdkWindow = CreateGdkWindow(parentGdkWindow, parentMozContainer);
4941
        mHasMappedToplevel = parentnsWindow->mHasMappedToplevel;
4942
      } else if (parentGtkContainer) {
4943
        // This MozContainer has its own window for drawing and receives
4944
        // events because there is no mShell widget (corresponding to this
4945
        // nsWindow).
4946
        GtkWidget* container = moz_container_new();
4947
        mContainer = MOZ_CONTAINER(container);
4948
        eventWidget = container;
4949
        gtk_widget_add_events(eventWidget, kEvents);
4950
        gtk_container_add(parentGtkContainer, container);
4951
        gtk_widget_realize(container);
4952
4953
        mGdkWindow = gtk_widget_get_window(container);
4954
      } else {
4955
        NS_WARNING(
4956
            "Warning: tried to create a new child widget with no parent!");
4957
        return NS_ERROR_FAILURE;
4958
      }
4959
    } break;
4960
    default:
4961
      break;
4962
  }
4963
4964
  // label the drawing window with this object so we can find our way home
4965
  g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
4966
  if (mDrawToContainer) {
4967
    // Also label mShell toplevel window,
4968
    // property_notify_event_cb callback also needs to find its way home
4969
    g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)), "nsWindow",
4970
                      this);
4971
  }
4972
4973
  if (mContainer) g_object_set_data(G_OBJECT(mContainer), "nsWindow", this);
4974
4975
  if (mShell) g_object_set_data(G_OBJECT(mShell), "nsWindow", this);
4976
4977
  // attach listeners for events
4978
  if (mShell) {
4979
    g_signal_connect(mShell, "configure_event", G_CALLBACK(configure_event_cb),
4980
                     nullptr);
4981
    g_signal_connect(mShell, "delete_event", G_CALLBACK(delete_event_cb),
4982
                     nullptr);
4983
    g_signal_connect(mShell, "window_state_event",
4984
                     G_CALLBACK(window_state_event_cb), nullptr);
4985
    g_signal_connect(mShell, "check-resize", G_CALLBACK(check_resize_cb),
4986
                     nullptr);
4987
    g_signal_connect(mShell, "composited-changed",
4988
                     G_CALLBACK(widget_composited_changed_cb), nullptr);
4989
    g_signal_connect(mShell, "property-notify-event",
4990
                     G_CALLBACK(property_notify_event_cb), nullptr);
4991
4992
    if (mWindowType == eWindowType_toplevel) {
4993
      g_signal_connect_after(mShell, "size_allocate",
4994
                             G_CALLBACK(toplevel_window_size_allocate_cb),
4995
                             nullptr);
4996
    }
4997
4998
    GdkScreen* screen = gtk_widget_get_screen(mShell);
4999
    if (!g_signal_handler_find(screen, G_SIGNAL_MATCH_FUNC, 0, 0, nullptr,
5000
                               FuncToGpointer(screen_composited_changed_cb),
5001
                               0)) {
5002
      g_signal_connect(screen, "composited-changed",
5003
                       G_CALLBACK(screen_composited_changed_cb), nullptr);
5004
    }
5005
5006
    GtkSettings* default_settings = gtk_settings_get_default();
5007
    g_signal_connect_after(default_settings, "notify::gtk-theme-name",
5008
                           G_CALLBACK(settings_changed_cb), this);
5009
    g_signal_connect_after(default_settings, "notify::gtk-font-name",
5010
                           G_CALLBACK(settings_changed_cb), this);
5011
    g_signal_connect_after(default_settings, "notify::gtk-enable-animations",
5012
                           G_CALLBACK(settings_changed_cb), this);
5013
    g_signal_connect_after(default_settings, "notify::gtk-decoration-layout",
5014
                           G_CALLBACK(settings_changed_cb), this);
5015
    g_signal_connect_after(default_settings, "notify::gtk-xft-dpi",
5016
                           G_CALLBACK(settings_xft_dpi_changed_cb), this);
5017
    // Text resolution affects system fonts and widget sizes.
5018
    g_signal_connect_after(default_settings, "notify::resolution",
5019
                           G_CALLBACK(settings_changed_cb), this);
5020
    // For remote LookAndFeel, to refresh the content processes' copies:
5021
    g_signal_connect_after(default_settings, "notify::gtk-cursor-blink-time",
5022
                           G_CALLBACK(settings_changed_cb), this);
5023
    g_signal_connect_after(default_settings, "notify::gtk-cursor-blink",
5024
                           G_CALLBACK(settings_changed_cb), this);
5025
    g_signal_connect_after(default_settings,
5026
                           "notify::gtk-entry-select-on-focus",
5027
                           G_CALLBACK(settings_changed_cb), this);
5028
    g_signal_connect_after(default_settings,
5029
                           "notify::gtk-primary-button-warps-slider",
5030
                           G_CALLBACK(settings_changed_cb), this);
5031
    g_signal_connect_after(default_settings, "notify::gtk-menu-popup-delay",
5032
                           G_CALLBACK(settings_changed_cb), this);
5033
    g_signal_connect_after(default_settings, "notify::gtk-dnd-drag-threshold",
5034
                           G_CALLBACK(settings_changed_cb), this);
5035
  }
5036
5037
  if (mContainer) {
5038
    // Widget signals
5039
    g_signal_connect(mContainer, "unrealize",
5040
                     G_CALLBACK(container_unrealize_cb), nullptr);
5041
    g_signal_connect_after(mContainer, "size_allocate",
5042
                           G_CALLBACK(size_allocate_cb), nullptr);
5043
    g_signal_connect(mContainer, "hierarchy-changed",
5044
                     G_CALLBACK(hierarchy_changed_cb), nullptr);
5045
    g_signal_connect(mContainer, "notify::scale-factor",
5046
                     G_CALLBACK(scale_changed_cb), nullptr);
5047
    // Initialize mHasMappedToplevel.
5048
    hierarchy_changed_cb(GTK_WIDGET(mContainer), nullptr);
5049
    // Expose, focus, key, and drag events are sent even to GTK_NO_WINDOW
5050
    // widgets.
5051
    g_signal_connect(G_OBJECT(mContainer), "draw", G_CALLBACK(expose_event_cb),
5052
                     nullptr);
5053
    g_signal_connect(mContainer, "focus_in_event",
5054
                     G_CALLBACK(focus_in_event_cb), nullptr);
5055
    g_signal_connect(mContainer, "focus_out_event",
5056
                     G_CALLBACK(focus_out_event_cb), nullptr);
5057
    g_signal_connect(mContainer, "key_press_event",
5058
                     G_CALLBACK(key_press_event_cb), nullptr);
5059
    g_signal_connect(mContainer, "key_release_event",
5060
                     G_CALLBACK(key_release_event_cb), nullptr);
5061
5062
    gtk_drag_dest_set((GtkWidget*)mContainer, (GtkDestDefaults)0, nullptr, 0,
5063
                      (GdkDragAction)0);
5064
5065
    g_signal_connect(mContainer, "drag_motion",
5066
                     G_CALLBACK(drag_motion_event_cb), nullptr);
5067
    g_signal_connect(mContainer, "drag_leave", G_CALLBACK(drag_leave_event_cb),
5068
                     nullptr);
5069
    g_signal_connect(mContainer, "drag_drop", G_CALLBACK(drag_drop_event_cb),
5070
                     nullptr);
5071
    g_signal_connect(mContainer, "drag_data_received",
5072
                     G_CALLBACK(drag_data_received_event_cb), nullptr);
5073
5074
#ifdef MOZ_X11
5075
    if (mIsX11Display) {
5076
      GtkWidget* widgets[] = {GTK_WIDGET(mContainer),
5077
                              !mDrawToContainer ? mShell : nullptr};
5078
      for (size_t i = 0; i < ArrayLength(widgets) && widgets[i]; ++i) {
5079
        // Double buffering is controlled by the window's owning
5080
        // widget. Disable double buffering for painting directly to the
5081
        // X Window.
5082
        gtk_widget_set_double_buffered(widgets[i], FALSE);
5083
      }
5084
    }
5085
#endif
5086
5087
    // We create input contexts for all containers, except for
5088
    // toplevel popup windows
5089
    if (mWindowType != eWindowType_popup) {
5090
      mIMContext = new IMContextWrapper(this);
5091
    }
5092
  } else if (!mIMContext) {
5093
    nsWindow* container = GetContainerWindow();
5094
    if (container) {
5095
      mIMContext = container->mIMContext;
5096
    }
5097
  }
5098
5099
  if (eventWidget) {
5100
    // These events are sent to the owning widget of the relevant window
5101
    // and propagate up to the first widget that handles the events, so we
5102
    // need only connect on mShell, if it exists, to catch events on its
5103
    // window and windows of mContainer.
5104
    g_signal_connect(eventWidget, "enter-notify-event",
5105
                     G_CALLBACK(enter_notify_event_cb), nullptr);
5106
    g_signal_connect(eventWidget, "leave-notify-event",
5107
                     G_CALLBACK(leave_notify_event_cb), nullptr);
5108
    g_signal_connect(eventWidget, "motion-notify-event",
5109
                     G_CALLBACK(motion_notify_event_cb), nullptr);
5110
    g_signal_connect(eventWidget, "button-press-event",
5111
                     G_CALLBACK(button_press_event_cb), nullptr);
5112
    g_signal_connect(eventWidget, "button-release-event",
5113
                     G_CALLBACK(button_release_event_cb), nullptr);
5114
    g_signal_connect(eventWidget, "scroll-event", G_CALLBACK(scroll_event_cb),
5115
                     nullptr);
5116
    if (gtk_check_version(3, 18, 0) == nullptr) {
5117
      g_signal_connect(eventWidget, "event", G_CALLBACK(generic_event_cb),
5118
                       nullptr);
5119
    }
5120
    g_signal_connect(eventWidget, "touch-event", G_CALLBACK(touch_event_cb),
5121
                     nullptr);
5122
  }
5123
5124
  LOG(("nsWindow [%p] %s %s\n", (void*)this,
5125
       mWindowType == eWindowType_toplevel ? "Toplevel" : "Popup",
5126
       mIsPIPWindow ? "PIP window" : ""));
5127
  if (mShell) {
5128
    LOG(("\tmShell %p mContainer %p mGdkWindow %p 0x%lx\n", mShell, mContainer,
5129
         mGdkWindow, mIsX11Display ? gdk_x11_window_get_xid(mGdkWindow) : 0));
5130
  } else if (mContainer) {
5131
    LOG(("\tmContainer %p mGdkWindow %p\n", mContainer, mGdkWindow));
5132
  } else if (mGdkWindow) {
5133
    LOG(("\tmGdkWindow %p parent %p\n", mGdkWindow,
5134
         gdk_window_get_parent(mGdkWindow)));
5135
  }
5136
5137
  // resize so that everything is set to the right dimensions
5138
  if (!mIsTopLevel)
5139
    Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
5140
5141
#ifdef MOZ_X11
5142
  if (mIsX11Display && mGdkWindow) {
5143
    mXDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
5144
    mXWindow = gdk_x11_window_get_xid(mGdkWindow);
5145
5146
    GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow);
5147
    mXVisual = gdk_x11_visual_get_xvisual(gdkVisual);
5148
    mXDepth = gdk_visual_get_depth(gdkVisual);
5149
    bool shaped = popupNeedsAlphaVisual && !mHasAlphaVisual;
5150
5151
    mSurfaceProvider.Initialize(mXDisplay, mXWindow, mXVisual, mXDepth, shaped);
5152
5153
    if (mIsTopLevel) {
5154
      // Set window manager hint to keep fullscreen windows composited.
5155
      //
5156
      // If the window were to get unredirected, there could be visible
5157
      // tearing because Gecko does not align its framebuffer updates with
5158
      // vblank.
5159
      SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
5160
    }
5161
  }
5162
#  ifdef MOZ_WAYLAND
5163
  else if (!mIsX11Display) {
5164
    mSurfaceProvider.Initialize(this);
5165
    WaylandStartVsync();
5166
  }
5167
#  endif
5168
#endif
5169
5170
  // Set default application name when it's empty.
5171
  if (mGtkWindowAppName.IsEmpty()) {
5172
    mGtkWindowAppName = gAppData->name;
5173
  }
5174
  RefreshWindowClass();
5175
5176
  return NS_OK;
5177
}
5178
5179
void nsWindow::RefreshWindowClass(void) {
5180
  GdkWindow* gdkWindow = gtk_widget_get_window(mShell);
5181
  if (!gdkWindow) {
5182
    return;
5183
  }
5184
5185
  if (!mGtkWindowRoleName.IsEmpty()) {
5186
    gdk_window_set_role(gdkWindow, mGtkWindowRoleName.get());
5187
  }
5188
5189
#ifdef MOZ_X11
5190
  if (!mGtkWindowAppName.IsEmpty() && mIsX11Display) {
5191
    XClassHint* class_hint = XAllocClassHint();
5192
    if (!class_hint) {
5193
      return;
5194
    }
5195
    const char* res_class = gdk_get_program_class();
5196
    if (!res_class) return;
5197
5198
    class_hint->res_name = const_cast<char*>(mGtkWindowAppName.get());
5199
    class_hint->res_class = const_cast<char*>(res_class);
5200
5201
    // Can't use gtk_window_set_wmclass() for this; it prints
5202
    // a warning & refuses to make the change.
5203
    GdkDisplay* display = gdk_display_get_default();
5204
    XSetClassHint(GDK_DISPLAY_XDISPLAY(display),
5205
                  gdk_x11_window_get_xid(gdkWindow), class_hint);
5206
    XFree(class_hint);
5207
  }
5208
#endif /* MOZ_X11 */
5209
}
5210
5211
void nsWindow::SetWindowClass(const nsAString& xulWinType) {
5212
  if (!mShell) return;
5213
5214
  char* res_name = ToNewCString(xulWinType, mozilla::fallible);
5215
  if (!res_name) return;
5216
5217
  const char* role = nullptr;
5218
5219
  // Parse res_name into a name and role. Characters other than
5220
  // [A-Za-z0-9_-] are converted to '_'. Anything after the first
5221
  // colon is assigned to role; if there's no colon, assign the
5222
  // whole thing to both role and res_name.
5223
  for (char* c = res_name; *c; c++) {
5224
    if (':' == *c) {
5225
      *c = 0;
5226
      role = c + 1;
5227
    } else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c)))
5228
      *c = '_';
5229
  }
5230
  res_name[0] = toupper(res_name[0]);
5231
  if (!role) role = res_name;
5232
5233
  mGtkWindowAppName = res_name;
5234
  mGtkWindowRoleName = role;
5235
  free(res_name);
5236
5237
  RefreshWindowClass();
5238
}
5239
5240
void nsWindow::NativeResize() {
5241
  if (!AreBoundsSane()) {
5242
    // If someone has set this so that the needs show flag is false
5243
    // and it needs to be hidden, update the flag and hide the
5244
    // window.  This flag will be cleared the next time someone
5245
    // hides the window or shows it.  It also prevents us from
5246
    // calling NativeShow(false) excessively on the window which
5247
    // causes unneeded X traffic.
5248
    if (!mNeedsShow && mIsShown) {
5249
      mNeedsShow = true;
5250
      NativeShow(false);
5251
    }
5252
    return;
5253
  }
5254
5255
  GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
5256
5257
  LOG(("nsWindow::NativeResize [%p] %d %d\n", (void*)this, size.width,
5258
       size.height));
5259
5260
  if (mIsTopLevel) {
5261
    MOZ_ASSERT(size.width > 0 && size.height > 0,
5262
               "Can't resize window smaller than 1x1.");
5263
    gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
5264
    if (mWaitingForMoveToRectCB) {
5265
      LOG(("Waiting for move to rect, schedulling "));
5266
      mPendingSizeRect = mBounds;
5267
    }
5268
  } else if (mContainer) {
5269
    GtkWidget* widget = GTK_WIDGET(mContainer);
5270
    GtkAllocation allocation, prev_allocation;
5271
    gtk_widget_get_allocation(widget, &prev_allocation);
5272
    allocation.x = prev_allocation.x;
5273
    allocation.y = prev_allocation.y;
5274
    allocation.width = size.width;
5275
    allocation.height = size.height;
5276
    gtk_widget_size_allocate(widget, &allocation);
5277
  } else if (mGdkWindow) {
5278
    gdk_window_resize(mGdkWindow, size.width, size.height);
5279
  }
5280
5281
#ifdef MOZ_X11
5282
  // Notify the GtkCompositorWidget of a ClientSizeChange
5283
  // This is different than OnSizeAllocate to catch initial sizing
5284
  if (mCompositorWidgetDelegate) {
5285
    mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
5286
  }
5287
#endif
5288
5289
  // Does it need to be shown because bounds were previously insane?
5290
  if (mNeedsShow && mIsShown) {
5291
    NativeShow(true);
5292
  }
5293
}
5294
5295
void nsWindow::NativeMoveResize() {
5296
  if (!AreBoundsSane()) {
5297
    // If someone has set this so that the needs show flag is false
5298
    // and it needs to be hidden, update the flag and hide the
5299
    // window.  This flag will be cleared the next time someone
5300
    // hides the window or shows it.  It also prevents us from
5301
    // calling NativeShow(false) excessively on the window which
5302
    // causes unneeded X traffic.
5303
    if (!mNeedsShow && mIsShown) {
5304
      mNeedsShow = true;
5305
      NativeShow(false);
5306
    }
5307
    NativeMove();
5308
5309
    return;
5310
  }
5311
5312
  GdkRectangle size = DevicePixelsToGdkSizeRoundUp(mBounds.Size());
5313
  GdkPoint topLeft = DevicePixelsToGdkPointRoundDown(mBounds.TopLeft());
5314
5315
  LOG(("nsWindow::NativeMoveResize [%p] %d %d %d %d\n", (void*)this, topLeft.x,
5316
       topLeft.y, size.width, size.height));
5317
5318
  if (IsWaylandPopup()) {
5319
    NativeMoveResizeWaylandPopup(&topLeft, &size);
5320
  } else {
5321
    if (mIsTopLevel) {
5322
      // x and y give the position of the window manager frame top-left.
5323
      gtk_window_move(GTK_WINDOW(mShell), topLeft.x, topLeft.y);
5324
      // This sets the client window size.
5325
      MOZ_ASSERT(size.width > 0 && size.height > 0,
5326
                 "Can't resize window smaller than 1x1.");
5327
      gtk_window_resize(GTK_WINDOW(mShell), size.width, size.height);
5328
    } else if (mContainer) {
5329
      GtkAllocation allocation;
5330
      allocation.x = topLeft.x;
5331
      allocation.y = topLeft.y;
5332
      allocation.width = size.width;
5333
      allocation.height = size.height;
5334
      gtk_widget_size_allocate(GTK_WIDGET(mContainer), &allocation);
5335
    } else if (mGdkWindow) {
5336
      gdk_window_move_resize(mGdkWindow, topLeft.x, topLeft.y, size.width,
5337
                             size.height);
5338
    }
5339
  }
5340
5341
#ifdef MOZ_X11
5342
  // Notify the GtkCompositorWidget of a ClientSizeChange
5343
  // This is different than OnSizeAllocate to catch initial sizing
5344
  if (mCompositorWidgetDelegate) {
5345
    mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize());
5346
  }
5347
#endif
5348
5349
  // Does it need to be shown because bounds were previously insane?
5350
  if (mNeedsShow && mIsShown) {
5351
    NativeShow(true);
5352
  }
5353
}
5354
5355
void nsWindow::PauseRemoteRenderer() {
5356
#ifdef MOZ_WAYLAND
5357
  if (!mIsDestroyed) {
5358
    if (mContainer) {
5359
      // Because wl_egl_window is destroyed on moz_container_unmap(),
5360
      // the current compositor cannot use it anymore. To avoid crash,
5361
      // pause the compositor and destroy EGLSurface & resume the compositor
5362
      // and re-create EGLSurface on next expose event.
5363
5364
      // moz_container_wayland_has_egl_window() could not be used here, since
5365
      // there is a case that resume compositor is not completed yet.
5366
5367
      CompositorBridgeChild* remoteRenderer = GetRemoteRenderer();
5368
      bool needsCompositorPause = !mNeedsCompositorResume && !!remoteRenderer &&
5369
                                  mCompositorWidgetDelegate;
5370
      if (needsCompositorPause) {
5371
        // XXX slow sync IPC
5372
        remoteRenderer->SendPause();
5373
        // Re-request initial draw callback
5374
        RefPtr<nsWindow> self(this);
5375
        moz_container_wayland_add_initial_draw_callback(
5376
            mContainer, [self]() -> void {
5377
              self->mNeedsCompositorResume = true;
5378
              self->MaybeResumeCompositor();
5379
            });
5380
      } else {
5381
        DestroyLayerManager();
5382
      }
5383
    }
5384
  }
5385
#endif
5386
}
5387
5388
void nsWindow::HideWaylandWindow() {
5389
  if (mWindowType == eWindowType_popup) {
5390
    LOG(("nsWindow::HideWaylandWindow: popup [%p]\n", this));
5391
    GList* foundWindow = g_list_find(gVisibleWaylandPopupWindows, this);
5392
    if (foundWindow) {
5393
      gVisibleWaylandPopupWindows =
5394
          g_list_delete_link(gVisibleWaylandPopupWindows, foundWindow);
5395
    }
5396
  }
5397
  PauseRemoteRenderer();
5398
  gtk_widget_hide(mShell);
5399
}
5400
5401
void nsWindow::WaylandStartVsync() {
5402
#ifdef MOZ_WAYLAND
5403
  // only use for toplevel windows for now - see bug 1619246
5404
  if (!gUseWaylandVsync || mWindowType != eWindowType_toplevel) {
5405
    return;
5406
  }
5407
5408
  if (!mWaylandVsyncSource) {
5409
    mWaylandVsyncSource = new mozilla::WaylandVsyncSource(mContainer);
5410
  }
5411
  WaylandVsyncSource::WaylandDisplay& display =
5412
      static_cast<WaylandVsyncSource::WaylandDisplay&>(
5413
          mWaylandVsyncSource->GetGlobalDisplay());
5414
  display.EnableMonitor();
5415
#endif
5416
}
5417
5418
void nsWindow::WaylandStopVsync() {
5419
#ifdef MOZ_WAYLAND
5420
  if (mWaylandVsyncSource) {
5421
    // The widget is going to be hidden, so clear the surface of our
5422
    // vsync source.
5423
    WaylandVsyncSource::WaylandDisplay& display =
5424
        static_cast<WaylandVsyncSource::WaylandDisplay&>(
5425
            mWaylandVsyncSource->GetGlobalDisplay());
5426
    display.DisableMonitor();
5427
  }
5428
#endif
5429
}
5430
5431
void nsWindow::NativeShow(bool aAction) {
5432
  if (aAction) {
5433
    // unset our flag now that our window has been shown
5434
    mNeedsShow = false;
5435
5436
    if (mIsTopLevel) {
5437
      // Set up usertime/startupID metadata for the created window.
5438
      if (mWindowType != eWindowType_invisible) {
5439
        SetUserTimeAndStartupIDForActivatedWindow(mShell);
5440
      }
5441
      // Update popup window hierarchy run-time on Wayland.
5442
      if (IsWaylandPopup()) {
5443
        if (!ConfigureWaylandPopupWindows()) {
5444
          mNeedsShow = true;
5445
          return;
5446
        }
5447
      }
5448
5449
      LOG(("  calling gtk_widget_show(mShell)\n"));
5450
      gtk_widget_show(mShell);
5451
      if (!mIsX11Display) {
5452
        WaylandStartVsync();
5453
      }
5454
    } else if (mContainer) {
5455
      LOG(("  calling gtk_widget_show(mContainer)\n"));
5456
      gtk_widget_show(GTK_WIDGET(mContainer));
5457
    } else if (mGdkWindow) {
5458
      LOG(("  calling gdk_window_show_unraised\n"));
5459
      gdk_window_show_unraised(mGdkWindow);
5460
    }
5461
  } else {
5462
    // There's a chance that when the popup will be shown again it might be
5463
    // resized because parent could be moved meanwhile.
5464
    mPreferredPopupRect = nsRect(0, 0, 0, 0);
5465
    mPreferredPopupRectFlushed = false;
5466
    if (!mIsX11Display) {
5467
      WaylandStopVsync();
5468
      if (IsWaylandPopup() && IsMainMenuWindow()) {
5469
        CleanupWaylandPopups();
5470
      }
5471
      HideWaylandWindow();
5472
    } else if (mIsTopLevel) {
5473
      // Workaround window freezes on GTK versions before 3.21.2 by
5474
      // ensuring that configure events get dispatched to windows before
5475
      // they are unmapped. See bug 1225044.
5476
      if (gtk_check_version(3, 21, 2) != nullptr && mPendingConfigures > 0) {
5477
        GtkAllocation allocation;
5478
        gtk_widget_get_allocation(GTK_WIDGET(mShell), &allocation);
5479
5480
        GdkEventConfigure event;
5481
        PodZero(&event);
5482
        event.type = GDK_CONFIGURE;
5483
        event.window = mGdkWindow;
5484
        event.send_event = TRUE;
5485
        event.x = allocation.x;
5486
        event.y = allocation.y;
5487
        event.width = allocation.width;
5488
        event.height = allocation.height;
5489
5490
        auto shellClass = GTK_WIDGET_GET_CLASS(mShell);
5491
        for (unsigned int i = 0; i < mPendingConfigures; i++) {
5492
          Unused << shellClass->configure_event(mShell, &event);
5493
        }
5494
        mPendingConfigures = 0;
5495
      }
5496
      gtk_widget_hide(mShell);
5497
5498
      ClearTransparencyBitmap();  // Release some resources
5499
    } else if (mContainer) {
5500
      gtk_widget_hide(GTK_WIDGET(mContainer));
5501
    } else if (mGdkWindow) {
5502
      gdk_window_hide(mGdkWindow);
5503
    }
5504
  }
5505
}
5506
5507
void nsWindow::SetHasMappedToplevel(bool aState) {
5508
  // Even when aState == mHasMappedToplevel (as when this method is called
5509
  // from Show()), child windows need to have their state checked, so don't
5510
  // return early.
5511
  bool oldState = mHasMappedToplevel;
5512
  mHasMappedToplevel = aState;
5513
5514
  // mHasMappedToplevel is not updated for children of windows that are
5515
  // hidden; GDK knows not to send expose events for these windows.  The
5516
  // state is recorded on the hidden window itself, but, for child trees of
5517
  // hidden windows, their state essentially becomes disconnected from their
5518
  // hidden parent.  When the hidden parent gets shown, the child trees are
5519
  // reconnected, and the state of the window being shown can be easily
5520
  // propagated.
5521
  if (!mIsShown || !mGdkWindow) return;
5522
5523
  if (aState && !oldState) {
5524
    // Check that a grab didn't fail due to the window not being
5525
    // viewable.
5526
    EnsureGrabs();
5527
  }
5528
5529
  for (GList* children = gdk_window_peek_children(mGdkWindow); children;
5530
       children = children->next) {
5531
    GdkWindow* gdkWin = GDK_WINDOW(children->data);
5532
    nsWindow* child = get_window_for_gdk_window(gdkWin);
5533
5534
    if (child && child->mHasMappedToplevel != aState) {
5535
      child->SetHasMappedToplevel(aState);
5536
    }
5537
  }
5538
}
5539
5540
LayoutDeviceIntSize nsWindow::GetSafeWindowSize(LayoutDeviceIntSize aSize) {
5541
  // The X protocol uses CARD32 for window sizes, but the server (1.11.3)
5542
  // reads it as CARD16.  Sizes of pixmaps, used for drawing, are (unsigned)
5543
  // CARD16 in the protocol, but the server's ProcCreatePixmap returns
5544
  // BadAlloc if dimensions cannot be represented by signed shorts.
5545
  // Because we are creating Cairo surfaces to represent window buffers,
5546
  // we also must ensure that the window can fit in a Cairo surface.
5547
  LayoutDeviceIntSize result = aSize;
5548
  int32_t maxSize = 32767;
5549
  if (mLayerManager && mLayerManager->AsKnowsCompositor()) {
5550
    maxSize = std::min(maxSize,
5551
                       mLayerManager->AsKnowsCompositor()->GetMaxTextureSize());
5552
  }
5553
  if (result.width > maxSize) {
5554
    result.width = maxSize;
5555
  }
5556
  if (result.height > maxSize) {
5557
    result.height = maxSize;
5558
  }
5559
  return result;
5560
}
5561
5562
void nsWindow::EnsureGrabs(void) {
5563
  if (mRetryPointerGrab) GrabPointer(sRetryGrabTime);
5564
}
5565
5566
void nsWindow::CleanLayerManagerRecursive(void) {
5567
  if (mLayerManager) {
5568
    mLayerManager->Destroy();
5569
    mLayerManager = nullptr;
5570
  }
5571
5572
  DestroyCompositor();
5573
5574
  GList* children = gdk_window_peek_children(mGdkWindow);
5575
  for (GList* list = children; list; list = list->next) {
5576
    nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
5577
    if (window) {
5578
      window->CleanLayerManagerRecursive();
5579
    }
5580
  }
5581
}
5582
5583
void nsWindow::SetTransparencyMode(nsTransparencyMode aMode) {
5584
  if (!mShell) {
5585
    // Pass the request to the toplevel window
5586
    GtkWidget* topWidget = GetToplevelWidget();
5587
    if (!topWidget) return;
5588
5589
    nsWindow* topWindow = get_window_for_gtk_widget(topWidget);
5590
    if (!topWindow) return;
5591
5592
    topWindow->SetTransparencyMode(aMode);
5593
    return;
5594
  }
5595
5596
  bool isTransparent = aMode == eTransparencyTransparent;
5597
5598
  if (mIsTransparent == isTransparent) {
5599
    return;
5600
  }
5601
5602
  if (mWindowType != eWindowType_popup) {
5603
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1344839 reported
5604
    // problems cleaning the layer manager for toplevel windows.
5605
    // Ignore the request so as to workaround that.
5606
    // mIsTransparent is set in Create() if transparency may be required.
5607
    if (isTransparent) {
5608
      NS_WARNING("Transparent mode not supported on non-popup windows.");
5609
    }
5610
    return;
5611
  }
5612
5613
  if (!isTransparent) {
5614
    ClearTransparencyBitmap();
5615
  }  // else the new default alpha values are "all 1", so we don't
5616
  // need to change anything yet
5617
5618
  mIsTransparent = isTransparent;
5619
5620
  if (!mHasAlphaVisual) {
5621
    // The choice of layer manager depends on
5622
    // GtkCompositorWidgetInitData::Shaped(), which will need to change, so
5623
    // clean out the old layer manager.
5624
    CleanLayerManagerRecursive();
5625
  }
5626
}
5627
5628
nsTransparencyMode nsWindow::GetTransparencyMode() {
5629
  if (!mShell) {
5630
    // Pass the request to the toplevel window
5631
    GtkWidget* topWidget = GetToplevelWidget();
5632
    if (!topWidget) {
5633
      return eTransparencyOpaque;
5634
    }
5635
5636
    nsWindow* topWindow = get_window_for_gtk_widget(topWidget);
5637
    if (!topWindow) {
5638
      return eTransparencyOpaque;
5639
    }
5640
5641
    return topWindow->GetTransparencyMode();
5642
  }
5643
5644
  return mIsTransparent ? eTransparencyTransparent : eTransparencyOpaque;
5645
}
5646
5647
void nsWindow::SetWindowMouseTransparent(bool aIsTransparent) {
5648
  if (!mGdkWindow) {
5649
    return;
5650
  }
5651
5652
  cairo_rectangle_int_t emptyRect = {0, 0, 0, 0};
5653
  cairo_region_t* region =
5654
      aIsTransparent ? cairo_region_create_rectangle(&emptyRect) : nullptr;
5655
5656
  gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
5657
  if (region) {
5658
    cairo_region_destroy(region);
5659
  }
5660
}
5661
5662
// For setting the draggable titlebar region from CSS
5663
// with -moz-window-dragging: drag.
5664
void nsWindow::UpdateWindowDraggingRegion(
5665
    const LayoutDeviceIntRegion& aRegion) {
5666
  if (mDraggableRegion != aRegion) {
5667
    mDraggableRegion = aRegion;
5668
  }
5669
}
5670
5671
// See subtract_corners_from_region() at gtk/gtkwindow.c
5672
// We need to subtract corners from toplevel window opaque region
5673
// to draw transparent corners of default Gtk titlebar.
5674
// Both implementations (cairo_region_t and wl_region) needs to be synced.
5675
static void SubtractTitlebarCorners(cairo_region_t* aRegion, int aX, int aY,
5676
                                    int aWindowWidth) {
5677
  cairo_rectangle_int_t rect = {aX, aY, TITLEBAR_SHAPE_MASK_HEIGHT,
5678
                                TITLEBAR_SHAPE_MASK_HEIGHT};
5679
  cairo_region_subtract_rectangle(aRegion, &rect);
5680
  rect = {
5681
      aX + aWindowWidth - TITLEBAR_SHAPE_MASK_HEIGHT,
5682
      aY,
5683
      TITLEBAR_SHAPE_MASK_HEIGHT,
5684
      TITLEBAR_SHAPE_MASK_HEIGHT,
5685
  };
5686
  cairo_region_subtract_rectangle(aRegion, &rect);
5687
}
5688
5689
void nsWindow::UpdateTopLevelOpaqueRegion(void) {
5690
  if (!mCompositedScreen) {
5691
    return;
5692
  }
5693
5694
  GdkWindow* window =
5695
      (mDrawToContainer) ? gtk_widget_get_window(mShell) : mGdkWindow;
5696
  if (!window) {
5697
    return;
5698
  }
5699
  MOZ_ASSERT(gdk_window_get_window_type(window) == GDK_WINDOW_TOPLEVEL);
5700
5701
  int x = 0;
5702
  int y = 0;
5703
5704
  if (mDrawToContainer) {
5705
    gdk_window_get_position(mGdkWindow, &x, &y);
5706
  }
5707
5708
  int width = DevicePixelsToGdkCoordRoundDown(mBounds.width);
5709
  int height = DevicePixelsToGdkCoordRoundDown(mBounds.height);
5710
5711
  cairo_region_t* region = cairo_region_create();
5712
  cairo_rectangle_int_t rect = {x, y, width, height};
5713
  cairo_region_union_rectangle(region, &rect);
5714
5715
  bool subtractCorners = DoDrawTilebarCorners();
5716
  if (subtractCorners) {
5717
    SubtractTitlebarCorners(region, x, y, width);
5718
  }
5719
5720
  gdk_window_set_opaque_region(window, region);
5721
5722
  cairo_region_destroy(region);
5723
5724
#ifdef MOZ_WAYLAND
5725
  if (!mIsX11Display) {
5726
    moz_container_wayland_update_opaque_region(mContainer, subtractCorners);
5727
  }
5728
#endif
5729
}
5730
5731
bool nsWindow::IsChromeWindowTitlebar() {
5732
  return mDrawInTitlebar && !mIsPIPWindow &&
5733
         mWindowType == eWindowType_toplevel;
5734
}
5735
5736
bool nsWindow::DoDrawTilebarCorners() {
5737
  return IsChromeWindowTitlebar() && mSizeState == nsSizeMode_Normal &&
5738
         !mIsTiled;
5739
}
5740
5741
nsresult nsWindow::ConfigureChildren(
5742
    const nsTArray<Configuration>& aConfigurations) {
5743
  // If this is a remotely updated widget we receive clipping, position, and
5744
  // size information from a source other than our owner. Don't let our parent
5745
  // update this information.
5746
  if (mWindowType == eWindowType_plugin_ipc_chrome) {
5747
    return NS_OK;
5748
  }
5749
5750
  for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
5751
    const Configuration& configuration = aConfigurations[i];
5752
    auto* w = static_cast<nsWindow*>(configuration.mChild.get());
5753
    NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child");
5754
    w->SetWindowClipRegion(configuration.mClipRegion, true);
5755
    if (w->mBounds.Size() != configuration.mBounds.Size()) {
5756
      w->Resize(configuration.mBounds.x, configuration.mBounds.y,
5757
                configuration.mBounds.width, configuration.mBounds.height,
5758
                true);
5759
    } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) {
5760
      w->Move(configuration.mBounds.x, configuration.mBounds.y);
5761
    }
5762
    w->SetWindowClipRegion(configuration.mClipRegion, false);
5763
  }
5764
  return NS_OK;
5765
}
5766
5767
nsresult nsWindow::SetWindowClipRegion(
5768
    const nsTArray<LayoutDeviceIntRect>& aRects, bool aIntersectWithExisting) {
5769
  const nsTArray<LayoutDeviceIntRect>* newRects = &aRects;
5770
5771
  AutoTArray<LayoutDeviceIntRect, 1> intersectRects;
5772
  if (aIntersectWithExisting) {
5773
    AutoTArray<LayoutDeviceIntRect, 1> existingRects;
5774
    GetWindowClipRegion(&existingRects);
5775
5776
    LayoutDeviceIntRegion existingRegion = RegionFromArray(existingRects);
5777
    LayoutDeviceIntRegion newRegion = RegionFromArray(aRects);
5778
    LayoutDeviceIntRegion intersectRegion;
5779
    intersectRegion.And(newRegion, existingRegion);
5780
5781
    // If mClipRects is null we haven't set a clip rect yet, so we
5782
    // need to set the clip even if it is equal.
5783
    if (mClipRects && intersectRegion.IsEqual(existingRegion)) {
5784
      return NS_OK;
5785
    }
5786
5787
    if (!intersectRegion.IsEqual(newRegion)) {
5788
      ArrayFromRegion(intersectRegion, intersectRects);
5789
      newRects = &intersectRects;
5790
    }
5791
  }
5792
5793
  if (IsWindowClipRegionEqual(*newRects)) return NS_OK;
5794
5795
  StoreWindowClipRegion(*newRects);
5796
5797
  if (!mGdkWindow) return NS_OK;
5798
5799
  cairo_region_t* region = cairo_region_create();
5800
  for (uint32_t i = 0; i < newRects->Length(); ++i) {
5801
    const LayoutDeviceIntRect& r = newRects->ElementAt(i);
5802
    cairo_rectangle_int_t rect = {r.x, r.y, r.width, r.height};
5803
    cairo_region_union_rectangle(region, &rect);
5804
  }
5805
5806
  gdk_window_shape_combine_region(mGdkWindow, region, 0, 0);
5807
  cairo_region_destroy(region);
5808
5809
  return NS_OK;
5810
}
5811
5812
void nsWindow::ResizeTransparencyBitmap() {
5813
  if (!mTransparencyBitmap) return;
5814
5815
  if (mBounds.width == mTransparencyBitmapWidth &&
5816
      mBounds.height == mTransparencyBitmapHeight)
5817
    return;
5818
5819
  int32_t newRowBytes = GetBitmapStride(mBounds.width);
5820
  int32_t newSize = newRowBytes * mBounds.height;
5821
  auto* newBits = new gchar[newSize];
5822
  // fill new mask with "transparent", first
5823
  memset(newBits, 0, newSize);
5824
5825
  // Now copy the intersection of the old and new areas into the new mask
5826
  int32_t copyWidth = std::min(mBounds.width, mTransparencyBitmapWidth);
5827
  int32_t copyHeight = std::min(mBounds.height, mTransparencyBitmapHeight);
5828
  int32_t oldRowBytes = GetBitmapStride(mTransparencyBitmapWidth);
5829
  int32_t copyBytes = GetBitmapStride(copyWidth);
5830
5831
  int32_t i;
5832
  gchar* fromPtr = mTransparencyBitmap;
5833
  gchar* toPtr = newBits;
5834
  for (i = 0; i < copyHeight; i++) {
5835
    memcpy(toPtr, fromPtr, copyBytes);
5836
    fromPtr += oldRowBytes;
5837
    toPtr += newRowBytes;
5838
  }
5839
5840
  delete[] mTransparencyBitmap;
5841
  mTransparencyBitmap = newBits;
5842
  mTransparencyBitmapWidth = mBounds.width;
5843
  mTransparencyBitmapHeight = mBounds.height;
5844
}
5845
5846
static bool ChangedMaskBits(gchar* aMaskBits, int32_t aMaskWidth,
5847
                            int32_t aMaskHeight, const nsIntRect& aRect,
5848
                            uint8_t* aAlphas, int32_t aStride) {
5849
  int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
5850
  int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
5851
  for (y = aRect.y; y < yMax; y++) {
5852
    gchar* maskBytes = aMaskBits + y * maskBytesPerRow;
5853
    uint8_t* alphas = aAlphas;
5854
    for (x = aRect.x; x < xMax; x++) {
5855
      bool newBit = *alphas > 0x7f;
5856
      alphas++;
5857
5858
      gchar maskByte = maskBytes[x >> 3];
5859
      bool maskBit = (maskByte & (1 << (x & 7))) != 0;
5860
5861
      if (maskBit != newBit) {
5862
        return true;
5863
      }
5864
    }
5865
    aAlphas += aStride;
5866
  }
5867
5868
  return false;
5869
}
5870
5871
static void UpdateMaskBits(gchar* aMaskBits, int32_t aMaskWidth,
5872
                           int32_t aMaskHeight, const nsIntRect& aRect,
5873
                           uint8_t* aAlphas, int32_t aStride) {
5874
  int32_t x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
5875
  int32_t maskBytesPerRow = GetBitmapStride(aMaskWidth);
5876
  for (y = aRect.y; y < yMax; y++) {
5877
    gchar* maskBytes = aMaskBits + y * maskBytesPerRow;
5878
    uint8_t* alphas = aAlphas;
5879
    for (x = aRect.x; x < xMax; x++) {
5880
      bool newBit = *alphas > 0x7f;
5881
      alphas++;
5882
5883
      gchar mask = 1 << (x & 7);
5884
      gchar maskByte = maskBytes[x >> 3];
5885
      // Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
5886
      maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
5887
    }
5888
    aAlphas += aStride;
5889
  }
5890
}
5891
5892
void nsWindow::ApplyTransparencyBitmap() {
5893
#ifdef MOZ_X11
5894
  // We use X11 calls where possible, because GDK handles expose events
5895
  // for shaped windows in a way that's incompatible with us (Bug 635903).
5896
  // It doesn't occur when the shapes are set through X.
5897
  Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
5898
  Window xDrawable = GDK_WINDOW_XID(mGdkWindow);
5899
  Pixmap maskPixmap = XCreateBitmapFromData(
5900
      xDisplay, xDrawable, mTransparencyBitmap, mTransparencyBitmapWidth,
5901
      mTransparencyBitmapHeight);
5902
  XShapeCombineMask(xDisplay, xDrawable, ShapeBounding, 0, 0, maskPixmap,
5903
                    ShapeSet);
5904
  XFreePixmap(xDisplay, maskPixmap);
5905
#else
5906
  cairo_surface_t* maskBitmap;
5907
  maskBitmap = cairo_image_surface_create_for_data(
5908
      (unsigned char*)mTransparencyBitmap, CAIRO_FORMAT_A1,
5909
      mTransparencyBitmapWidth, mTransparencyBitmapHeight,
5910
      GetBitmapStride(mTransparencyBitmapWidth));
5911
  if (!maskBitmap) return;
5912
5913
  cairo_region_t* maskRegion = gdk_cairo_region_create_from_surface(maskBitmap);
5914
  gtk_widget_shape_combine_region(mShell, maskRegion);
5915
  cairo_region_destroy(maskRegion);
5916
  cairo_surface_destroy(maskBitmap);
5917
#endif  // MOZ_X11
5918
}
5919
5920
void nsWindow::ClearTransparencyBitmap() {
5921
  if (!mTransparencyBitmap) return;
5922
5923
  delete[] mTransparencyBitmap;
5924
  mTransparencyBitmap = nullptr;
5925
  mTransparencyBitmapWidth = 0;
5926
  mTransparencyBitmapHeight = 0;
5927
5928
  if (!mShell) return;
5929
5930
#ifdef MOZ_X11
5931
  if (!mGdkWindow) return;
5932
5933
  Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
5934
  Window xWindow = gdk_x11_window_get_xid(mGdkWindow);
5935
5936
  XShapeCombineMask(xDisplay, xWindow, ShapeBounding, 0, 0, X11None, ShapeSet);
5937
#endif
5938
}
5939
5940
nsresult nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect,
5941
                                                        uint8_t* aAlphas,
5942
                                                        int32_t aStride) {
5943
  if (!mShell) {
5944
    // Pass the request to the toplevel window
5945
    GtkWidget* topWidget = GetToplevelWidget();
5946
    if (!topWidget) return NS_ERROR_FAILURE;
5947
5948
    nsWindow* topWindow = get_window_for_gtk_widget(topWidget);
5949
    if (!topWindow) return NS_ERROR_FAILURE;
5950
5951
    return topWindow->UpdateTranslucentWindowAlphaInternal(aRect, aAlphas,
5952
                                                           aStride);
5953
  }
5954
5955
  NS_ASSERTION(mIsTransparent, "Window is not transparent");
5956
  NS_ASSERTION(!mTransparencyBitmapForTitlebar,
5957
               "Transparency bitmap is already used for titlebar rendering");
5958
5959
  if (mTransparencyBitmap == nullptr) {
5960
    int32_t size = GetBitmapStride(mBounds.width) * mBounds.height;
5961
    mTransparencyBitmap = new gchar[size];
5962
    memset(mTransparencyBitmap, 255, size);
5963
    mTransparencyBitmapWidth = mBounds.width;
5964
    mTransparencyBitmapHeight = mBounds.height;
5965
  } else {
5966
    ResizeTransparencyBitmap();
5967
  }
5968
5969
  nsIntRect rect;
5970
  rect.IntersectRect(aRect, nsIntRect(0, 0, mBounds.width, mBounds.height));
5971
5972
  if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height, rect,
5973
                       aAlphas, aStride))
5974
    // skip the expensive stuff if the mask bits haven't changed; hopefully
5975
    // this is the common case
5976
    return NS_OK;
5977
5978
  UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height, rect,
5979
                 aAlphas, aStride);
5980
5981
  if (!mNeedsShow) {
5982
    ApplyTransparencyBitmap();
5983
  }
5984
  return NS_OK;
5985
}
5986
5987
LayoutDeviceIntRect nsWindow::GetTitlebarRect() {
5988
  if (!mGdkWindow || !mDrawInTitlebar) {
5989
    return LayoutDeviceIntRect();
5990
  }
5991
5992
  return LayoutDeviceIntRect(0, 0, mBounds.width, TITLEBAR_SHAPE_MASK_HEIGHT);
5993
}
5994
5995
void nsWindow::UpdateTitlebarTransparencyBitmap() {
5996
  NS_ASSERTION(mTransparencyBitmapForTitlebar,
5997
               "Transparency bitmap is already used to draw window shape");
5998
5999
  if (!mGdkWindow || !mDrawInTitlebar ||
6000
      (mBounds.width == mTransparencyBitmapWidth &&
6001
       mBounds.height == mTransparencyBitmapHeight)) {
6002
    return;
6003
  }
6004
6005
  bool maskCreate =
6006
      !mTransparencyBitmap || mBounds.width > mTransparencyBitmapWidth;
6007
6008
  bool maskUpdate =
6009
      !mTransparencyBitmap || mBounds.width != mTransparencyBitmapWidth;
6010
6011
  if (maskCreate) {
6012
    if (mTransparencyBitmap) {
6013
      delete[] mTransparencyBitmap;
6014
    }
6015
    int32_t size = GetBitmapStride(mBounds.width) * TITLEBAR_SHAPE_MASK_HEIGHT;
6016
    mTransparencyBitmap = new gchar[size];
6017
    mTransparencyBitmapWidth = mBounds.width;
6018
  } else {
6019
    mTransparencyBitmapWidth = mBounds.width;
6020
  }
6021
  mTransparencyBitmapHeight = mBounds.height;
6022
6023
  if (maskUpdate) {
6024
    cairo_surface_t* surface = cairo_image_surface_create(
6025
        CAIRO_FORMAT_A8, mTransparencyBitmapWidth, TITLEBAR_SHAPE_MASK_HEIGHT);
6026
    if (!surface) return;
6027
6028
    cairo_t* cr = cairo_create(surface);
6029
6030
    GtkWidgetState state;
6031
    memset((void*)&state, 0, sizeof(state));
6032
    GdkRectangle rect = {0, 0, mTransparencyBitmapWidth,
6033
                         TITLEBAR_SHAPE_MASK_HEIGHT};
6034
6035
    moz_gtk_widget_paint(MOZ_GTK_HEADER_BAR, cr, &rect, &state, 0,
6036
                         GTK_TEXT_DIR_NONE);
6037
6038
    cairo_destroy(cr);
6039
    cairo_surface_mark_dirty(surface);
6040
    cairo_surface_flush(surface);
6041
6042
    UpdateMaskBits(
6043
        mTransparencyBitmap, mTransparencyBitmapWidth,
6044
        TITLEBAR_SHAPE_MASK_HEIGHT,
6045
        nsIntRect(0, 0, mTransparencyBitmapWidth, TITLEBAR_SHAPE_MASK_HEIGHT),
6046
        cairo_image_surface_get_data(surface),
6047
        cairo_format_stride_for_width(CAIRO_FORMAT_A8,
6048
                                      mTransparencyBitmapWidth));
6049
6050
    cairo_surface_destroy(surface);
6051
  }
6052
6053
  if (!mNeedsShow) {
6054
    Display* xDisplay = GDK_WINDOW_XDISPLAY(mGdkWindow);
6055
    Window xDrawable = GDK_WINDOW_XID(mGdkWindow);
6056
6057
    Pixmap maskPixmap = XCreateBitmapFromData(
6058
        xDisplay, xDrawable, mTransparencyBitmap, mTransparencyBitmapWidth,
6059
        TITLEBAR_SHAPE_MASK_HEIGHT);
6060
6061
    XShapeCombineMask(xDisplay, xDrawable, ShapeBounding, 0, 0, maskPixmap,
6062
                      ShapeSet);
6063
6064
    if (mTransparencyBitmapHeight > TITLEBAR_SHAPE_MASK_HEIGHT) {
6065
      XRectangle rect = {0, 0, (unsigned short)mTransparencyBitmapWidth,
6066
                         (unsigned short)(mTransparencyBitmapHeight -
6067
                                          TITLEBAR_SHAPE_MASK_HEIGHT)};
6068
      XShapeCombineRectangles(xDisplay, xDrawable, ShapeBounding, 0,
6069
                              TITLEBAR_SHAPE_MASK_HEIGHT, &rect, 1, ShapeUnion,
6070
                              0);
6071
    }
6072
6073
    XFreePixmap(xDisplay, maskPixmap);
6074
  }
6075
}
6076
6077
void nsWindow::GrabPointer(guint32 aTime) {
6078
  LOG(("GrabPointer time=0x%08x retry=%d\n", (unsigned int)aTime,
6079
       mRetryPointerGrab));
6080
6081
  mRetryPointerGrab = false;
6082
  sRetryGrabTime = aTime;
6083
6084
  // If the window isn't visible, just set the flag to retry the
6085
  // grab.  When this window becomes visible, the grab will be
6086
  // retried.
6087
  if (!mHasMappedToplevel) {
6088
    LOG(("GrabPointer: window not visible\n"));
6089
    mRetryPointerGrab = true;
6090
    return;
6091
  }
6092
6093
  if (!mGdkWindow) return;
6094
6095
  if (!mIsX11Display) {
6096
    // Don't to the grab on Wayland as it causes a regression
6097
    // from Bug 1377084.
6098
    return;
6099
  }
6100
6101
  gint retval;
6102
  // Note that we need GDK_TOUCH_MASK below to work around a GDK/X11 bug that
6103
  // causes touch events that would normally be received by this client on
6104
  // other windows to be discarded during the grab.
6105
  retval = gdk_pointer_grab(
6106
      mGdkWindow, TRUE,
6107
      (GdkEventMask)(GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6108
                     GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6109
                     GDK_POINTER_MOTION_MASK | GDK_TOUCH_MASK),
6110
      (GdkWindow*)nullptr, nullptr, aTime);
6111
6112
  if (retval == GDK_GRAB_NOT_VIEWABLE) {
6113
    LOG(("GrabPointer: window not viewable; will retry\n"));
6114
    mRetryPointerGrab = true;
6115
  } else if (retval != GDK_GRAB_SUCCESS) {
6116
    LOG(("GrabPointer: pointer grab failed: %i\n", retval));
6117
    // A failed grab indicates that another app has grabbed the pointer.
6118
    // Check for rollup now, because, without the grab, we likely won't
6119
    // get subsequent button press events. Do this with an event so that
6120
    // popups don't rollup while potentially adjusting the grab for
6121
    // this popup.
6122
    nsCOMPtr<nsIRunnable> event =
6123
        NewRunnableMethod("nsWindow::CheckForRollupDuringGrab", this,
6124
                          &nsWindow::CheckForRollupDuringGrab);
6125
    NS_DispatchToCurrentThread(event.forget());
6126
  }
6127
}
6128
6129
void nsWindow::ReleaseGrabs(void) {
6130
  LOG(("ReleaseGrabs\n"));
6131
6132
  mRetryPointerGrab = false;
6133
6134
  if (!mIsX11Display) {
6135
    // Don't to the ungrab on Wayland as it causes a regression
6136
    // from Bug 1377084.
6137
    return;
6138
  }
6139
6140
  gdk_pointer_ungrab(GDK_CURRENT_TIME);
6141
}
6142
6143
GtkWidget* nsWindow::GetToplevelWidget() {
6144
  if (mShell) {
6145
    return mShell;
6146
  }
6147
6148
  GtkWidget* widget = GetMozContainerWidget();
6149
  if (!widget) return nullptr;
6150
6151
  return gtk_widget_get_toplevel(widget);
6152
}
6153
6154
GtkWidget* nsWindow::GetMozContainerWidget() {
6155
  if (!mGdkWindow) return nullptr;
6156
6157
  if (mContainer) return GTK_WIDGET(mContainer);
6158
6159
  GtkWidget* owningWidget = get_gtk_widget_for_gdk_window(mGdkWindow);
6160
  return owningWidget;
6161
}
6162
6163
nsWindow* nsWindow::GetContainerWindow() {
6164
  GtkWidget* owningWidget = GetMozContainerWidget();
6165
  if (!owningWidget) return nullptr;
6166
6167
  nsWindow* window = get_window_for_gtk_widget(owningWidget);
6168
  NS_ASSERTION(window, "No nsWindow for container widget");
6169
  return window;
6170
}
6171
6172
void nsWindow::SetUrgencyHint(GtkWidget* top_window, bool state) {
6173
  if (!top_window) return;
6174
6175
  gdk_window_set_urgency_hint(gtk_widget_get_window(top_window), state);
6176
}
6177
6178
void nsWindow::SetDefaultIcon(void) { SetIcon(u"default"_ns); }
6179
6180
gint nsWindow::ConvertBorderStyles(nsBorderStyle aStyle) {
6181
  gint w = 0;
6182
6183
  if (aStyle == eBorderStyle_default) return -1;
6184
6185
  // note that we don't handle eBorderStyle_close yet
6186
  if (aStyle & eBorderStyle_all) w |= GDK_DECOR_ALL;
6187
  if (aStyle & eBorderStyle_border) w |= GDK_DECOR_BORDER;
6188
  if (aStyle & eBorderStyle_resizeh) w |= GDK_DECOR_RESIZEH;
6189
  if (aStyle & eBorderStyle_title) w |= GDK_DECOR_TITLE;
6190
  if (aStyle & eBorderStyle_menu) w |= GDK_DECOR_MENU;
6191
  if (aStyle & eBorderStyle_minimize) w |= GDK_DECOR_MINIMIZE;
6192
  if (aStyle & eBorderStyle_maximize) w |= GDK_DECOR_MAXIMIZE;
6193
6194
  return w;
6195
}
6196
6197
class FullscreenTransitionWindow final : public nsISupports {
6198
 public:
6199
  NS_DECL_ISUPPORTS
6200
6201
  explicit FullscreenTransitionWindow(GtkWidget* aWidget);
6202
6203
  GtkWidget* mWindow;
6204
6205
 private:
6206
  ~FullscreenTransitionWindow();
6207
};
6208
6209
NS_IMPL_ISUPPORTS0(FullscreenTransitionWindow)
6210
6211
FullscreenTransitionWindow::FullscreenTransitionWindow(GtkWidget* aWidget) {
6212
  mWindow = gtk_window_new(GTK_WINDOW_POPUP);
6213
  GtkWindow* gtkWin = GTK_WINDOW(mWindow);
6214
6215
  gtk_window_set_type_hint(gtkWin, GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
6216
  gtk_window_set_transient_for(gtkWin, GTK_WINDOW(aWidget));
6217
  gtk_window_set_decorated(gtkWin, false);
6218
6219
  GdkWindow* gdkWin = gtk_widget_get_window(aWidget);
6220
  GdkScreen* screen = gtk_widget_get_screen(aWidget);
6221
  gint monitorNum = gdk_screen_get_monitor_at_window(screen, gdkWin);
6222
  GdkRectangle monitorRect;
6223
  gdk_screen_get_monitor_geometry(screen, monitorNum, &monitorRect);
6224
  gtk_window_set_screen(gtkWin, screen);
6225
  gtk_window_move(gtkWin, monitorRect.x, monitorRect.y);
6226
  MOZ_ASSERT(monitorRect.width > 0 && monitorRect.height > 0,
6227
             "Can't resize window smaller than 1x1.");
6228
  gtk_window_resize(gtkWin, monitorRect.width, monitorRect.height);
6229
6230
  GdkColor bgColor;
6231
  bgColor.red = bgColor.green = bgColor.blue = 0;
6232
  gtk_widget_modify_bg(mWindow, GTK_STATE_NORMAL, &bgColor);
6233
6234
  gtk_window_set_opacity(gtkWin, 0.0);
6235
  gtk_widget_show(mWindow);
6236
}
6237
6238
FullscreenTransitionWindow::~FullscreenTransitionWindow() {
6239
  gtk_widget_destroy(mWindow);
6240
}
6241
6242
class FullscreenTransitionData {
6243
 public:
6244
  FullscreenTransitionData(nsIWidget::FullscreenTransitionStage aStage,
6245
                           uint16_t aDuration, nsIRunnable* aCallback,
6246
                           FullscreenTransitionWindow* aWindow)
6247
      : mStage(aStage),
6248
        mStartTime(TimeStamp::Now()),
6249
        mDuration(TimeDuration::FromMilliseconds(aDuration)),
6250
        mCallback(aCallback),
6251
        mWindow(aWindow) {}
6252
6253
  static const guint sInterval = 1000 / 30;  // 30fps
6254
  static gboolean TimeoutCallback(gpointer aData);
6255
6256
 private:
6257
  nsIWidget::FullscreenTransitionStage mStage;
6258
  TimeStamp mStartTime;
6259
  TimeDuration mDuration;
6260
  nsCOMPtr<nsIRunnable> mCallback;
6261
  RefPtr<FullscreenTransitionWindow> mWindow;
6262
};
6263
6264
/* static */
6265
gboolean FullscreenTransitionData::TimeoutCallback(gpointer aData) {
6266
  bool finishing = false;
6267
  auto data = static_cast<FullscreenTransitionData*>(aData);
6268
  gdouble opacity = (TimeStamp::Now() - data->mStartTime) / data->mDuration;
6269
  if (opacity >= 1.0) {
6270
    opacity = 1.0;
6271
    finishing = true;
6272
  }
6273
  if (data->mStage == nsIWidget::eAfterFullscreenToggle) {
6274
    opacity = 1.0 - opacity;
6275
  }
6276
  gtk_window_set_opacity(GTK_WINDOW(data->mWindow->mWindow), opacity);
6277
6278
  if (!finishing) {
6279
    return TRUE;
6280
  }
6281
  NS_DispatchToMainThread(data->mCallback.forget());
6282
  delete data;
6283
  return FALSE;
6284
}
6285
6286
/* virtual */
6287
bool nsWindow::PrepareForFullscreenTransition(nsISupports** aData) {
6288
  if (!mCompositedScreen) {
6289
    return false;
6290
  }
6291
  *aData = do_AddRef(new FullscreenTransitionWindow(mShell)).take();
6292
  return true;
6293
}
6294
6295
/* virtual */
6296
void nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage,
6297
                                           uint16_t aDuration,
6298
                                           nsISupports* aData,
6299
                                           nsIRunnable* aCallback) {
6300
  auto data = static_cast<FullscreenTransitionWindow*>(aData);
6301
  // This will be released at the end of the last timeout callback for it.
6302
  auto transitionData =
6303
      new FullscreenTransitionData(aStage, aDuration, aCallback, data);
6304
  g_timeout_add_full(G_PRIORITY_HIGH, FullscreenTransitionData::sInterval,
6305
                     FullscreenTransitionData::TimeoutCallback, transitionData,
6306
                     nullptr);
6307
}
6308
6309
already_AddRefed<nsIScreen> nsWindow::GetWidgetScreen() {
6310
  nsCOMPtr<nsIScreenManager> screenManager;
6311
  screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
6312
  if (!screenManager) {
6313
    return nullptr;
6314
  }
6315
6316
  // GetScreenBounds() is slow for the GTK port so we override and use
6317
  // mBounds directly.
6318
  LayoutDeviceIntRect bounds = mBounds;
6319
  if (!mIsTopLevel) {
6320
    bounds.MoveTo(WidgetToScreenOffset());
6321
  }
6322
6323
  DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale());
6324
  nsCOMPtr<nsIScreen> screen;
6325
  screenManager->ScreenForRect(deskBounds.x, deskBounds.y, deskBounds.width,
6326
                               deskBounds.height, getter_AddRefs(screen));
6327
  return screen.forget();
6328
}
6329
6330
RefPtr<VsyncSource> nsWindow::GetVsyncSource() {
6331
#ifdef MOZ_WAYLAND
6332
  if (mWaylandVsyncSource) {
6333
    return mWaylandVsyncSource;
6334
  }
6335
#endif
6336
6337
  return nullptr;
6338
}
6339
6340
static bool IsFullscreenSupported(GtkWidget* aShell) {
6341
#ifdef MOZ_X11
6342
  GdkScreen* screen = gtk_widget_get_screen(aShell);
6343
  GdkAtom atom = gdk_atom_intern("_NET_WM_STATE_FULLSCREEN", FALSE);
6344
  if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) {
6345
    return false;
6346
  }
6347
#endif
6348
  return true;
6349
}
6350
6351
nsresult nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) {
6352
  LOG(("nsWindow::MakeFullScreen [%p] aFullScreen %d\n", (void*)this,
6353
       aFullScreen));
6354
6355
  if (mIsX11Display && !IsFullscreenSupported(mShell)) {
6356
    return NS_ERROR_NOT_AVAILABLE;
6357
  }
6358
6359
  bool wasFullscreen = mSizeState == nsSizeMode_Fullscreen;
6360
  if (aFullScreen != wasFullscreen && mWidgetListener) {
6361
    mWidgetListener->FullscreenWillChange(aFullScreen);
6362
  }
6363
6364
  if (aFullScreen) {
6365
    if (mSizeMode != nsSizeMode_Fullscreen) mLastSizeMode = mSizeMode;
6366
6367
    mSizeMode = nsSizeMode_Fullscreen;
6368
6369
    if (mIsPIPWindow) {
6370
      gtk_window_set_type_hint(GTK_WINDOW(mShell), GDK_WINDOW_TYPE_HINT_NORMAL);
6371
      if (gUseAspectRatio) {
6372
        mAspectRatioSaved = mAspectRatio;
6373
        mAspectRatio = 0.0f;
6374
        ApplySizeConstraints();
6375
      }
6376
    }
6377
6378
    gtk_window_fullscreen(GTK_WINDOW(mShell));
6379
  } else {
6380
    mSizeMode = mLastSizeMode;
6381
    gtk_window_unfullscreen(GTK_WINDOW(mShell));
6382
6383
    if (mIsPIPWindow) {
6384
      gtk_window_set_type_hint(GTK_WINDOW(mShell),
6385
                               GDK_WINDOW_TYPE_HINT_UTILITY);
6386
      if (gUseAspectRatio) {
6387
        mAspectRatio = mAspectRatioSaved;
6388
        // ApplySizeConstraints();
6389
      }
6390
    }
6391
  }
6392
6393
  NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen,
6394
               "mLastSizeMode should never be fullscreen");
6395
  return NS_OK;
6396
}
6397
6398
void nsWindow::SetWindowDecoration(nsBorderStyle aStyle) {
6399
  LOG(("nsWindow::SetWindowDecoration() [%p] Border style %x\n", (void*)this,
6400
       aStyle));
6401
6402
  if (!mShell) {
6403
    // Pass the request to the toplevel window
6404
    GtkWidget* topWidget = GetToplevelWidget();
6405
    if (!topWidget) return;
6406
6407
    nsWindow* topWindow = get_window_for_gtk_widget(topWidget);
6408
    if (!topWindow) return;
6409
6410
    topWindow->SetWindowDecoration(aStyle);
6411
    return;
6412
  }
6413
6414
  // We can't use mGdkWindow directly here as it can be
6415
  // derived from mContainer which is not a top-level GdkWindow.
6416
  GdkWindow* window = gtk_widget_get_window(mShell);
6417
6418
  // Sawfish, metacity, and presumably other window managers get
6419
  // confused if we change the window decorations while the window
6420
  // is visible.
6421
  bool wasVisible = false;
6422
  if (gdk_window_is_visible(window)) {
6423
    gdk_window_hide(window);
6424
    wasVisible = true;
6425
  }
6426
6427
  gint wmd = ConvertBorderStyles(aStyle);
6428
  if (wmd != -1) gdk_window_set_decorations(window, (GdkWMDecoration)wmd);
6429
6430
  if (wasVisible) gdk_window_show(window);
6431
6432
    // For some window managers, adding or removing window decorations
6433
    // requires unmapping and remapping our toplevel window.  Go ahead
6434
    // and flush the queue here so that we don't end up with a BadWindow
6435
    // error later when this happens (when the persistence timer fires
6436
    // and GetWindowPos is called)
6437
#ifdef MOZ_X11
6438
  if (mIsX11Display) {
6439
    XSync(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), X11False);
6440
  } else
6441
#endif /* MOZ_X11 */
6442
  {
6443
    gdk_flush();
6444
  }
6445
}
6446
6447
void nsWindow::HideWindowChrome(bool aShouldHide) {
6448
  SetWindowDecoration(aShouldHide ? eBorderStyle_none : mBorderStyle);
6449
}
6450
6451
bool nsWindow::CheckForRollup(gdouble aMouseX, gdouble aMouseY, bool aIsWheel,
6452
                              bool aAlwaysRollup) {
6453
  nsIRollupListener* rollupListener = GetActiveRollupListener();
6454
  nsCOMPtr<nsIWidget> rollupWidget;
6455
  if (rollupListener) {
6456
    rollupWidget = rollupListener->GetRollupWidget();
6457
  }
6458
  if (!rollupWidget) {
6459
    nsBaseWidget::gRollupListener = nullptr;
6460
    return false;
6461
  }
6462
6463
  bool retVal = false;
6464
  auto* currentPopup =
6465
      (GdkWindow*)rollupWidget->GetNativeData(NS_NATIVE_WINDOW);
6466
  if (aAlwaysRollup || !is_mouse_in_window(currentPopup, aMouseX, aMouseY)) {
6467
    bool rollup = true;
6468
    if (aIsWheel) {
6469
      rollup = rollupListener->ShouldRollupOnMouseWheelEvent();
6470
      retVal = rollupListener->ShouldConsumeOnMouseWheelEvent();
6471
    }
6472
    // if we're dealing with menus, we probably have submenus and
6473
    // we don't want to rollup if the click is in a parent menu of
6474
    // the current submenu
6475
    uint32_t popupsToRollup = UINT32_MAX;
6476
    if (!aAlwaysRollup) {
6477
      AutoTArray<nsIWidget*, 5> widgetChain;
6478
      uint32_t sameTypeCount =
6479
          rollupListener->GetSubmenuWidgetChain(&widgetChain);
6480
      for (unsigned long i = 0; i < widgetChain.Length(); ++i) {
6481
        nsIWidget* widget = widgetChain[i];
6482
        auto* currWindow = (GdkWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
6483
        if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) {
6484
          // don't roll up if the mouse event occurred within a
6485
          // menu of the same type. If the mouse event occurred
6486
          // in a menu higher than that, roll up, but pass the
6487
          // number of popups to Rollup so that only those of the
6488
          // same type close up.
6489
          if (i < sameTypeCount) {
6490
            rollup = false;
6491
          } else {
6492
            popupsToRollup = sameTypeCount;
6493
          }
6494
          break;
6495
        }
6496
      }  // foreach parent menu widget
6497
    }    // if rollup listener knows about menus
6498
6499
    // if we've determined that we should still rollup, do it.
6500
    bool usePoint = !aIsWheel && !aAlwaysRollup;
6501
    IntPoint point;
6502
    if (usePoint) {
6503
      LayoutDeviceIntPoint p = GdkEventCoordsToDevicePixels(aMouseX, aMouseY);
6504
      point = p.ToUnknownPoint();
6505
    }
6506
    if (rollup &&
6507
        rollupListener->Rollup(popupsToRollup, true,
6508
                               usePoint ? &point : nullptr, nullptr)) {
6509
      retVal = true;
6510
    }
6511
  }
6512
  return retVal;
6513
}
6514
6515
/* static */
6516
bool nsWindow::DragInProgress(void) {
6517
  nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
6518
  if (!dragService) {
6519
    return false;
6520
  }
6521
6522
  nsCOMPtr<nsIDragSession> currentDragSession;
6523
  dragService->GetCurrentSession(getter_AddRefs(currentDragSession));
6524
6525
  return currentDragSession != nullptr;
6526
}
6527
6528
// This is an ugly workaround for
6529
// https://bugzilla.mozilla.org/show_bug.cgi?id=1622107
6530
// We try to detect when Wayland compositor / gtk fails to deliver
6531
// info about finished D&D operations and cancel it on our own.
6532
MOZ_CAN_RUN_SCRIPT static void WaylandDragWorkaround(GdkEventButton* aEvent) {
6533
  static int buttonPressCountWithDrag = 0;
6534
6535
  // We track only left button state as Firefox performs D&D on left
6536
  // button only.
6537
  if (aEvent->button != 1 || aEvent->type != GDK_BUTTON_PRESS) {
6538
    return;
6539
  }
6540
6541
  nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
6542
  if (!dragService) {
6543
    return;
6544
  }
6545
  nsCOMPtr<nsIDragSession> currentDragSession;
6546
  dragService->GetCurrentSession(getter_AddRefs(currentDragSession));
6547
6548
  if (currentDragSession != nullptr) {
6549
    buttonPressCountWithDrag++;
6550
    if (buttonPressCountWithDrag > 1) {
6551
      NS_WARNING(
6552
          "Quit unfinished Wayland Drag and Drop operation. Buggy Wayland "
6553
          "compositor?");
6554
      buttonPressCountWithDrag = 0;
6555
      dragService->EndDragSession(false, 0);
6556
    }
6557
  }
6558
}
6559
6560
static bool is_mouse_in_window(GdkWindow* aWindow, gdouble aMouseX,
6561
                               gdouble aMouseY) {
6562
  gint x = 0;
6563
  gint y = 0;
6564
  gint w, h;
6565
6566
  gint offsetX = 0;
6567
  gint offsetY = 0;
6568
6569
  GdkWindow* window = aWindow;
6570
6571
  while (window) {
6572
    gint tmpX = 0;
6573
    gint tmpY = 0;
6574
6575
    gdk_window_get_position(window, &tmpX, &tmpY);
6576
    GtkWidget* widget = get_gtk_widget_for_gdk_window(window);
6577
6578
    // if this is a window, compute x and y given its origin and our
6579
    // offset
6580
    if (GTK_IS_WINDOW(widget)) {
6581
      x = tmpX + offsetX;
6582
      y = tmpY + offsetY;
6583
      break;
6584
    }
6585
6586
    offsetX += tmpX;
6587
    offsetY += tmpY;
6588
    window = gdk_window_get_parent(window);
6589
  }
6590
6591
  w = gdk_window_get_width(aWindow);
6592
  h = gdk_window_get_height(aWindow);
6593
6594
  if (aMouseX > x && aMouseX < x + w && aMouseY > y && aMouseY < y + h)
6595
    return true;
6596
6597
  return false;
6598
}
6599
6600
static nsWindow* get_window_for_gtk_widget(GtkWidget* widget) {
6601
  gpointer user_data = g_object_get_data(G_OBJECT(widget), "nsWindow");
6602
6603
  return static_cast<nsWindow*>(user_data);
6604
}
6605
6606
static nsWindow* get_window_for_gdk_window(GdkWindow* window) {
6607
  gpointer user_data = g_object_get_data(G_OBJECT(window), "nsWindow");
6608
6609
  return static_cast<nsWindow*>(user_data);
6610
}
6611
6612
static GtkWidget* get_gtk_widget_for_gdk_window(GdkWindow* window) {
6613
  gpointer user_data = nullptr;
6614
  gdk_window_get_user_data(window, &user_data);
6615
6616
  return GTK_WIDGET(user_data);
6617
}
6618
6619
static GdkCursor* get_gtk_cursor(nsCursor aCursor) {
6620
  GdkCursor* gdkcursor = nullptr;
6621
  uint8_t newType = 0xff;
6622
6623
  if ((gdkcursor = gCursorCache[aCursor])) {
6624
    return gdkcursor;
6625
  }
6626
6627
  GdkDisplay* defaultDisplay = gdk_display_get_default();
6628
6629
  // The strategy here is to use standard GDK cursors, and, if not available,
6630
  // load by standard name with gdk_cursor_new_from_name.
6631
  // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/
6632
  switch (aCursor) {
6633
    case eCursor_standard:
6634
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR);
6635
      break;
6636
    case eCursor_wait:
6637
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_WATCH);
6638
      break;
6639
    case eCursor_select:
6640
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_XTERM);
6641
      break;
6642
    case eCursor_hyperlink:
6643
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_HAND2);
6644
      break;
6645
    case eCursor_n_resize:
6646
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_SIDE);
6647
      break;
6648
    case eCursor_s_resize:
6649
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_SIDE);
6650
      break;
6651
    case eCursor_w_resize:
6652
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_SIDE);
6653
      break;
6654
    case eCursor_e_resize:
6655
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_RIGHT_SIDE);
6656
      break;
6657
    case eCursor_nw_resize:
6658
      gdkcursor =
6659
          gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_LEFT_CORNER);
6660
      break;
6661
    case eCursor_se_resize:
6662
      gdkcursor =
6663
          gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_RIGHT_CORNER);
6664
      break;
6665
    case eCursor_ne_resize:
6666
      gdkcursor =
6667
          gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_RIGHT_CORNER);
6668
      break;
6669
    case eCursor_sw_resize:
6670
      gdkcursor =
6671
          gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_LEFT_CORNER);
6672
      break;
6673
    case eCursor_crosshair:
6674
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_CROSSHAIR);
6675
      break;
6676
    case eCursor_move:
6677
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR);
6678
      break;
6679
    case eCursor_help:
6680
      gdkcursor =
6681
          gdk_cursor_new_for_display(defaultDisplay, GDK_QUESTION_ARROW);
6682
      break;
6683
    case eCursor_copy:  // CSS3
6684
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy");
6685
      if (!gdkcursor) newType = MOZ_CURSOR_COPY;
6686
      break;
6687
    case eCursor_alias:
6688
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias");
6689
      if (!gdkcursor) newType = MOZ_CURSOR_ALIAS;
6690
      break;
6691
    case eCursor_context_menu:
6692
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu");
6693
      if (!gdkcursor) newType = MOZ_CURSOR_CONTEXT_MENU;
6694
      break;
6695
    case eCursor_cell:
6696
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_PLUS);
6697
      break;
6698
    // Those two aren’t standardized. Trying both KDE’s and GNOME’s names
6699
    case eCursor_grab:
6700
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand");
6701
      if (!gdkcursor) newType = MOZ_CURSOR_HAND_GRAB;
6702
      break;
6703
    case eCursor_grabbing:
6704
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand");
6705
      if (!gdkcursor)
6706
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing");
6707
      if (!gdkcursor) newType = MOZ_CURSOR_HAND_GRABBING;
6708
      break;
6709
    case eCursor_spinning:
6710
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress");
6711
      if (!gdkcursor) newType = MOZ_CURSOR_SPINNING;
6712
      break;
6713
    case eCursor_zoom_in:
6714
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-in");
6715
      if (!gdkcursor) newType = MOZ_CURSOR_ZOOM_IN;
6716
      break;
6717
    case eCursor_zoom_out:
6718
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-out");
6719
      if (!gdkcursor) newType = MOZ_CURSOR_ZOOM_OUT;
6720
      break;
6721
    case eCursor_not_allowed:
6722
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed");
6723
      if (!gdkcursor)  // nonstandard, yet common
6724
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle");
6725
      if (!gdkcursor) newType = MOZ_CURSOR_NOT_ALLOWED;
6726
      break;
6727
    case eCursor_no_drop:
6728
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop");
6729
      if (!gdkcursor)  // this nonstandard sequence makes it work on KDE and
6730
                       // GNOME
6731
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden");
6732
      if (!gdkcursor)
6733
        gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle");
6734
      if (!gdkcursor) newType = MOZ_CURSOR_NOT_ALLOWED;
6735
      break;
6736
    case eCursor_vertical_text:
6737
      newType = MOZ_CURSOR_VERTICAL_TEXT;
6738
      break;
6739
    case eCursor_all_scroll:
6740
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR);
6741
      break;
6742
    case eCursor_nesw_resize:
6743
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag");
6744
      if (!gdkcursor) newType = MOZ_CURSOR_NESW_RESIZE;
6745
      break;
6746
    case eCursor_nwse_resize:
6747
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag");
6748
      if (!gdkcursor) newType = MOZ_CURSOR_NWSE_RESIZE;
6749
      break;
6750
    case eCursor_ns_resize:
6751
      gdkcursor =
6752
          gdk_cursor_new_for_display(defaultDisplay, GDK_SB_V_DOUBLE_ARROW);
6753
      break;
6754
    case eCursor_ew_resize:
6755
      gdkcursor =
6756
          gdk_cursor_new_for_display(defaultDisplay, GDK_SB_H_DOUBLE_ARROW);
6757
      break;
6758
    // Here, two better fitting cursors exist in some cursor themes. Try those
6759
    // first
6760
    case eCursor_row_resize:
6761
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v");
6762
      if (!gdkcursor)
6763
        gdkcursor =
6764
            gdk_cursor_new_for_display(defaultDisplay, GDK_SB_V_DOUBLE_ARROW);
6765
      break;
6766
    case eCursor_col_resize:
6767
      gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h");
6768
      if (!gdkcursor)
6769
        gdkcursor =
6770
            gdk_cursor_new_for_display(defaultDisplay, GDK_SB_H_DOUBLE_ARROW);
6771
      break;
6772
    case eCursor_none:
6773
      newType = MOZ_CURSOR_NONE;
6774
      break;
6775
    default:
6776
      NS_ASSERTION(aCursor, "Invalid cursor type");
6777
      gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR);
6778
      break;
6779
  }
6780
6781
  // If by now we don't have a xcursor, this means we have to make a custom
6782
  // one. First, we try creating a named cursor based on the hash of our
6783
  // custom bitmap, as libXcursor has some magic to convert bitmapped cursors
6784
  // to themed cursors
6785
  if (newType != 0xFF && GtkCursors[newType].hash) {
6786
    gdkcursor =
6787
        gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash);
6788
  }
6789
6790
  // If we still don't have a xcursor, we now really create a bitmap cursor
6791
  if (newType != 0xff && !gdkcursor) {
6792
    GdkPixbuf* cursor_pixbuf =
6793
        gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32);
6794
    if (!cursor_pixbuf) return nullptr;
6795
6796
    guchar* data = gdk_pixbuf_get_pixels(cursor_pixbuf);
6797
6798
    // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and
6799
    // mask GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for
6800
    // each pixel) so it's 128 byte array (4 bytes for are one bitmap row and
6801
    // there are 32 rows here).
6802
    const unsigned char* bits = GtkCursors[newType].bits;
6803
    const unsigned char* mask_bits = GtkCursors[newType].mask_bits;
6804
6805
    for (int i = 0; i < 128; i++) {
6806
      char bit = *bits++;
6807
      char mask = *mask_bits++;
6808
      for (int j = 0; j < 8; j++) {
6809
        unsigned char pix = ~(((bit >> j) & 0x01) * 0xff);
6810
        *data++ = pix;
6811
        *data++ = pix;
6812
        *data++ = pix;
6813
        *data++ = (((mask >> j) & 0x01) * 0xff);
6814
      }
6815
    }
6816
6817
    gdkcursor = gdk_cursor_new_from_pixbuf(
6818
        gdk_display_get_default(), cursor_pixbuf, GtkCursors[newType].hot_x,
6819
        GtkCursors[newType].hot_y);
6820
6821
    g_object_unref(cursor_pixbuf);
6822
  }
6823
6824
  gCursorCache[aCursor] = gdkcursor;
6825
6826
  return gdkcursor;
6827
}
6828
6829
// gtk callbacks
6830
6831
void draw_window_of_widget(GtkWidget* widget, GdkWindow* aWindow, cairo_t* cr) {
6832
  if (gtk_cairo_should_draw_window(cr, aWindow)) {
6833
    RefPtr<nsWindow> window = get_window_for_gdk_window(aWindow);
6834
    if (!window) {
6835
      NS_WARNING("Cannot get nsWindow from GtkWidget");
6836
    } else {
6837
      cairo_save(cr);
6838
      gtk_cairo_transform_to_window(cr, widget, aWindow);
6839
      // TODO - window->OnExposeEvent() can destroy this or other windows,
6840
      // do we need to handle it somehow?
6841
      window->OnExposeEvent(cr);
6842
      cairo_restore(cr);
6843
    }
6844
  }
6845
6846
  GList* children = gdk_window_get_children(aWindow);
6847
  GList* child = children;
6848
  while (child) {
6849
    GdkWindow* window = GDK_WINDOW(child->data);
6850
    gpointer windowWidget;
6851
    gdk_window_get_user_data(window, &windowWidget);
6852
    if (windowWidget == widget) {
6853
      draw_window_of_widget(widget, window, cr);
6854
    }
6855
    child = g_list_next(child);
6856
  }
6857
  g_list_free(children);
6858
}
6859
6860
/* static */
6861
gboolean expose_event_cb(GtkWidget* widget, cairo_t* cr) {
6862
  draw_window_of_widget(widget, gtk_widget_get_window(widget), cr);
6863
6864
  // A strong reference is already held during "draw" signal emission,
6865
  // but GTK+ 3.4 wants the object to live a little longer than that
6866
  // (bug 1225970).
6867
  g_object_ref(widget);
6868
  g_idle_add(
6869
      [](gpointer data) -> gboolean {
6870
        g_object_unref(data);
6871
        return G_SOURCE_REMOVE;
6872
      },
6873
      widget);
6874
6875
  return FALSE;
6876
}
6877
6878
static gboolean configure_event_cb(GtkWidget* widget,
6879
                                   GdkEventConfigure* event) {
6880
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
6881
  if (!window) {
6882
    return FALSE;
6883
  }
6884
6885
  return window->OnConfigureEvent(widget, event);
6886
}
6887
6888
static void container_unrealize_cb(GtkWidget* widget) {
6889
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
6890
  if (!window) {
6891
    return;
6892
  }
6893
6894
  window->OnContainerUnrealize();
6895
}
6896
6897
static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation) {
6898
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
6899
  if (!window) {
6900
    return;
6901
  }
6902
6903
  window->OnSizeAllocate(allocation);
6904
}
6905
6906
static void toplevel_window_size_allocate_cb(GtkWidget* widget,
6907
                                             GtkAllocation* allocation) {
6908
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
6909
  if (!window) {
6910
    return;
6911
  }
6912
6913
  window->UpdateTopLevelOpaqueRegion();
6914
}
6915
6916
static gboolean delete_event_cb(GtkWidget* widget, GdkEventAny* event) {
6917
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
6918
  if (!window) {
6919
    return FALSE;
6920
  }
6921
6922
  window->OnDeleteEvent();
6923
6924
  return TRUE;
6925
}
6926
6927
static gboolean enter_notify_event_cb(GtkWidget* widget,
6928
                                      GdkEventCrossing* event) {
6929
  RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
6930
  if (!window) {
6931
    return TRUE;
6932
  }
6933
6934
  window->OnEnterNotifyEvent(event);
6935
6936
  return TRUE;
6937
}
6938
6939
static gboolean leave_notify_event_cb(GtkWidget* widget,
6940
                                      GdkEventCrossing* event) {
6941
  if (is_parent_grab_leave(event)) {
6942
    return TRUE;
6943
  }
6944
6945
  // bug 369599: Suppress LeaveNotify events caused by pointer grabs to
6946
  // avoid generating spurious mouse exit events.
6947
  auto x = gint(event->x_root);
6948
  auto y = gint(event->y_root);
6949
  GdkDisplay* display = gtk_widget_get_display(widget);
6950
  GdkWindow* winAtPt = gdk_display_get_window_at_pointer(display, &x, &y);
6951
  if (winAtPt == event->window) {
6952
    return TRUE;
6953
  }
6954
6955
  RefPtr<nsWindow> window = get_window_for_gdk_window(event->window);
6956
  if (!window) return TRUE;
6957
6958
  window->OnLeaveNotifyEvent(event);
6959
6960
  return TRUE;
6961
}
6962
6963
static nsWindow* GetFirstNSWindowForGDKWindow(GdkWindow* aGdkWindow) {
6964
  nsWindow* window;
6965
  while (!(window = get_window_for_gdk_window(aGdkWindow))) {
6966
    // The event has bubbled to the moz_container widget as passed into each
6967
    // caller's *widget parameter, but its corresponding nsWindow is an ancestor
6968
    // of the window that we need.  Instead, look at event->window and find the
6969
    // first ancestor nsWindow of it because event->window may be in a plugin.
6970
    aGdkWindow = gdk_window_get_parent(aGdkWindow);
6971
    if (!aGdkWindow) {
6972
      window = nullptr;
6973
      break;
6974
    }
6975
  }
6976
  return window;
6977
}
6978
6979
static gboolean motion_notify_event_cb(GtkWidget* widget,
6980
                                       GdkEventMotion* event) {
6981
  UpdateLastInputEventTime(event);
6982
6983
  nsWindow* window = GetFirstNSWindowForGDKWindow(event->window);
6984
  if (!window) return FALSE;
6985
6986
  window->OnMotionNotifyEvent(event);
6987
6988
  return TRUE;
6989
}
6990
6991
static gboolean button_press_event_cb(GtkWidget* widget,
6992
                                      GdkEventButton* event) {
6993
  UpdateLastInputEventTime(event);
6994
6995
  nsWindow* window = GetFirstNSWindowForGDKWindow(event->window);
6996
  if (!window) return FALSE;
6997
6998
  window->OnButtonPressEvent(event);
6999
7000
  if (gfxPlatformGtk::GetPlatform()->IsWaylandDisplay()) {
7001
    WaylandDragWorkaround(event);
7002
  }
7003
7004
  return TRUE;
7005
}
7006
7007
static gboolean button_release_event_cb(GtkWidget* widget,
7008
                                        GdkEventButton* event) {
7009
  UpdateLastInputEventTime(event);
7010
7011
  nsWindow* window = GetFirstNSWindowForGDKWindow(event->window);
7012
  if (!window) return FALSE;
7013
7014
  window->OnButtonReleaseEvent(event);
7015
7016
  return TRUE;
7017
}
7018
7019
static gboolean focus_in_event_cb(GtkWidget* widget, GdkEventFocus* event) {
7020
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
7021
  if (!window) return FALSE;
7022
7023
  window->OnContainerFocusInEvent(event);
7024
7025
  return FALSE;
7026
}
7027
7028
static gboolean focus_out_event_cb(GtkWidget* widget, GdkEventFocus* event) {
7029
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
7030
  if (!window) return FALSE;
7031
7032
  window->OnContainerFocusOutEvent(event);
7033
7034
  return FALSE;
7035
}
7036
7037
#ifdef MOZ_X11
7038
// For long-lived popup windows that don't really take focus themselves but
7039
// may have elements that accept keyboard input when the parent window is
7040
// active, focus is handled specially.  These windows include noautohide
7041
// panels.  (This special handling is not necessary for temporary popups where
7042
// the keyboard is grabbed.)
7043
//
7044
// Mousing over or clicking on these windows should not cause them to steal
7045
// focus from their parent windows, so, the input field of WM_HINTS is set to
7046
// False to request that the window manager not set the input focus to this
7047
// window.  http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
7048
//
7049
// However, these windows can still receive WM_TAKE_FOCUS messages from the
7050
// window manager, so they can still detect when the user has indicated that
7051
// they wish to direct keyboard input at these windows.  When the window
7052
// manager offers focus to these windows (after a mouse over or click, for
7053
// example), a request to make the parent window active is issued.  When the
7054
// parent window becomes active, keyboard events will be received.
7055
7056
static GdkFilterReturn popup_take_focus_filter(GdkXEvent* gdk_xevent,
7057
                                               GdkEvent* event, gpointer data) {
7058
  auto* xevent = static_cast<XEvent*>(gdk_xevent);
7059
  if (xevent->type != ClientMessage) return GDK_FILTER_CONTINUE;
7060
7061
  XClientMessageEvent& xclient = xevent->xclient;
7062
  if (xclient.message_type != gdk_x11_get_xatom_by_name("WM_PROTOCOLS"))
7063
    return GDK_FILTER_CONTINUE;
7064
7065
  Atom atom = xclient.data.l[0];
7066
  if (atom != gdk_x11_get_xatom_by_name("WM_TAKE_FOCUS"))
7067
    return GDK_FILTER_CONTINUE;
7068
7069
  guint32 timestamp = xclient.data.l[1];
7070
7071
  GtkWidget* widget = get_gtk_widget_for_gdk_window(event->any.window);
7072
  if (!widget) return GDK_FILTER_CONTINUE;
7073
7074
  GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(widget));
7075
  if (!parent) return GDK_FILTER_CONTINUE;
7076
7077
  if (gtk_window_is_active(parent))
7078
    return GDK_FILTER_REMOVE;  // leave input focus on the parent
7079
7080
  GdkWindow* parent_window = gtk_widget_get_window(GTK_WIDGET(parent));
7081
  if (!parent_window) return GDK_FILTER_CONTINUE;
7082
7083
  // In case the parent has not been deconified.
7084
  gdk_window_show_unraised(parent_window);
7085
7086
  // Request focus on the parent window.
7087
  // Use gdk_window_focus rather than gtk_window_present to avoid
7088
  // raising the parent window.
7089
  gdk_window_focus(parent_window, timestamp);
7090
  return GDK_FILTER_REMOVE;
7091
}
7092
#endif /* MOZ_X11 */
7093
7094
static gboolean key_press_event_cb(GtkWidget* widget, GdkEventKey* event) {
7095
  LOG(("key_press_event_cb\n"));
7096
7097
  UpdateLastInputEventTime(event);
7098
7099
  // find the window with focus and dispatch this event to that widget
7100
  nsWindow* window = get_window_for_gtk_widget(widget);
7101
  if (!window) return FALSE;
7102
7103
  RefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
7104
7105
#ifdef MOZ_X11
7106
  // Keyboard repeat can cause key press events to queue up when there are
7107
  // slow event handlers (bug 301029).  Throttle these events by removing
7108
  // consecutive pending duplicate KeyPress events to the same window.
7109
  // We use the event time of the last one.
7110
  // Note: GDK calls XkbSetDetectableAutorepeat so that KeyRelease events
7111
  // are generated only when the key is physically released.
7112
#  define NS_GDKEVENT_MATCH_MASK 0x1FFF  // GDK_SHIFT_MASK .. GDK_BUTTON5_MASK
7113
  // Our headers undefine X11 KeyPress - let's redefine it here.
7114
#  ifndef KeyPress
7115
#    define KeyPress 2
7116
#  endif
7117
  GdkDisplay* gdkDisplay = gtk_widget_get_display(widget);
7118
  if (GdkIsX11Display(gdkDisplay)) {
7119
    Display* dpy = GDK_DISPLAY_XDISPLAY(gdkDisplay);
7120
    while (XPending(dpy)) {
7121
      XEvent next_event;
7122
      XPeekEvent(dpy, &next_event);
7123
      GdkWindow* nextGdkWindow =
7124
          gdk_x11_window_lookup_for_display(gdkDisplay, next_event.xany.window);
7125
      if (nextGdkWindow != event->window || next_event.type != KeyPress ||
7126
          next_event.xkey.keycode != event->hardware_keycode ||
7127
          next_event.xkey.state != (event->state & NS_GDKEVENT_MATCH_MASK)) {
7128
        break;
7129
      }
7130
      XNextEvent(dpy, &next_event);
7131
      event->time = next_event.xkey.time;
7132
    }
7133
  }
7134
#endif
7135
7136
  return focusWindow->OnKeyPressEvent(event);
7137
}
7138
7139
static gboolean key_release_event_cb(GtkWidget* widget, GdkEventKey* event) {
7140
  LOG(("key_release_event_cb\n"));
7141
7142
  UpdateLastInputEventTime(event);
7143
7144
  // find the window with focus and dispatch this event to that widget
7145
  nsWindow* window = get_window_for_gtk_widget(widget);
7146
  if (!window) return FALSE;
7147
7148
  RefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
7149
7150
  return focusWindow->OnKeyReleaseEvent(event);
7151
}
7152
7153
static gboolean property_notify_event_cb(GtkWidget* aWidget,
7154
                                         GdkEventProperty* aEvent) {
7155
  RefPtr<nsWindow> window = get_window_for_gdk_window(aEvent->window);
7156
  if (!window) return FALSE;
7157
7158
  return window->OnPropertyNotifyEvent(aWidget, aEvent);
7159
}
7160
7161
static gboolean scroll_event_cb(GtkWidget* widget, GdkEventScroll* event) {
7162
  nsWindow* window = GetFirstNSWindowForGDKWindow(event->window);
7163
  if (!window) return FALSE;
7164
7165
  window->OnScrollEvent(event);
7166
7167
  return TRUE;
7168
}
7169
7170
static void hierarchy_changed_cb(GtkWidget* widget,
7171
                                 GtkWidget* previous_toplevel) {
7172
  GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
7173
  GdkWindowState old_window_state = GDK_WINDOW_STATE_WITHDRAWN;
7174
  GdkEventWindowState event;
7175
7176
  event.new_window_state = GDK_WINDOW_STATE_WITHDRAWN;
7177
7178
  if (GTK_IS_WINDOW(previous_toplevel)) {
7179
    g_signal_handlers_disconnect_by_func(
7180
        previous_toplevel, FuncToGpointer(window_state_event_cb), widget);
7181
    GdkWindow* win = gtk_widget_get_window(previous_toplevel);
7182
    if (win) {
7183
      old_window_state = gdk_window_get_state(win);
7184
    }
7185
  }
7186
7187
  if (GTK_IS_WINDOW(toplevel)) {
7188
    g_signal_connect_swapped(toplevel, "window-state-event",
7189
                             G_CALLBACK(window_state_event_cb), widget);
7190
    GdkWindow* win = gtk_widget_get_window(toplevel);
7191
    if (win) {
7192
      event.new_window_state = gdk_window_get_state(win);
7193
    }
7194
  }
7195
7196
  event.changed_mask =
7197
      static_cast<GdkWindowState>(old_window_state ^ event.new_window_state);
7198
7199
  if (event.changed_mask) {
7200
    event.type = GDK_WINDOW_STATE;
7201
    event.window = nullptr;
7202
    event.send_event = TRUE;
7203
    window_state_event_cb(widget, &event);
7204
  }
7205
}
7206
7207
static gboolean window_state_event_cb(GtkWidget* widget,
7208
                                      GdkEventWindowState* event) {
7209
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
7210
  if (!window) return FALSE;
7211
7212
  window->OnWindowStateEvent(widget, event);
7213
7214
  return FALSE;
7215
}
7216
7217
static void settings_changed_cb(GtkSettings* settings, GParamSpec* pspec,
7218
                                nsWindow* data) {
7219
  if (sIgnoreChangedSettings) {
7220
    return;
7221
  }
7222
  RefPtr<nsWindow> window = data;
7223
  window->ThemeChanged();
7224
}
7225
7226
static void settings_xft_dpi_changed_cb(GtkSettings* gtk_settings,
7227
                                        GParamSpec* pspec, nsWindow* data) {
7228
  RefPtr<nsWindow> window = data;
7229
  window->OnDPIChanged();
7230
  // Even though the window size in screen pixels has not changed,
7231
  // nsViewManager stores the dimensions in app units.
7232
  // DispatchResized() updates those.
7233
  window->DispatchResized();
7234
}
7235
7236
static void check_resize_cb(GtkContainer* container, gpointer user_data) {
7237
  RefPtr<nsWindow> window = get_window_for_gtk_widget(GTK_WIDGET(container));
7238
  if (!window) {
7239
    return;
7240
  }
7241
  window->OnCheckResize();
7242
}
7243
7244
static void screen_composited_changed_cb(GdkScreen* screen,
7245
                                         gpointer user_data) {
7246
  // This callback can run before gfxPlatform::Init() in rare
7247
  // cases involving the profile manager. When this happens,
7248
  // we have no reason to reset any compositors as graphics
7249
  // hasn't been initialized yet.
7250
  if (GPUProcessManager::Get()) {
7251
    GPUProcessManager::Get()->ResetCompositors();
7252
  }
7253
}
7254
7255
static void widget_composited_changed_cb(GtkWidget* widget,
7256
                                         gpointer user_data) {
7257
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
7258
  if (!window) {
7259
    return;
7260
  }
7261
  window->OnCompositedChanged();
7262
}
7263
7264
static void scale_changed_cb(GtkWidget* widget, GParamSpec* aPSpec,
7265
                             gpointer aPointer) {
7266
  RefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
7267
  if (!window) {
7268
    return;
7269
  }
7270
7271
  GtkAllocation allocation;
7272
  gtk_widget_get_allocation(widget, &allocation);
7273
  window->OnScaleChanged(&allocation);
7274
}
7275
7276
static gboolean touch_event_cb(GtkWidget* aWidget, GdkEventTouch* aEvent) {
7277
  UpdateLastInputEventTime(aEvent);
7278
7279
  nsWindow* window = GetFirstNSWindowForGDKWindow(aEvent->window);
7280
  if (!window) {
7281
    return FALSE;
7282
  }
7283
7284
  return window->OnTouchEvent(aEvent);
7285
}
7286
7287
// This function called generic because there is no signal specific to touchpad
7288
// pinch events.
7289
static gboolean generic_event_cb(GtkWidget* widget, GdkEvent* aEvent) {
7290
  if (aEvent->type != GDK_TOUCHPAD_PINCH) {
7291
    return FALSE;
7292
  }
7293
  // Using reinterpret_cast because the touchpad_pinch field of GdkEvent is not
7294
  // available in GTK+ versions lower than v3.18
7295
  GdkEventTouchpadPinch* event =
7296
      reinterpret_cast<GdkEventTouchpadPinch*>(aEvent);
7297
7298
  nsWindow* window = GetFirstNSWindowForGDKWindow(event->window);
7299
7300
  if (!window) {
7301
    return FALSE;
7302
  }
7303
  return window->OnTouchpadPinchEvent(event);
7304
}
7305
7306
//////////////////////////////////////////////////////////////////////
7307
// These are all of our drag and drop operations
7308
7309
void nsWindow::InitDragEvent(WidgetDragEvent& aEvent) {
7310
  // set the keyboard modifiers
7311
  guint modifierState = KeymapWrapper::GetCurrentModifierState();
7312
  KeymapWrapper::InitInputEvent(aEvent, modifierState);
7313
}
7314
7315
gboolean WindowDragMotionHandler(GtkWidget* aWidget,
7316
                                 GdkDragContext* aDragContext,
7317
                                 nsWaylandDragContext* aWaylandDragContext,
7318
                                 gint aX, gint aY, guint aTime) {
7319
  RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
7320
  if (!window) {
7321
    return FALSE;
7322
  }
7323
7324
  // figure out which internal widget this drag motion actually happened on
7325
  nscoord retx = 0;
7326
  nscoord rety = 0;
7327
7328
  GdkWindow* innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget),
7329
                                                aX, aY, &retx, &rety);
7330
  RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
7331
7332
  if (!innerMostWindow) {
7333
    innerMostWindow = window;
7334
  }
7335
7336
  LOGDRAG(("WindowDragMotionHandler nsWindow %p\n", (void*)innerMostWindow));
7337
7338
  LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({retx, rety});
7339
7340
  RefPtr<nsDragService> dragService = nsDragService::GetInstance();
7341
  return dragService->ScheduleMotionEvent(innerMostWindow, aDragContext,
7342
                                          aWaylandDragContext, point, aTime);
7343
}
7344
7345
static gboolean drag_motion_event_cb(GtkWidget* aWidget,
7346
                                     GdkDragContext* aDragContext, gint aX,
7347
                                     gint aY, guint aTime, gpointer aData) {
7348
  return WindowDragMotionHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
7349
}
7350
7351
void WindowDragLeaveHandler(GtkWidget* aWidget) {
7352
  LOGDRAG(("WindowDragLeaveHandler()\n"));
7353
7354
  RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
7355
  if (!window) {
7356
    LOGDRAG(("    Failed - can't find nsWindow!\n"));
7357
    return;
7358
  }
7359
7360
  RefPtr<nsDragService> dragService = nsDragService::GetInstance();
7361
7362
  nsWindow* mostRecentDragWindow = dragService->GetMostRecentDestWindow();
7363
  if (!mostRecentDragWindow) {
7364
    // This can happen when the target will not accept a drop.  A GTK drag
7365
    // source sends the leave message to the destination before the
7366
    // drag-failed signal on the source widget, but the leave message goes
7367
    // via the X server, and so doesn't get processed at least until the
7368
    // event loop runs again.
7369
    LOGDRAG(("    Failed - GetMostRecentDestWindow()!\n"));
7370
    return;
7371
  }
7372
7373
  GtkWidget* mozContainer = mostRecentDragWindow->GetMozContainerWidget();
7374
  if (aWidget != mozContainer) {
7375
    // When the drag moves between widgets, GTK can send leave signal for
7376
    // the old widget after the motion or drop signal for the new widget.
7377
    // We'll send the leave event when the motion or drop event is run.
7378
    LOGDRAG(("    Failed - GetMozContainerWidget()!\n"));
7379
    return;
7380
  }
7381
7382
  LOGDRAG(
7383
      ("WindowDragLeaveHandler nsWindow %p\n", (void*)mostRecentDragWindow));
7384
  dragService->ScheduleLeaveEvent();
7385
}
7386
7387
static void drag_leave_event_cb(GtkWidget* aWidget,
7388
                                GdkDragContext* aDragContext, guint aTime,
7389
                                gpointer aData) {
7390
  WindowDragLeaveHandler(aWidget);
7391
}
7392
7393
gboolean WindowDragDropHandler(GtkWidget* aWidget, GdkDragContext* aDragContext,
7394
                               nsWaylandDragContext* aWaylandDragContext,
7395
                               gint aX, gint aY, guint aTime) {
7396
  RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
7397
  if (!window) return FALSE;
7398
7399
  // figure out which internal widget this drag motion actually happened on
7400
  nscoord retx = 0;
7401
  nscoord rety = 0;
7402
7403
  GdkWindow* innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget),
7404
                                                aX, aY, &retx, &rety);
7405
  RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
7406
7407
  if (!innerMostWindow) {
7408
    innerMostWindow = window;
7409
  }
7410
7411
  LOGDRAG(("WindowDragDropHandler nsWindow %p\n", (void*)innerMostWindow));
7412
7413
  LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({retx, rety});
7414
7415
  RefPtr<nsDragService> dragService = nsDragService::GetInstance();
7416
  return dragService->ScheduleDropEvent(innerMostWindow, aDragContext,
7417
                                        aWaylandDragContext, point, aTime);
7418
}
7419
7420
static gboolean drag_drop_event_cb(GtkWidget* aWidget,
7421
                                   GdkDragContext* aDragContext, gint aX,
7422
                                   gint aY, guint aTime, gpointer aData) {
7423
  return WindowDragDropHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
7424
}
7425
7426
static void drag_data_received_event_cb(GtkWidget* aWidget,
7427
                                        GdkDragContext* aDragContext, gint aX,
7428
                                        gint aY,
7429
                                        GtkSelectionData* aSelectionData,
7430
                                        guint aInfo, guint aTime,
7431
                                        gpointer aData) {
7432
  RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
7433
  if (!window) return;
7434
7435
  window->OnDragDataReceivedEvent(aWidget, aDragContext, aX, aY, aSelectionData,
7436
                                  aInfo, aTime, aData);
7437
}
7438
7439
static nsresult initialize_prefs(void) {
7440
  gRaiseWindows =
7441
      Preferences::GetBool("mozilla.widget.raise-on-setfocus", true);
7442
  gUseWaylandVsync =
7443
      Preferences::GetBool("widget.wayland_vsync.enabled", false);
7444
7445
  if (Preferences::HasUserValue("widget.use-aspect-ratio")) {
7446
    gUseAspectRatio = Preferences::GetBool("widget.use-aspect-ratio", true);
7447
  } else {
7448
    static const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
7449
    gUseAspectRatio =
7450
        currentDesktop ? (strstr(currentDesktop, "GNOME") != nullptr) : false;
7451
  }
7452
7453
  return NS_OK;
7454
}
7455
7456
static GdkWindow* get_inner_gdk_window(GdkWindow* aWindow, gint x, gint y,
7457
                                       gint* retx, gint* rety) {
7458
  gint cx, cy, cw, ch;
7459
  GList* children = gdk_window_peek_children(aWindow);
7460
  for (GList* child = g_list_last(children); child;
7461
       child = g_list_previous(child)) {
7462
    auto* childWindow = (GdkWindow*)child->data;
7463
    if (get_window_for_gdk_window(childWindow)) {
7464
      gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch);
7465
      if ((cx < x) && (x < (cx + cw)) && (cy < y) && (y < (cy + ch)) &&
7466
          gdk_window_is_visible(childWindow)) {
7467
        return get_inner_gdk_window(childWindow, x - cx, y - cy, retx, rety);
7468
      }
7469
    }
7470
  }
7471
  *retx = x;
7472
  *rety = y;
7473
  return aWindow;
7474
}
7475
7476
static int is_parent_ungrab_enter(GdkEventCrossing* aEvent) {
7477
  return (GDK_CROSSING_UNGRAB == aEvent->mode) &&
7478
         ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
7479
          (GDK_NOTIFY_VIRTUAL == aEvent->detail));
7480
}
7481
7482
static int is_parent_grab_leave(GdkEventCrossing* aEvent) {
7483
  return (GDK_CROSSING_GRAB == aEvent->mode) &&
7484
         ((GDK_NOTIFY_ANCESTOR == aEvent->detail) ||
7485
          (GDK_NOTIFY_VIRTUAL == aEvent->detail));
7486
}
7487
7488
#ifdef ACCESSIBILITY
7489
void nsWindow::CreateRootAccessible() {
7490
  if (mIsTopLevel && !mRootAccessible) {
7491
    LOG(("nsWindow:: Create Toplevel Accessibility\n"));
7492
    mRootAccessible = GetRootAccessible();
7493
  }
7494
}
7495
7496
void nsWindow::DispatchEventToRootAccessible(uint32_t aEventType) {
7497
  if (!a11y::ShouldA11yBeEnabled()) {
7498
    return;
7499
  }
7500
7501
  nsAccessibilityService* accService = GetOrCreateAccService();
7502
  if (!accService) {
7503
    return;
7504
  }
7505
7506
  // Get the root document accessible and fire event to it.
7507
  a11y::LocalAccessible* acc = GetRootAccessible();
7508
  if (acc) {
7509
    accService->FireAccessibleEvent(aEventType, acc);
7510
  }
7511
}
7512
7513
void nsWindow::DispatchActivateEventAccessible(void) {
7514
  DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE);
7515
}
7516
7517
void nsWindow::DispatchDeactivateEventAccessible(void) {
7518
  DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE);
7519
}
7520
7521
void nsWindow::DispatchMaximizeEventAccessible(void) {
7522
  DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE);
7523
}
7524
7525
void nsWindow::DispatchMinimizeEventAccessible(void) {
7526
  DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE);
7527
}
7528
7529
void nsWindow::DispatchRestoreEventAccessible(void) {
7530
  DispatchEventToRootAccessible(nsIAccessibleEvent::EVENT_WINDOW_RESTORE);
7531
}
7532
7533
#endif /* #ifdef ACCESSIBILITY */
7534
7535
void nsWindow::SetInputContext(const InputContext& aContext,
7536
                               const InputContextAction& aAction) {
7537
  if (!mIMContext) {
7538
    return;
7539
  }
7540
  mIMContext->SetInputContext(this, &aContext, &aAction);
7541
}
7542
7543
InputContext nsWindow::GetInputContext() {
7544
  InputContext context;
7545
  if (!mIMContext) {
7546
    context.mIMEState.mEnabled = IMEEnabled::Disabled;
7547
    context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
7548
  } else {
7549
    context = mIMContext->GetInputContext();
7550
  }
7551
  return context;
7552
}
7553
7554
TextEventDispatcherListener* nsWindow::GetNativeTextEventDispatcherListener() {
7555
  if (NS_WARN_IF(!mIMContext)) {
7556
    return nullptr;
7557
  }
7558
  return mIMContext;
7559
}
7560
7561
bool nsWindow::GetEditCommands(NativeKeyBindingsType aType,
7562
                               const WidgetKeyboardEvent& aEvent,
7563
                               nsTArray<CommandInt>& aCommands) {
7564
  // Validate the arguments.
7565
  if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType, aEvent, aCommands))) {
7566
    return false;
7567
  }
7568
7569
  Maybe<WritingMode> writingMode;
7570
  if (aEvent.NeedsToRemapNavigationKey()) {
7571
    if (RefPtr<TextEventDispatcher> dispatcher = GetTextEventDispatcher()) {
7572
      writingMode = dispatcher->MaybeWritingModeAtSelection();
7573
    }
7574
  }
7575
7576
  NativeKeyBindings* keyBindings = NativeKeyBindings::GetInstance(aType);
7577
  keyBindings->GetEditCommands(aEvent, writingMode, aCommands);
7578
  return true;
7579
}
7580
7581
already_AddRefed<DrawTarget> nsWindow::StartRemoteDrawingInRegion(
7582
    const LayoutDeviceIntRegion& aInvalidRegion, BufferMode* aBufferMode) {
7583
  return mSurfaceProvider.StartRemoteDrawingInRegion(aInvalidRegion,
7584
                                                     aBufferMode);
7585
}
7586
7587
void nsWindow::EndRemoteDrawingInRegion(
7588
    DrawTarget* aDrawTarget, const LayoutDeviceIntRegion& aInvalidRegion) {
7589
  mSurfaceProvider.EndRemoteDrawingInRegion(aDrawTarget, aInvalidRegion);
7590
}
7591
7592
// Code shared begin BeginMoveDrag and BeginResizeDrag
7593
bool nsWindow::GetDragInfo(WidgetMouseEvent* aMouseEvent, GdkWindow** aWindow,
7594
                           gint* aButton, gint* aRootX, gint* aRootY) {
7595
  if (aMouseEvent->mButton != MouseButton::ePrimary) {
7596
    // we can only begin a move drag with the left mouse button
7597
    return false;
7598
  }
7599
  *aButton = 1;
7600
7601
  // get the gdk window for this widget
7602
  GdkWindow* gdk_window = mGdkWindow;
7603
  if (!gdk_window) {
7604
    return false;
7605
  }
7606
#ifdef DEBUG
7607
  // GDK_IS_WINDOW(...) expands to a statement-expression, and
7608
  // statement-expressions are not allowed in template-argument lists. So we
7609
  // have to make the MOZ_ASSERT condition indirect.
7610
  if (!GDK_IS_WINDOW(gdk_window)) {
7611
    MOZ_ASSERT(false, "must really be window");
7612
  }
7613
#endif
7614
7615
  // find the top-level window
7616
  gdk_window = gdk_window_get_toplevel(gdk_window);
7617
  MOZ_ASSERT(gdk_window, "gdk_window_get_toplevel should not return null");
7618
  *aWindow = gdk_window;
7619
7620
  if (!aMouseEvent->mWidget) {
7621
    return false;
7622
  }
7623
7624
  if (mIsX11Display) {
7625
    // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=789054
7626
    // To avoid crashes disable double-click on WM without _NET_WM_MOVERESIZE.
7627
    // See _should_perform_ewmh_drag() at gdkwindow-x11.c
7628
    GdkScreen* screen = gdk_window_get_screen(gdk_window);
7629
    GdkAtom atom = gdk_atom_intern("_NET_WM_MOVERESIZE", FALSE);
7630
    if (!gdk_x11_screen_supports_net_wm_hint(screen, atom)) {
7631
      static unsigned int lastTimeStamp = 0;
7632
      if (lastTimeStamp != aMouseEvent->mTime) {
7633
        lastTimeStamp = aMouseEvent->mTime;
7634
      } else {
7635
        return false;
7636
      }
7637
    }
7638
  }
7639
7640
  // FIXME: It would be nice to have the widget position at the time
7641
  // of the event, but it's relatively unlikely that the widget has
7642
  // moved since the mousedown.  (On the other hand, it's quite likely
7643
  // that the mouse has moved, which is why we use the mouse position
7644
  // from the event.)
7645
  LayoutDeviceIntPoint offset = aMouseEvent->mWidget->WidgetToScreenOffset();
7646
  *aRootX = aMouseEvent->mRefPoint.x + offset.x;
7647
  *aRootY = aMouseEvent->mRefPoint.y + offset.y;
7648
7649
  return true;
7650
}
7651
7652
nsresult nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent, int32_t aHorizontal,
7653
                                   int32_t aVertical) {
7654
  NS_ENSURE_ARG_POINTER(aEvent);
7655
7656
  if (aEvent->mClass != eMouseEventClass) {
7657
    // you can only begin a resize drag with a mouse event
7658
    return NS_ERROR_INVALID_ARG;
7659
  }
7660
7661
  GdkWindow* gdk_window;
7662
  gint button, screenX, screenY;
7663
  if (!GetDragInfo(aEvent->AsMouseEvent(), &gdk_window, &button, &screenX,
7664
                   &screenY)) {
7665
    return NS_ERROR_FAILURE;
7666
  }
7667
7668
  // work out what GdkWindowEdge we're talking about
7669
  GdkWindowEdge window_edge;
7670
  if (aVertical < 0) {
7671
    if (aHorizontal < 0) {
7672
      window_edge = GDK_WINDOW_EDGE_NORTH_WEST;
7673
    } else if (aHorizontal == 0) {
7674
      window_edge = GDK_WINDOW_EDGE_NORTH;
7675
    } else {
7676
      window_edge = GDK_WINDOW_EDGE_NORTH_EAST;
7677
    }
7678
  } else if (aVertical == 0) {
7679
    if (aHorizontal < 0) {
7680
      window_edge = GDK_WINDOW_EDGE_WEST;
7681
    } else if (aHorizontal == 0) {
7682
      return NS_ERROR_INVALID_ARG;
7683
    } else {
7684
      window_edge = GDK_WINDOW_EDGE_EAST;
7685
    }
7686
  } else {
7687
    if (aHorizontal < 0) {
7688
      window_edge = GDK_WINDOW_EDGE_SOUTH_WEST;
7689
    } else if (aHorizontal == 0) {
7690
      window_edge = GDK_WINDOW_EDGE_SOUTH;
7691
    } else {
7692
      window_edge = GDK_WINDOW_EDGE_SOUTH_EAST;
7693
    }
7694
  }
7695
7696
  // tell the window manager to start the resize
7697
  gdk_window_begin_resize_drag(gdk_window, window_edge, button, screenX,
7698
                               screenY, aEvent->mTime);
7699
7700
  return NS_OK;
7701
}
7702
7703
nsIWidget::LayerManager* nsWindow::GetLayerManager(
7704
    PLayerTransactionChild* aShadowManager, LayersBackend aBackendHint,
7705
    LayerManagerPersistence aPersistence) {
7706
  if (mIsDestroyed) {
7707
    // Prevent external code from triggering the re-creation of the
7708
    // LayerManager/Compositor during shutdown. Just return what we currently
7709
    // have, which is most likely null.
7710
    return mLayerManager;
7711
  }
7712
7713
  return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint,
7714
                                       aPersistence);
7715
}
7716
7717
void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) {
7718
  if (delegate) {
7719
    mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
7720
    MOZ_ASSERT(mCompositorWidgetDelegate,
7721
               "nsWindow::SetCompositorWidgetDelegate called with a "
7722
               "non-PlatformCompositorWidgetDelegate");
7723
#ifdef MOZ_WAYLAND
7724
    MaybeResumeCompositor();
7725
#endif
7726
  } else {
7727
    mCompositorWidgetDelegate = nullptr;
7728
  }
7729
}
7730
7731
void nsWindow::ClearCachedResources() {
7732
  if (mLayerManager && mLayerManager->GetBackendType() ==
7733
                           mozilla::layers::LayersBackend::LAYERS_BASIC) {
7734
    mLayerManager->ClearCachedResources();
7735
  }
7736
7737
  GList* children = gdk_window_peek_children(mGdkWindow);
7738
  for (GList* list = children; list; list = list->next) {
7739
    nsWindow* window = get_window_for_gdk_window(GDK_WINDOW(list->data));
7740
    if (window) {
7741
      window->ClearCachedResources();
7742
    }
7743
  }
7744
}
7745
7746
/* nsWindow::UpdateClientOffsetFromCSDWindow() is designed to be called from
7747
 * nsWindow::OnConfigureEvent() when mContainer window is already positioned.
7748
 *
7749
 * It works only for CSD decorated GtkWindow.
7750
 */
7751
void nsWindow::UpdateClientOffsetFromCSDWindow() {
7752
  int x, y;
7753
  gdk_window_get_position(mGdkWindow, &x, &y);
7754
7755
  x = GdkCoordToDevicePixels(x);
7756
  y = GdkCoordToDevicePixels(y);
7757
7758
  if (mClientOffset.x != x || mClientOffset.y != y) {
7759
    mClientOffset = nsIntPoint(x, y);
7760
7761
    LOG(("nsWindow::UpdateClientOffsetFromCSDWindow [%p] %d, %d\n", (void*)this,
7762
         mClientOffset.x, mClientOffset.y));
7763
7764
    // Send a WindowMoved notification. This ensures that BrowserParent
7765
    // picks up the new client offset and sends it to the child process
7766
    // if appropriate.
7767
    NotifyWindowMoved(mBounds.x, mBounds.y);
7768
  }
7769
}
7770
7771
nsresult nsWindow::SetNonClientMargins(LayoutDeviceIntMargin& aMargins) {
7772
  SetDrawsInTitlebar(aMargins.top == 0);
7773
  return NS_OK;
7774
}
7775
7776
void nsWindow::SetDrawsInTitlebar(bool aState) {
7777
  LOG(("nsWindow::SetDrawsInTitlebar() [%p] State %d mGtkWindowDecoration %d\n",
7778
       (void*)this, aState, (int)mGtkWindowDecoration));
7779
7780
  if (mIsPIPWindow && aState == mDrawInTitlebar) {
7781
    gtk_window_set_decorated(GTK_WINDOW(mShell), !aState);
7782
    return;
7783
  }
7784
7785
  if (!mShell || mGtkWindowDecoration == GTK_DECORATION_NONE ||
7786
      aState == mDrawInTitlebar) {
7787
    return;
7788
  }
7789
7790
  if (mGtkWindowDecoration == GTK_DECORATION_SYSTEM) {
7791
    SetWindowDecoration(aState ? eBorderStyle_border : mBorderStyle);
7792
  } else if (mGtkWindowDecoration == GTK_DECORATION_CLIENT) {
7793
    LOG(("    Using CSD mode\n"));
7794
7795
    /* Window manager does not support GDK_DECOR_BORDER,
7796
     * emulate it by CSD.
7797
     *
7798
     * gtk_window_set_titlebar() works on unrealized widgets only,
7799
     * we need to handle mShell carefully here.
7800
     * When CSD is enabled mGdkWindow is owned by mContainer which is good
7801
     * as we can't delete our mGdkWindow. To make mShell unrealized while
7802
     * mContainer is preserved we temporary reparent mContainer to an
7803
     * invisible GtkWindow.
7804
     */
7805
    NativeShow(false);
7806
7807
    // Using GTK_WINDOW_POPUP rather than
7808
    // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
7809
    // initialization and window manager interaction.
7810
    GtkWidget* tmpWindow = gtk_window_new(GTK_WINDOW_POPUP);
7811
    gtk_widget_realize(tmpWindow);
7812
7813
    gtk_widget_reparent(GTK_WIDGET(mContainer), tmpWindow);
7814
    gtk_widget_unrealize(GTK_WIDGET(mShell));
7815
7816
    if (aState) {
7817
      // Add a hidden titlebar widget to trigger CSD, but disable the default
7818
      // titlebar.  GtkFixed is a somewhat random choice for a simple unused
7819
      // widget. gtk_window_set_titlebar() takes ownership of the titlebar
7820
      // widget.
7821
      gtk_window_set_titlebar(GTK_WINDOW(mShell), gtk_fixed_new());
7822
    } else {
7823
      gtk_window_set_titlebar(GTK_WINDOW(mShell), nullptr);
7824
    }
7825
7826
    /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=791081
7827
     * gtk_widget_realize() throws:
7828
     * "In pixman_region32_init_rect: Invalid rectangle passed"
7829
     * when mShell has default 1x1 size.
7830
     */
7831
    GtkAllocation allocation = {0, 0, 0, 0};
7832
    gtk_widget_get_preferred_width(GTK_WIDGET(mShell), nullptr,
7833
                                   &allocation.width);
7834
    gtk_widget_get_preferred_height(GTK_WIDGET(mShell), nullptr,
7835
                                    &allocation.height);
7836
    gtk_widget_size_allocate(GTK_WIDGET(mShell), &allocation);
7837
7838
    gtk_widget_realize(GTK_WIDGET(mShell));
7839
    gtk_widget_reparent(GTK_WIDGET(mContainer), GTK_WIDGET(mShell));
7840
    mNeedsShow = true;
7841
    NativeResize();
7842
7843
    // Label mShell toplevel window so property_notify_event_cb callback
7844
    // can find its way home.
7845
    g_object_set_data(G_OBJECT(gtk_widget_get_window(mShell)), "nsWindow",
7846
                      this);
7847
#ifdef MOZ_X11
7848
    SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED);
7849
#endif
7850
    RefreshWindowClass();
7851
7852
    gtk_widget_destroy(tmpWindow);
7853
  }
7854
7855
  mDrawInTitlebar = aState;
7856
7857
  if (mTransparencyBitmapForTitlebar) {
7858
    if (mDrawInTitlebar && mSizeState == nsSizeMode_Normal && !mIsTiled) {
7859
      UpdateTitlebarTransparencyBitmap();
7860
    } else {
7861
      ClearTransparencyBitmap();
7862
    }
7863
  }
7864
}
7865
7866
GtkWindow* nsWindow::GetCurrentTopmostWindow() {
7867
  GtkWindow* parentWindow = GTK_WINDOW(GetGtkWidget());
7868
  GtkWindow* topmostParentWindow = nullptr;
7869
  while (parentWindow) {
7870
    topmostParentWindow = parentWindow;
7871
    parentWindow = gtk_window_get_transient_for(parentWindow);
7872
  }
7873
  return topmostParentWindow;
7874
}
7875
7876
gint nsWindow::GdkScaleFactor() {
7877
  // We depend on notify::scale-factor callback which is reliable for toplevel
7878
  // windows only, so don't use scale cache for popup windows.
7879
  if (mWindowType == eWindowType_toplevel && !mWindowScaleFactorChanged) {
7880
    return mWindowScaleFactor;
7881
  }
7882
7883
  GdkWindow* scaledGdkWindow = mGdkWindow;
7884
  if (!mIsX11Display) {
7885
    // For popup windows/dialogs with parent window we need to get scale factor
7886
    // of the topmost window. Otherwise the scale factor of the popup is
7887
    // not updated during it's hidden.
7888
    if (mWindowType == eWindowType_popup || mWindowType == eWindowType_dialog) {
7889
      // Get toplevel window for scale factor:
7890
      GtkWindow* topmostParentWindow = GetCurrentTopmostWindow();
7891
      if (topmostParentWindow) {
7892
        scaledGdkWindow =
7893
            gtk_widget_get_window(GTK_WIDGET(topmostParentWindow));
7894
      } else {
7895
        NS_WARNING("Popup/Dialog has no parent.");
7896
      }
7897
      // Fallback for windows which parent has been unrealized.
7898
      if (!scaledGdkWindow) {
7899
        scaledGdkWindow = mGdkWindow;
7900
      }
7901
    }
7902
  }
7903
7904
  // Available as of GTK 3.10+
7905
  static auto sGdkWindowGetScaleFactorPtr =
7906
      (gint(*)(GdkWindow*))dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor");
7907
  if (sGdkWindowGetScaleFactorPtr && scaledGdkWindow) {
7908
    mWindowScaleFactor = (*sGdkWindowGetScaleFactorPtr)(scaledGdkWindow);
7909
    mWindowScaleFactorChanged = false;
7910
  } else {
7911
    mWindowScaleFactor = ScreenHelperGTK::GetGTKMonitorScaleFactor();
7912
  }
7913
7914
  return mWindowScaleFactor;
7915
}
7916
7917
gint nsWindow::DevicePixelsToGdkCoordRoundUp(int pixels) {
7918
  gint scale = GdkScaleFactor();
7919
  return (pixels + scale - 1) / scale;
7920
}
7921
7922
gint nsWindow::DevicePixelsToGdkCoordRoundDown(int pixels) {
7923
  gint scale = GdkScaleFactor();
7924
  return pixels / scale;
7925
}
7926
7927
GdkPoint nsWindow::DevicePixelsToGdkPointRoundDown(LayoutDeviceIntPoint point) {
7928
  gint scale = GdkScaleFactor();
7929
  return {point.x / scale, point.y / scale};
7930
}
7931
7932
GdkRectangle nsWindow::DevicePixelsToGdkRectRoundOut(LayoutDeviceIntRect rect) {
7933
  gint scale = GdkScaleFactor();
7934
  int x = rect.x / scale;
7935
  int y = rect.y / scale;
7936
  int right = (rect.x + rect.width + scale - 1) / scale;
7937
  int bottom = (rect.y + rect.height + scale - 1) / scale;
7938
  return {x, y, right - x, bottom - y};
7939
}
7940
7941
GdkRectangle nsWindow::DevicePixelsToGdkSizeRoundUp(
7942
    LayoutDeviceIntSize pixelSize) {
7943
  gint scale = GdkScaleFactor();
7944
  gint width = (pixelSize.width + scale - 1) / scale;
7945
  gint height = (pixelSize.height + scale - 1) / scale;
7946
  return {0, 0, width, height};
7947
}
7948
7949
int nsWindow::GdkCoordToDevicePixels(gint coord) {
7950
  return coord * GdkScaleFactor();
7951
}
7952
7953
LayoutDeviceIntPoint nsWindow::GdkEventCoordsToDevicePixels(gdouble x,
7954
                                                            gdouble y) {
7955
  gint scale = GdkScaleFactor();
7956
  return LayoutDeviceIntPoint::Floor(x * scale, y * scale);
7957
}
7958
7959
LayoutDeviceIntPoint nsWindow::GdkPointToDevicePixels(GdkPoint point) {
7960
  gint scale = GdkScaleFactor();
7961
  return LayoutDeviceIntPoint(point.x * scale, point.y * scale);
7962
}
7963
7964
LayoutDeviceIntRect nsWindow::GdkRectToDevicePixels(GdkRectangle rect) {
7965
  gint scale = GdkScaleFactor();
7966
  return LayoutDeviceIntRect(rect.x * scale, rect.y * scale, rect.width * scale,
7967
                             rect.height * scale);
7968
}
7969
7970
nsresult nsWindow::SynthesizeNativeMouseEvent(
7971
    LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
7972
    MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
7973
    nsIObserver* aObserver) {
7974
  AutoObserverNotifier notifier(aObserver, "mouseevent");
7975
7976
  if (!mGdkWindow) {
7977
    return NS_OK;
7978
  }
7979
7980
  GdkDisplay* display = gdk_window_get_display(mGdkWindow);
7981
7982
  // When a button-press/release event is requested, create it here and put it
7983
  // in the event queue. This will not emit a motion event - this needs to be
7984
  // done explicitly *before* requesting a button-press/release. You will also
7985
  // need to wait for the motion event to be dispatched before requesting a
7986
  // button-press/release event to maintain the desired event order.
7987
  switch (aNativeMessage) {
7988
    case NativeMouseMessage::ButtonDown:
7989
    case NativeMouseMessage::ButtonUp: {
7990
      GdkEvent event;
7991
      memset(&event, 0, sizeof(GdkEvent));
7992
      event.type = aNativeMessage == NativeMouseMessage::ButtonDown
7993
                       ? GDK_BUTTON_PRESS
7994
                       : GDK_BUTTON_RELEASE;
7995
      switch (aButton) {
7996
        case MouseButton::ePrimary:
7997
        case MouseButton::eMiddle:
7998
        case MouseButton::eSecondary:
7999
        case MouseButton::eX1:
8000
        case MouseButton::eX2:
8001
          event.button.button = aButton + 1;
8002
          break;
8003
        default:
8004
          return NS_ERROR_INVALID_ARG;
8005
      }
8006
      event.button.state =
8007
          KeymapWrapper::ConvertWidgetModifierToGdkState(aModifierFlags);
8008
      event.button.window = mGdkWindow;
8009
      event.button.time = GDK_CURRENT_TIME;
8010
8011
      // Get device for event source
8012
      GdkDeviceManager* device_manager =
8013
          gdk_display_get_device_manager(display);
8014
      event.button.device =
8015
          gdk_device_manager_get_client_pointer(device_manager);
8016
8017
      event.button.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
8018
      event.button.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
8019
8020
      LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
8021
      event.button.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
8022
      event.button.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
8023
8024
      gdk_event_put(&event);
8025
      return NS_OK;
8026
    }
8027
    case NativeMouseMessage::Move: {
8028
      // We don't support specific events other than button-press/release. In
8029
      // all other cases we'll synthesize a motion event that will be emitted by
8030
      // gdk_display_warp_pointer().
8031
      // XXX How to activate native modifier for the other events?
8032
#ifdef MOZ_WAYLAND
8033
      // Impossible to warp the pointer on Wayland.
8034
      // For pointer lock, pointer-constraints and relative-pointer are used.
8035
      if (GdkIsWaylandDisplay()) {
8036
        return NS_OK;
8037
      }
8038
#endif
8039
      GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
8040
      GdkPoint point = DevicePixelsToGdkPointRoundDown(aPoint);
8041
      gdk_display_warp_pointer(display, screen, point.x, point.y);
8042
      return NS_OK;
8043
    }
8044
    case NativeMouseMessage::EnterWindow:
8045
    case NativeMouseMessage::LeaveWindow:
8046
      MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Linux");
8047
      return NS_ERROR_INVALID_ARG;
8048
  }
8049
  return NS_ERROR_UNEXPECTED;
8050
}
8051
8052
nsresult nsWindow::SynthesizeNativeMouseScrollEvent(
8053
    mozilla::LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
8054
    double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
8055
    uint32_t aAdditionalFlags, nsIObserver* aObserver) {
8056
  AutoObserverNotifier notifier(aObserver, "mousescrollevent");
8057
8058
  if (!mGdkWindow) {
8059
    return NS_OK;
8060
  }
8061
8062
  GdkEvent event;
8063
  memset(&event, 0, sizeof(GdkEvent));
8064
  event.type = GDK_SCROLL;
8065
  event.scroll.window = mGdkWindow;
8066
  event.scroll.time = GDK_CURRENT_TIME;
8067
  // Get device for event source
8068
  GdkDisplay* display = gdk_window_get_display(mGdkWindow);
8069
  GdkDeviceManager* device_manager = gdk_display_get_device_manager(display);
8070
  event.scroll.device = gdk_device_manager_get_client_pointer(device_manager);
8071
  event.scroll.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
8072
  event.scroll.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
8073
8074
  LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
8075
  event.scroll.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
8076
  event.scroll.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
8077
8078
  // The delta values are backwards on Linux compared to Windows and Cocoa,
8079
  // hence the negation.
8080
  event.scroll.direction = GDK_SCROLL_SMOOTH;
8081
  event.scroll.delta_x = -aDeltaX;
8082
  event.scroll.delta_y = -aDeltaY;
8083
8084
  gdk_event_put(&event);
8085
8086
  return NS_OK;
8087
}
8088
8089
nsresult nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
8090
                                              TouchPointerState aPointerState,
8091
                                              LayoutDeviceIntPoint aPoint,
8092
                                              double aPointerPressure,
8093
                                              uint32_t aPointerOrientation,
8094
                                              nsIObserver* aObserver) {
8095
  AutoObserverNotifier notifier(aObserver, "touchpoint");
8096
8097
  if (!mGdkWindow) {
8098
    return NS_OK;
8099
  }
8100
8101
  GdkEvent event;
8102
  memset(&event, 0, sizeof(GdkEvent));
8103
8104
  static std::map<uint32_t, GdkEventSequence*> sKnownPointers;
8105
8106
  auto result = sKnownPointers.find(aPointerId);
8107
  switch (aPointerState) {
8108
    case TOUCH_CONTACT:
8109
      if (result == sKnownPointers.end()) {
8110
        // GdkEventSequence isn't a thing we can instantiate, and never gets
8111
        // dereferenced in the gtk code. It's an opaque pointer, the only
8112
        // requirement is that it be distinct from other instances of
8113
        // GdkEventSequence*.
8114
        event.touch.sequence = (GdkEventSequence*)((uintptr_t)aPointerId);
8115
        sKnownPointers[aPointerId] = event.touch.sequence;
8116
        event.type = GDK_TOUCH_BEGIN;
8117
      } else {
8118
        event.touch.sequence = result->second;
8119
        event.type = GDK_TOUCH_UPDATE;
8120
      }
8121
      break;
8122
    case TOUCH_REMOVE:
8123
      event.type = GDK_TOUCH_END;
8124
      if (result == sKnownPointers.end()) {
8125
        NS_WARNING("Tried to synthesize touch-end for unknown pointer!");
8126
        return NS_ERROR_UNEXPECTED;
8127
      }
8128
      event.touch.sequence = result->second;
8129
      sKnownPointers.erase(result);
8130
      break;
8131
    case TOUCH_CANCEL:
8132
      event.type = GDK_TOUCH_CANCEL;
8133
      if (result == sKnownPointers.end()) {
8134
        NS_WARNING("Tried to synthesize touch-cancel for unknown pointer!");
8135
        return NS_ERROR_UNEXPECTED;
8136
      }
8137
      event.touch.sequence = result->second;
8138
      sKnownPointers.erase(result);
8139
      break;
8140
    case TOUCH_HOVER:
8141
    default:
8142
      return NS_ERROR_NOT_IMPLEMENTED;
8143
  }
8144
8145
  event.touch.window = mGdkWindow;
8146
  event.touch.time = GDK_CURRENT_TIME;
8147
8148
  GdkDisplay* display = gdk_window_get_display(mGdkWindow);
8149
  GdkDeviceManager* device_manager = gdk_display_get_device_manager(display);
8150
  event.touch.device = gdk_device_manager_get_client_pointer(device_manager);
8151
8152
  event.touch.x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
8153
  event.touch.y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
8154
8155
  LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
8156
  event.touch.x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
8157
  event.touch.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
8158
8159
  gdk_event_put(&event);
8160
8161
  return NS_OK;
8162
}
8163
8164
nsresult nsWindow::SynthesizeNativeTouchPadPinch(TouchpadPinchPhase aEventPhase,
8165
                                                 float aScale,
8166
                                                 LayoutDeviceIntPoint aPoint,
8167
                                                 int32_t aModifierFlags) {
8168
  if (!mGdkWindow) {
8169
    return NS_OK;
8170
  }
8171
  GdkEvent event;
8172
  memset(&event, 0, sizeof(GdkEvent));
8173
8174
  GdkEventTouchpadPinch* touchpad_event =
8175
      reinterpret_cast<GdkEventTouchpadPinch*>(&event);
8176
  touchpad_event->type = GDK_TOUCHPAD_PINCH;
8177
8178
  switch (aEventPhase) {
8179
    case PHASE_BEGIN:
8180
      touchpad_event->phase = GDK_TOUCHPAD_GESTURE_PHASE_BEGIN;
8181
      break;
8182
    case PHASE_UPDATE:
8183
      touchpad_event->phase = GDK_TOUCHPAD_GESTURE_PHASE_UPDATE;
8184
      break;
8185
    case PHASE_END:
8186
      touchpad_event->phase = GDK_TOUCHPAD_GESTURE_PHASE_END;
8187
      break;
8188
8189
    default:
8190
      return NS_ERROR_NOT_IMPLEMENTED;
8191
  }
8192
8193
  touchpad_event->window = mGdkWindow;
8194
  // We only set the fields of GdkEventTouchpadPinch which are
8195
  // actually used in OnTouchpadPinchEvent().
8196
  // GdkEventTouchpadPinch has additional fields (for example, `dx` and `dy`).
8197
  // If OnTouchpadPinchEvent() is changed to use other fields, this function
8198
  // will need to change to set them as well.
8199
  touchpad_event->time = GDK_CURRENT_TIME;
8200
  touchpad_event->scale = aScale;
8201
  touchpad_event->x_root = DevicePixelsToGdkCoordRoundDown(aPoint.x);
8202
  touchpad_event->y_root = DevicePixelsToGdkCoordRoundDown(aPoint.y);
8203
8204
  LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset();
8205
  touchpad_event->x = DevicePixelsToGdkCoordRoundDown(pointInWindow.x);
8206
  touchpad_event->y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y);
8207
  touchpad_event->state = aModifierFlags;
8208
8209
  gdk_event_put(&event);
8210
8211
  return NS_OK;
8212
}
8213
8214
nsWindow::GtkWindowDecoration nsWindow::GetSystemGtkWindowDecoration() {
8215
  if (sGtkWindowDecoration != GTK_DECORATION_UNKNOWN) {
8216
    return sGtkWindowDecoration;
8217
  }
8218
8219
  // Allow MOZ_GTK_TITLEBAR_DECORATION to override our heuristics
8220
  const char* decorationOverride = getenv("MOZ_GTK_TITLEBAR_DECORATION");
8221
  if (decorationOverride) {
8222
    if (strcmp(decorationOverride, "none") == 0) {
8223
      sGtkWindowDecoration = GTK_DECORATION_NONE;
8224
    } else if (strcmp(decorationOverride, "client") == 0) {
8225
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8226
    } else if (strcmp(decorationOverride, "system") == 0) {
8227
      sGtkWindowDecoration = GTK_DECORATION_SYSTEM;
8228
    }
8229
    return sGtkWindowDecoration;
8230
  }
8231
8232
  // nsWindow::GetSystemGtkWindowDecoration can be called from various threads
8233
  // so we can't use gfxPlatformGtk here.
8234
  if (GdkIsWaylandDisplay()) {
8235
    sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8236
    return sGtkWindowDecoration;
8237
  }
8238
8239
  // GTK_CSD forces CSD mode - use also CSD because window manager
8240
  // decorations does not work with CSD.
8241
  // We check GTK_CSD as well as gtk_window_should_use_csd() does.
8242
  const char* csdOverride = getenv("GTK_CSD");
8243
  if (csdOverride && atoi(csdOverride)) {
8244
    sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8245
    return sGtkWindowDecoration;
8246
  }
8247
8248
  const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
8249
  if (currentDesktop) {
8250
    // GNOME Flashback (fallback)
8251
    if (strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr) {
8252
      sGtkWindowDecoration = GTK_DECORATION_SYSTEM;
8253
      // Pop Linux Bug 1629198
8254
    } else if (strstr(currentDesktop, "pop:GNOME") != nullptr) {
8255
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8256
      // gnome-shell
8257
    } else if (strstr(currentDesktop, "GNOME") != nullptr) {
8258
      sGtkWindowDecoration = GTK_DECORATION_SYSTEM;
8259
    } else if (strstr(currentDesktop, "XFCE") != nullptr) {
8260
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8261
    } else if (strstr(currentDesktop, "X-Cinnamon") != nullptr) {
8262
      sGtkWindowDecoration = GTK_DECORATION_SYSTEM;
8263
      // KDE Plasma
8264
    } else if (strstr(currentDesktop, "KDE") != nullptr) {
8265
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8266
    } else if (strstr(currentDesktop, "Enlightenment") != nullptr) {
8267
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8268
    } else if (strstr(currentDesktop, "LXDE") != nullptr) {
8269
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8270
    } else if (strstr(currentDesktop, "openbox") != nullptr) {
8271
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8272
    } else if (strstr(currentDesktop, "i3") != nullptr) {
8273
      sGtkWindowDecoration = GTK_DECORATION_NONE;
8274
    } else if (strstr(currentDesktop, "MATE") != nullptr) {
8275
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8276
      // Ubuntu Unity
8277
    } else if (strstr(currentDesktop, "Unity") != nullptr) {
8278
      sGtkWindowDecoration = GTK_DECORATION_SYSTEM;
8279
      // Elementary OS
8280
    } else if (strstr(currentDesktop, "Pantheon") != nullptr) {
8281
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8282
    } else if (strstr(currentDesktop, "LXQt") != nullptr) {
8283
      sGtkWindowDecoration = GTK_DECORATION_SYSTEM;
8284
    } else if (strstr(currentDesktop, "Deepin") != nullptr) {
8285
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8286
    } else {
8287
      sGtkWindowDecoration = GTK_DECORATION_CLIENT;
8288
    }
8289
  } else {
8290
    sGtkWindowDecoration = GTK_DECORATION_NONE;
8291
  }
8292
  return sGtkWindowDecoration;
8293
}
8294
8295
bool nsWindow::TitlebarUseShapeMask() {
8296
  static int useShapeMask = []() {
8297
    // Don't use titlebar shape mask on Wayland
8298
    if (!gfxPlatformGtk::GetPlatform()->IsX11Display()) {
8299
      return false;
8300
    }
8301
8302
    // We can'y use shape masks on Mutter/X.org as we can't resize Firefox
8303
    // window there (Bug 1530252).
8304
    const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
8305
    if (currentDesktop) {
8306
      if (strstr(currentDesktop, "GNOME") != nullptr) {
8307
        const char* sessionType = getenv("XDG_SESSION_TYPE");
8308
        if (sessionType && strstr(sessionType, "x11") != nullptr) {
8309
          return false;
8310
        }
8311
      }
8312
    }
8313
8314
    return Preferences::GetBool("widget.titlebar-x11-use-shape-mask", false);
8315
  }();
8316
  return useShapeMask;
8317
}
8318
8319
bool nsWindow::HideTitlebarByDefault() {
8320
  static int hideTitlebar = []() {
8321
    // When user defined widget.default-hidden-titlebar don't do any
8322
    // heuristics and just follow it.
8323
    if (Preferences::HasUserValue("widget.default-hidden-titlebar")) {
8324
      return Preferences::GetBool("widget.default-hidden-titlebar", false);
8325
    }
8326
8327
    // Don't hide titlebar when it's disabled on current desktop.
8328
    const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP");
8329
    if (!currentDesktop ||
8330
        GetSystemGtkWindowDecoration() == GTK_DECORATION_NONE) {
8331
      return false;
8332
    }
8333
8334
    // We hide system titlebar on Gnome/ElementaryOS without any restriction.
8335
    return ((strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr ||
8336
             strstr(currentDesktop, "GNOME") != nullptr ||
8337
             strstr(currentDesktop, "Pantheon") != nullptr));
8338
  }();
8339
  return hideTitlebar;
8340
}
8341
8342
int32_t nsWindow::RoundsWidgetCoordinatesTo() { return GdkScaleFactor(); }
8343
8344
void nsWindow::GetCompositorWidgetInitData(
8345
    mozilla::widget::CompositorWidgetInitData* aInitData) {
8346
  // Make sure the window XID is propagated to X server, we can fail otherwise
8347
  // in GPU process (Bug 1401634).
8348
  if (mXDisplay && mXWindow != X11None) {
8349
    XFlush(mXDisplay);
8350
  }
8351
8352
  bool isShaped =
8353
      mIsTransparent && !mHasAlphaVisual && !mTransparencyBitmapForTitlebar;
8354
  *aInitData = mozilla::widget::GtkCompositorWidgetInitData(
8355
      (mXWindow != X11None) ? mXWindow : (uintptr_t) nullptr,
8356
      mXDisplay ? nsCString(XDisplayString(mXDisplay)) : nsCString(), isShaped,
8357
      mIsX11Display, GetClientSize());
8358
}
8359
8360
#ifdef MOZ_WAYLAND
8361
bool nsWindow::WaylandSurfaceNeedsClear() {
8362
  if (mContainer) {
8363
    return moz_container_wayland_surface_needs_clear(MOZ_CONTAINER(mContainer));
8364
  }
8365
  return false;
8366
}
8367
#endif
8368
8369
#ifdef MOZ_X11
8370
/* XApp progress support currently works by setting a property
8371
 * on a window with this Atom name.  A supporting window manager
8372
 * will notice this and pass it along to whatever handling has
8373
 * been implemented on that end (e.g. passing it on to a taskbar
8374
 * widget.)  There is no issue if WM support is lacking, this is
8375
 * simply ignored in that case.
8376
 *
8377
 * See https://github.com/linuxmint/xapps/blob/master/libxapp/xapp-gtk-window.c
8378
 * for further details.
8379
 */
8380
8381
#  define PROGRESS_HINT "_NET_WM_XAPP_PROGRESS"
8382
8383
static void set_window_hint_cardinal(Window xid, const gchar* atom_name,
8384
                                     gulong cardinal) {
8385
  GdkDisplay* display;
8386
8387
  display = gdk_display_get_default();
8388
8389
  if (cardinal > 0) {
8390
    XChangeProperty(GDK_DISPLAY_XDISPLAY(display), xid,
8391
                    gdk_x11_get_xatom_by_name_for_display(display, atom_name),
8392
                    XA_CARDINAL, 32, PropModeReplace, (guchar*)&cardinal, 1);
8393
  } else {
8394
    XDeleteProperty(GDK_DISPLAY_XDISPLAY(display), xid,
8395
                    gdk_x11_get_xatom_by_name_for_display(display, atom_name));
8396
  }
8397
}
8398
#endif  // MOZ_X11
8399
8400
void nsWindow::SetProgress(unsigned long progressPercent) {
8401
#ifdef MOZ_X11
8402
8403
  if (!mIsX11Display) {
8404
    return;
8405
  }
8406
8407
  if (!mShell) {
8408
    return;
8409
  }
8410
8411
  progressPercent = MIN(progressPercent, 100);
8412
8413
  set_window_hint_cardinal(GDK_WINDOW_XID(gtk_widget_get_window(mShell)),
8414
                           PROGRESS_HINT, progressPercent);
8415
#endif  // MOZ_X11
8416
}
8417
8418
#ifdef MOZ_X11
8419
void nsWindow::SetCompositorHint(WindowComposeRequest aState) {
8420
  if (!mIsX11Display) {
8421
    return;
8422
  }
8423
8424
  gulong value = aState;
8425
  GdkAtom cardinal_atom = gdk_x11_xatom_to_atom(XA_CARDINAL);
8426
  gdk_property_change(gtk_widget_get_window(mShell),
8427
                      gdk_atom_intern("_NET_WM_BYPASS_COMPOSITOR", FALSE),
8428
                      cardinal_atom,
8429
                      32,  // format
8430
                      GDK_PROP_MODE_REPLACE, (guchar*)&value, 1);
8431
}
8432
#endif
8433
8434
nsresult nsWindow::SetSystemFont(const nsCString& aFontName) {
8435
  GtkSettings* settings = gtk_settings_get_default();
8436
  g_object_set(settings, "gtk-font-name", aFontName.get(), nullptr);
8437
  return NS_OK;
8438
}
8439
8440
nsresult nsWindow::GetSystemFont(nsCString& aFontName) {
8441
  GtkSettings* settings = gtk_settings_get_default();
8442
  gchar* fontName = nullptr;
8443
  g_object_get(settings, "gtk-font-name", &fontName, nullptr);
8444
  if (fontName) {
8445
    aFontName.Assign(fontName);
8446
    g_free(fontName);
8447
  }
8448
  return NS_OK;
8449
}
8450
8451
already_AddRefed<nsIWidget> nsIWidget::CreateTopLevelWindow() {
8452
  nsCOMPtr<nsIWidget> window = new nsWindow();
8453
  return window.forget();
8454
}
8455
8456
already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
8457
  nsCOMPtr<nsIWidget> window = new nsWindow();
8458
  return window.forget();
8459
}
8460
8461
#ifdef MOZ_WAYLAND
8462
static void relative_pointer_handle_relative_motion(
8463
    void* data, struct zwp_relative_pointer_v1* pointer, uint32_t time_hi,
8464
    uint32_t time_lo, wl_fixed_t dx_w, wl_fixed_t dy_w, wl_fixed_t dx_unaccel_w,
8465
    wl_fixed_t dy_unaccel_w) {
8466
  RefPtr<nsWindow> window(reinterpret_cast<nsWindow*>(data));
8467
8468
  WidgetMouseEvent event(true, eMouseMove, window, WidgetMouseEvent::eReal);
8469
8470
  event.mRefPoint = window->GetNativePointerLockCenter();
8471
  event.mRefPoint.x += wl_fixed_to_double(dx_unaccel_w);
8472
  event.mRefPoint.y += wl_fixed_to_double(dy_unaccel_w);
8473
8474
  event.AssignEventTime(window->GetWidgetEventTime(time_lo));
8475
  window->DispatchInputEvent(&event);
8476
}
8477
8478
static const struct zwp_relative_pointer_v1_listener relative_pointer_listener =
8479
    {
8480
        relative_pointer_handle_relative_motion,
8481
};
8482
8483
void nsWindow::SetNativePointerLockCenter(
8484
    const LayoutDeviceIntPoint& aLockCenter) {
8485
  mNativePointerLockCenter = aLockCenter;
8486
}
8487
8488
void nsWindow::LockNativePointer() {
8489
  if (!GdkIsWaylandDisplay()) {
8490
    return;
8491
  }
8492
8493
  auto waylandDisplay = WaylandDisplayGet();
8494
8495
  auto* pointerConstraints = waylandDisplay->GetPointerConstraints();
8496
  if (!pointerConstraints) {
8497
    return;
8498
  }
8499
8500
  auto* relativePointerMgr = waylandDisplay->GetRelativePointerManager();
8501
  if (!relativePointerMgr) {
8502
    return;
8503
  }
8504
8505
  GdkDisplay* display = gdk_display_get_default();
8506
8507
  GdkDeviceManager* manager = gdk_display_get_device_manager(display);
8508
  MOZ_ASSERT(manager);
8509
8510
  GdkDevice* device = gdk_device_manager_get_client_pointer(manager);
8511
  if (!device) {
8512
    NS_WARNING("Could not find Wayland pointer to lock");
8513
    return;
8514
  }
8515
  wl_pointer* pointer = gdk_wayland_device_get_wl_pointer(device);
8516
  MOZ_ASSERT(pointer);
8517
8518
  wl_surface* surface =
8519
      gdk_wayland_window_get_wl_surface(gtk_widget_get_window(GetGtkWidget()));
8520
  if (!surface) {
8521
    /* Can be null when the window is hidden.
8522
     * Though it's unlikely that a lock request comes in that case, be
8523
     * defensive. */
8524
    return;
8525
  }
8526
8527
  mLockedPointer = zwp_pointer_constraints_v1_lock_pointer(
8528
      pointerConstraints, surface, pointer, nullptr,
8529
      ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
8530
  if (!mLockedPointer) {
8531
    NS_WARNING("Could not lock Wayland pointer");
8532
    return;
8533
  }
8534
8535
  mRelativePointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
8536
      relativePointerMgr, pointer);
8537
  if (!mRelativePointer) {
8538
    NS_WARNING("Could not create relative Wayland pointer");
8539
    zwp_locked_pointer_v1_destroy(mLockedPointer);
8540
    mLockedPointer = nullptr;
8541
    return;
8542
  }
8543
8544
  zwp_relative_pointer_v1_add_listener(mRelativePointer,
8545
                                       &relative_pointer_listener, this);
8546
}
8547
8548
void nsWindow::UnlockNativePointer() {
8549
  if (!GdkIsWaylandDisplay()) {
8550
    return;
8551
  }
8552
  if (mRelativePointer) {
8553
    zwp_relative_pointer_v1_destroy(mRelativePointer);
8554
    mRelativePointer = nullptr;
8555
  }
8556
  if (mLockedPointer) {
8557
    zwp_locked_pointer_v1_destroy(mLockedPointer);
8558
    mLockedPointer = nullptr;
8559
  }
8560
}
8561
8562
nsresult nsWindow::GetScreenRect(LayoutDeviceIntRect* aRect) {
8563
  typedef struct _GdkMonitor GdkMonitor;
8564
  static auto s_gdk_display_get_monitor_at_window =
8565
      (GdkMonitor * (*)(GdkDisplay*, GdkWindow*))
8566
          dlsym(RTLD_DEFAULT, "gdk_display_get_monitor_at_window");
8567
8568
  static auto s_gdk_monitor_get_workarea =
8569
      (void (*)(GdkMonitor*, GdkRectangle*))dlsym(RTLD_DEFAULT,
8570
                                                  "gdk_monitor_get_workarea");
8571
8572
  if (!s_gdk_display_get_monitor_at_window || !s_gdk_monitor_get_workarea) {
8573
    return NS_ERROR_NOT_IMPLEMENTED;
8574
  }
8575
8576
  GtkWindow* topmostParentWindow = GetCurrentTopmostWindow();
8577
  GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(topmostParentWindow));
8578
8579
  GdkMonitor* monitor =
8580
      s_gdk_display_get_monitor_at_window(gdk_display_get_default(), gdkWindow);
8581
  if (monitor) {
8582
    GdkRectangle workArea;
8583
    s_gdk_monitor_get_workarea(monitor, &workArea);
8584
    // The monitor offset won't help us in Wayland, because we can't get the
8585
    // absolute position of our window.
8586
    aRect->x = aRect->y = 0;
8587
    aRect->width = workArea.width;
8588
    aRect->height = workArea.height;
8589
    LOG(("  workarea for [%p], monitor %p: x%d y%d w%d h%d\n", this, monitor,
8590
         workArea.x, workArea.y, workArea.width, workArea.height));
8591
    return NS_OK;
8592
  }
8593
  return NS_ERROR_NOT_IMPLEMENTED;
8594
}
8595
#endif
8596
8597
bool nsWindow::GetTopLevelWindowActiveState(nsIFrame* aFrame) {
8598
  // Used by window frame and button box rendering. We can end up in here in
8599
  // the content process when rendering one of these moz styles freely in a
8600
  // page. Fail in this case, there is no applicable window focus state.
8601
  if (!XRE_IsParentProcess()) {
8602
    return false;
8603
  }
8604
  // All headless windows are considered active so they are painted.
8605
  if (gfxPlatform::IsHeadless()) {
8606
    return true;
8607
  }
8608
  // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
8609
  // until it finds a real window.
8610
  nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget());
8611
  if (!window) {
8612
    return false;
8613
  }
8614
8615
  // Get our toplevel nsWindow.
8616
  if (!window->mIsTopLevel) {
8617
    GtkWidget* widget = window->GetMozContainerWidget();
8618
    if (!widget) {
8619
      return false;
8620
    }
8621
8622
    GtkWidget* toplevelWidget = gtk_widget_get_toplevel(widget);
8623
    window = get_window_for_gtk_widget(toplevelWidget);
8624
    if (!window) {
8625
      return false;
8626
    }
8627
  }
8628
8629
  return !window->mTitlebarBackdropState;
8630
}
8631
8632
static nsIFrame* FindTitlebarFrame(nsIFrame* aFrame) {
8633
  for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
8634
    StyleAppearance appearance =
8635
        childFrame->StyleDisplay()->EffectiveAppearance();
8636
    if (appearance == StyleAppearance::MozWindowTitlebar ||
8637
        appearance == StyleAppearance::MozWindowTitlebarMaximized) {
8638
      return childFrame;
8639
    }
8640
8641
    if (nsIFrame* foundFrame = FindTitlebarFrame(childFrame)) {
8642
      return foundFrame;
8643
    }
8644
  }
8645
  return nullptr;
8646
}
8647
8648
nsIFrame* nsWindow::GetFrame(void) {
8649
  nsView* view = nsView::GetViewFor(this);
8650
  if (!view) {
8651
    return nullptr;
8652
  }
8653
  return view->GetFrame();
8654
}
8655
8656
void nsWindow::UpdateMozWindowActive() {
8657
  // Update activation state for the :-moz-window-inactive pseudoclass.
8658
  // Normally, this follows focus; we override it here to follow
8659
  // GDK_WINDOW_STATE_FOCUSED.
8660
  if (mozilla::dom::Document* document = GetDocument()) {
8661
    if (nsPIDOMWindowOuter* window = document->GetWindow()) {
8662
      if (RefPtr<mozilla::dom::BrowsingContext> bc =
8663
              window->GetBrowsingContext()) {
8664
        bc->SetIsActiveBrowserWindow(!mTitlebarBackdropState);
8665
      }
8666
    }
8667
  }
8668
}
8669
8670
void nsWindow::ForceTitlebarRedraw(void) {
8671
  MOZ_ASSERT(mDrawInTitlebar, "We should not redraw invisible titlebar.");
8672
8673
  if (!mWidgetListener || !mWidgetListener->GetPresShell()) {
8674
    return;
8675
  }
8676
8677
  nsIFrame* frame = GetFrame();
8678
  if (!frame) {
8679
    return;
8680
  }
8681
8682
  frame = FindTitlebarFrame(frame);
8683
  if (frame) {
8684
    nsIContent* content = frame->GetContent();
8685
    if (content) {
8686
      nsLayoutUtils::PostRestyleEvent(content->AsElement(), RestyleHint{0},
8687
                                      nsChangeHint_RepaintFrame);
8688
    }
8689
  }
8690
}
8691
8692
GtkTextDirection nsWindow::GetTextDirection() {
8693
  nsIFrame* frame = GetFrame();
8694
  if (!frame) {
8695
    return GTK_TEXT_DIR_LTR;
8696
  }
8697
8698
  WritingMode wm = frame->GetWritingMode();
8699
  return wm.IsPhysicalLTR() ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
8700
}
8701
8702
void nsWindow::LockAspectRatio(bool aShouldLock) {
8703
  if (!gUseAspectRatio) {
8704
    return;
8705
  }
8706
8707
  if (aShouldLock) {
8708
    int decWidth = 0, decHeight = 0;
8709
    AddCSDDecorationSize(&decWidth, &decHeight);
8710
8711
    float width =
8712
        (float)DevicePixelsToGdkCoordRoundDown(mBounds.width) + decWidth;
8713
    float height =
8714
        (float)DevicePixelsToGdkCoordRoundDown(mBounds.height) + decHeight;
8715
8716
    mAspectRatio = width / height;
8717
    LOG(("nsWindow::LockAspectRatio() [%p] width %f height %f aspect %f\n",
8718
         (void*)this, width, height, mAspectRatio));
8719
  } else {
8720
    mAspectRatio = 0.0;
8721
    LOG(("nsWindow::LockAspectRatio() [%p] removed aspect ratio\n",
8722
         (void*)this));
8723
  }
8724
8725
  ApplySizeConstraints();
8726
}
8727
8728
#ifdef MOZ_WAYLAND
8729
void nsWindow::SetEGLNativeWindowSize(
8730
    const LayoutDeviceIntSize& aEGLWindowSize) {
8731
  if (mContainer && !mIsX11Display) {
8732
    moz_container_wayland_egl_window_set_size(mContainer, aEGLWindowSize.width,
8733
                                              aEGLWindowSize.height);
8734
  }
8735
}
8736
8737
nsWindow* nsWindow::GetFocusedWindow() { return gFocusWindow; }
8738
#endif
8739
8740
LayoutDeviceIntRect nsWindow::GetMozContainerSize() {
8741
  LayoutDeviceIntRect size(0, 0, 0, 0);
8742
  if (mContainer) {
8743
    GtkAllocation allocation;
8744
    gtk_widget_get_allocation(GTK_WIDGET(mContainer), &allocation);
8745
    int scale = GdkScaleFactor();
8746
    size.width = allocation.width * scale;
8747
    size.height = allocation.height * scale;
8748
  }
8749
  return size;
8750
}

Return to bug 769494