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