Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/nsAppShell.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim:expandtab:shiftwidth=4:tabstop=4:
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 <sys/types.h>
9
#include <unistd.h>
10
#include <fcntl.h>
11
#include <errno.h>
12
#include <gdk/gdk.h>
13
#include "nsAppShell.h"
14
#include "nsWindow.h"
15
#include "mozilla/Logging.h"
16
#include "prenv.h"
17
#include "mozilla/BackgroundHangMonitor.h"
18
#include "mozilla/Hal.h"
19
#include "mozilla/Unused.h"
20
#include "mozilla/WidgetUtils.h"
21
#include "GeckoProfiler.h"
22
#include "nsIPowerManagerService.h"
23
#ifdef MOZ_ENABLE_DBUS
24
#include "WakeLockListener.h"
25
#endif
26
#include "gfxPlatform.h"
27
#include "ScreenHelperGTK.h"
28
#include "HeadlessScreenHelper.h"
29
#include "mozilla/widget/ScreenManager.h"
30
31
using mozilla::Unused;
32
using mozilla::widget::ScreenHelperGTK;
33
using mozilla::widget::HeadlessScreenHelper;
34
using mozilla::widget::ScreenManager;
35
using mozilla::LazyLogModule;
36
37
0
#define NOTIFY_TOKEN 0xFA
38
39
LazyLogModule gWidgetLog("Widget");
40
LazyLogModule gWidgetFocusLog("WidgetFocus");
41
LazyLogModule gWidgetDragLog("WidgetDrag");
42
LazyLogModule gWidgetDrawLog("WidgetDraw");
43
44
static GPollFunc sPollFunc;
45
46
// Wrapper function to disable hang monitoring while waiting in poll().
47
static gint
48
PollWrapper(GPollFD *ufds, guint nfsd, gint timeout_)
49
0
{
50
0
    mozilla::BackgroundHangMonitor().NotifyWait();
51
0
    gint result;
52
0
    {
53
0
        AUTO_PROFILER_LABEL("PollWrapper", IDLE);
54
0
        AUTO_PROFILER_THREAD_SLEEP;
55
0
        result = (*sPollFunc)(ufds, nfsd, timeout_);
56
0
    }
57
0
    mozilla::BackgroundHangMonitor().NotifyActivity();
58
0
    return result;
59
0
}
60
61
#ifdef MOZ_WIDGET_GTK
62
// For bug 726483.
63
static decltype(GtkContainerClass::check_resize) sReal_gtk_window_check_resize;
64
65
static void
66
wrap_gtk_window_check_resize(GtkContainer *container)
67
0
{
68
0
    GdkWindow* gdk_window = gtk_widget_get_window(&container->widget);
69
0
    if (gdk_window) {
70
0
        g_object_ref(gdk_window);
71
0
    }
72
0
73
0
    sReal_gtk_window_check_resize(container);
74
0
75
0
    if (gdk_window) {
76
0
        g_object_unref(gdk_window);
77
0
    }
78
0
}
79
80
// Emit resume-events on GdkFrameClock if flush-events has not been
81
// balanced by resume-events at dispose.
82
// For https://bugzilla.gnome.org/show_bug.cgi?id=742636
83
static decltype(GObjectClass::constructed) sRealGdkFrameClockConstructed;
84
static decltype(GObjectClass::dispose) sRealGdkFrameClockDispose;
85
static GQuark sPendingResumeQuark;
86
87
static void
88
OnFlushEvents(GObject* clock, gpointer)
89
0
{
90
0
    g_object_set_qdata(clock, sPendingResumeQuark, GUINT_TO_POINTER(1));
91
0
}
92
93
static void
94
OnResumeEvents(GObject* clock, gpointer)
95
0
{
96
0
    g_object_set_qdata(clock, sPendingResumeQuark, nullptr);
97
0
}
98
99
static void
100
WrapGdkFrameClockConstructed(GObject* object)
101
0
{
102
0
    sRealGdkFrameClockConstructed(object);
103
0
104
0
    g_signal_connect(object, "flush-events",
105
0
                     G_CALLBACK(OnFlushEvents), nullptr);
106
0
    g_signal_connect(object, "resume-events",
107
0
                     G_CALLBACK(OnResumeEvents), nullptr);
108
0
}
109
110
static void
111
WrapGdkFrameClockDispose(GObject* object)
112
0
{
113
0
    if (g_object_get_qdata(object, sPendingResumeQuark)) {
114
0
        g_signal_emit_by_name(object, "resume-events");
115
0
    }
116
0
117
0
    sRealGdkFrameClockDispose(object);
118
0
}
119
#endif
120
121
/*static*/ gboolean
122
nsAppShell::EventProcessorCallback(GIOChannel *source,
123
                                   GIOCondition condition,
124
                                   gpointer data)
125
0
{
126
0
    nsAppShell *self = static_cast<nsAppShell *>(data);
127
0
128
0
    unsigned char c;
129
0
    Unused << read(self->mPipeFDs[0], &c, 1);
130
0
    NS_ASSERTION(c == (unsigned char) NOTIFY_TOKEN, "wrong token");
131
0
132
0
    self->NativeEventCallback();
133
0
    return TRUE;
134
0
}
135
136
nsAppShell::~nsAppShell()
137
0
{
138
0
    mozilla::hal::Shutdown();
139
0
140
0
    if (mTag)
141
0
        g_source_remove(mTag);
142
0
    if (mPipeFDs[0])
143
0
        close(mPipeFDs[0]);
144
0
    if (mPipeFDs[1])
145
0
        close(mPipeFDs[1]);
146
0
}
147
148
nsresult
149
nsAppShell::Init()
150
0
{
151
0
    // For any versions of Glib before 2.36, g_type_init must be explicitly called
152
0
    // to safely use the library. Failure to do so may cause various failures/crashes
153
0
    // in any code that uses Glib, Gdk, or Gtk. In later versions of Glib, this call
154
0
    // is a no-op.
155
0
    g_type_init();
156
0
157
0
    mozilla::hal::Init();
158
0
159
0
#ifdef MOZ_ENABLE_DBUS
160
0
    if (XRE_IsParentProcess()) {
161
0
        nsCOMPtr<nsIPowerManagerService> powerManagerService =
162
0
            do_GetService(POWERMANAGERSERVICE_CONTRACTID);
163
0
164
0
        if (powerManagerService) {
165
0
            powerManagerService->AddWakeLockListener(WakeLockListener::GetSingleton());
166
0
        } else {
167
0
            NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
168
0
        }
169
0
    }
170
0
#endif
171
0
172
0
    if (!sPollFunc) {
173
0
        sPollFunc = g_main_context_get_poll_func(nullptr);
174
0
        g_main_context_set_poll_func(nullptr, &PollWrapper);
175
0
    }
176
0
177
0
    if (XRE_IsParentProcess()) {
178
0
        ScreenManager& screenManager = ScreenManager::GetSingleton();
179
0
        if (gfxPlatform::IsHeadless()) {
180
0
            screenManager.SetHelper(mozilla::MakeUnique<HeadlessScreenHelper>());
181
0
        } else {
182
0
            screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperGTK>());
183
0
        }
184
0
    }
185
0
186
0
    if (gtk_check_version(3, 16, 3) == nullptr) {
187
0
        // Before 3.16.3, GDK cannot override classname by --class command line
188
0
        // option when program uses gdk_set_program_class().
189
0
        //
190
0
        // See https://bugzilla.gnome.org/show_bug.cgi?id=747634
191
0
        nsAutoString brandName;
192
0
        mozilla::widget::WidgetUtils::GetBrandShortName(brandName);
193
0
        if (!brandName.IsEmpty()) {
194
0
            gdk_set_program_class(NS_ConvertUTF16toUTF8(brandName).get());
195
0
        }
196
0
    }
197
0
198
0
#ifdef MOZ_WIDGET_GTK
199
0
    if (!sReal_gtk_window_check_resize &&
200
0
        gtk_check_version(3,8,0) != nullptr) { // GTK 3.0 to GTK 3.6.
201
0
        // GtkWindow is a static class and so will leak anyway but this ref
202
0
        // makes sure it isn't recreated.
203
0
        gpointer gtk_plug_class = g_type_class_ref(GTK_TYPE_WINDOW);
204
0
        auto check_resize = &GTK_CONTAINER_CLASS(gtk_plug_class)->check_resize;
205
0
        sReal_gtk_window_check_resize = *check_resize;
206
0
        *check_resize = wrap_gtk_window_check_resize;
207
0
    }
208
0
209
0
    if (!sPendingResumeQuark &&
210
0
        gtk_check_version(3,14,7) != nullptr) { // GTK 3.0 to GTK 3.14.7.
211
0
        // GTK 3.8 - 3.14 registered this type when creating the frame clock
212
0
        // for the root window of the display when the display was opened.
213
0
        GType gdkFrameClockIdleType = g_type_from_name("GdkFrameClockIdle");
214
0
        if (gdkFrameClockIdleType) { // not in versions prior to 3.8
215
0
            sPendingResumeQuark = g_quark_from_string("moz-resume-is-pending");
216
0
            auto gdk_frame_clock_idle_class =
217
0
                G_OBJECT_CLASS(g_type_class_peek_static(gdkFrameClockIdleType));
218
0
            auto constructed = &gdk_frame_clock_idle_class->constructed;
219
0
            sRealGdkFrameClockConstructed = *constructed;
220
0
            *constructed = WrapGdkFrameClockConstructed;
221
0
            auto dispose = &gdk_frame_clock_idle_class->dispose;
222
0
            sRealGdkFrameClockDispose = *dispose;
223
0
            *dispose = WrapGdkFrameClockDispose;
224
0
        }
225
0
    }
226
0
227
0
    // Workaround for bug 1209659 which is fixed by Gtk3.20
228
0
    if (gtk_check_version(3, 20, 0) != nullptr)
229
0
        unsetenv("GTK_CSD");
230
0
#endif
231
0
232
0
    if (PR_GetEnv("MOZ_DEBUG_PAINTS"))
233
0
        gdk_window_set_debug_updates(TRUE);
234
0
235
0
    // Whitelist of only common, stable formats - see bugs 1197059 and 1203078
236
0
    GSList* pixbufFormats = gdk_pixbuf_get_formats();
237
0
    for (GSList* iter = pixbufFormats; iter; iter = iter->next) {
238
0
        GdkPixbufFormat* format = static_cast<GdkPixbufFormat*>(iter->data);
239
0
        gchar* name = gdk_pixbuf_format_get_name(format);
240
0
        if (strcmp(name, "jpeg") &&
241
0
            strcmp(name, "png") &&
242
0
            strcmp(name, "gif") &&
243
0
            strcmp(name, "bmp") &&
244
0
            strcmp(name, "ico") &&
245
0
            strcmp(name, "xpm") &&
246
0
            strcmp(name, "svg")) {
247
0
          gdk_pixbuf_format_set_disabled(format, TRUE);
248
0
        }
249
0
        g_free(name);
250
0
    }
251
0
    g_slist_free(pixbufFormats);
252
0
253
0
    int err = pipe(mPipeFDs);
254
0
    if (err)
255
0
        return NS_ERROR_OUT_OF_MEMORY;
256
0
257
0
    GIOChannel *ioc;
258
0
    GSource *source;
259
0
260
0
    // make the pipe nonblocking
261
0
262
0
    int flags = fcntl(mPipeFDs[0], F_GETFL, 0);
263
0
    if (flags == -1)
264
0
        goto failed;
265
0
    err = fcntl(mPipeFDs[0], F_SETFL, flags | O_NONBLOCK);
266
0
    if (err == -1)
267
0
        goto failed;
268
0
    flags = fcntl(mPipeFDs[1], F_GETFL, 0);
269
0
    if (flags == -1)
270
0
        goto failed;
271
0
    err = fcntl(mPipeFDs[1], F_SETFL, flags | O_NONBLOCK);
272
0
    if (err == -1)
273
0
        goto failed;
274
0
275
0
    ioc = g_io_channel_unix_new(mPipeFDs[0]);
276
0
    source = g_io_create_watch(ioc, G_IO_IN);
277
0
    g_io_channel_unref(ioc);
278
0
    g_source_set_callback(source, (GSourceFunc)EventProcessorCallback, this, nullptr);
279
0
    g_source_set_can_recurse(source, TRUE);
280
0
    mTag = g_source_attach(source, nullptr);
281
0
    g_source_unref(source);
282
0
283
0
    return nsBaseAppShell::Init();
284
0
failed:
285
0
    close(mPipeFDs[0]);
286
0
    close(mPipeFDs[1]);
287
0
    mPipeFDs[0] = mPipeFDs[1] = 0;
288
0
    return NS_ERROR_FAILURE;
289
0
}
290
291
void
292
nsAppShell::ScheduleNativeEventCallback()
293
0
{
294
0
    unsigned char buf[] = { NOTIFY_TOKEN };
295
0
    Unused << write(mPipeFDs[1], buf, 1);
296
0
}
297
298
bool
299
nsAppShell::ProcessNextNativeEvent(bool mayWait)
300
0
{
301
0
    return g_main_context_iteration(nullptr, mayWait);
302
0
}