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