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  * MozContainerWayland is a wrapper over MozContainer which provides
9  * wl_surface for MozContainer widget.
10  *
11  * The widget scheme looks like:
12  *
13  *   ---------------------------------------------------------
14  *  |  mShell Gtk widget (contains wl_surface owned by Gtk+)  |
15  *  |                                                         |
16  *  |  ---------------------------------------------------    |
17  *  | | mContainer (contains wl_surface owned by Gtk+)    |   |
18  *  | |                                                   |   |
19  *  | |  ---------------------------------------------    |   |
20  *  | | | wl_subsurface (attached to wl_surface       |   |   |
21  *  | | |                of mContainer)               |   |   |
22  *  | | |                                             |   |   |
23  *  | | |                                             |   |   |
24  *  | |  ---------------------------------------------    |   |
25  *  |  ---------------------------------------------------    |
26  *   ---------------------------------------------------------
27  *
28  *  We draw to wl_subsurface owned by MozContainerWayland.
29  *  We need to wait until wl_surface of mContainer is created
30  *  and then we create and attach our wl_subsurface to it.
31  *
32  *  First wl_subsurface creation has these steps:
33  *
34  *  1) moz_container_wayland_size_allocate() handler is called when
35  *     mContainer size/position is known.
36  *     It calls moz_container_wayland_surface_create_locked(), registers
37  *     a frame callback handler
38  *     moz_container_wayland_frame_callback_handler().
39  *
40  *  2) moz_container_wayland_frame_callback_handler() is called
41  *     when wl_surface owned by mozContainer is ready.
42  *     We call initial_draw_cbs() handler and we can create our wl_subsurface
43  *     on top of wl_surface owned by mozContainer.
44  *
45  *  When MozContainer hides/show again, moz_container_wayland_size_allocate()
46  *  handler may not be called as MozContainer size is set. So after first
47  *  show/hide sequence use moz_container_wayland_map_event() to create
48  *  wl_subsurface of MozContainer.
49  */
50 
51 #include "MozContainer.h"
52 
53 #include <dlfcn.h>
54 #include <glib.h>
55 #include <stdio.h>
56 #include <wayland-egl.h>
57 
58 #include "mozilla/gfx/gfxVars.h"
59 #include "mozilla/StaticPrefs_widget.h"
60 #include "nsGtkUtils.h"
61 #include "nsWaylandDisplay.h"
62 #include "base/task.h"
63 
64 #ifdef MOZ_LOGGING
65 
66 #  include "mozilla/Logging.h"
67 #  include "nsTArray.h"
68 #  include "Units.h"
69 #  include "nsWindow.h"
70 extern mozilla::LazyLogModule gWidgetWaylandLog;
71 #  define LOGWAYLAND(...) \
72     MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
73 #else
74 #  define LOGWAYLAND(...)
75 #endif /* MOZ_LOGGING */
76 
77 using namespace mozilla;
78 using namespace mozilla::widget;
79 
80 /* init methods */
81 static void moz_container_wayland_destroy(GtkWidget* widget);
82 
83 /* widget class methods */
84 static void moz_container_wayland_map(GtkWidget* widget);
85 static gboolean moz_container_wayland_map_event(GtkWidget* widget,
86                                                 GdkEventAny* event);
87 static void moz_container_wayland_unmap(GtkWidget* widget);
88 static void moz_container_wayland_size_allocate(GtkWidget* widget,
89                                                 GtkAllocation* allocation);
90 static bool moz_container_wayland_surface_create_locked(
91     const MutexAutoLock& aProofOfLock, MozContainer* container);
92 static void moz_container_wayland_set_opaque_region_locked(
93     const MutexAutoLock& aProofOfLock, MozContainer* container);
94 
95 // Imlemented in MozContainer.cpp
96 void moz_container_realize(GtkWidget* widget);
97 
98 // Invalidate gtk wl_surface to commit changes to wl_subsurface.
99 // wl_subsurface changes are effective when parent surface is commited.
moz_container_wayland_invalidate(MozContainer * container)100 static void moz_container_wayland_invalidate(MozContainer* container) {
101   LOGWAYLAND("moz_container_wayland_invalidate [%p]\n",
102              (void*)moz_container_get_nsWindow(container));
103 
104   GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
105   if (!window) {
106     LOGWAYLAND("    Failed - missing GdkWindow!\n");
107     return;
108   }
109   gdk_window_invalidate_rect(window, nullptr, true);
110 }
111 
112 // Route input to parent wl_surface owned by Gtk+ so we get input
113 // events from Gtk+.
moz_container_clear_input_region(MozContainer * container)114 static void moz_container_clear_input_region(MozContainer* container) {
115   struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor();
116   MozContainerWayland* wl_container = &container->wl_container;
117   wl_region* region = wl_compositor_create_region(compositor);
118   wl_surface_set_input_region(wl_container->surface, region);
119   wl_region_destroy(region);
120 }
121 
moz_container_wayland_move_locked(const MutexAutoLock & aProofOfLock,MozContainer * container,int dx,int dy)122 static void moz_container_wayland_move_locked(const MutexAutoLock& aProofOfLock,
123                                               MozContainer* container, int dx,
124                                               int dy) {
125   LOGWAYLAND("moz_container_wayland_move [%p] %d,%d\n",
126              (void*)moz_container_get_nsWindow(container), dx, dy);
127 
128   MozContainerWayland* wl_container = &container->wl_container;
129   if (!wl_container->subsurface || (wl_container->subsurface_dx == dx &&
130                                     wl_container->subsurface_dy == dy)) {
131     return;
132   }
133 
134   wl_container->subsurface_dx = dx;
135   wl_container->subsurface_dy = dy;
136   wl_subsurface_set_position(wl_container->subsurface,
137                              wl_container->subsurface_dx,
138                              wl_container->subsurface_dy);
139 }
140 
141 // This is called from layout/compositor code only with
142 // size equal to GL rendering context. Otherwise there are
143 // rendering artifacts as wl_egl_window size does not match
144 // GL rendering pipeline setup.
moz_container_wayland_egl_window_set_size(MozContainer * container,int width,int height)145 void moz_container_wayland_egl_window_set_size(MozContainer* container,
146                                                int width, int height) {
147   MozContainerWayland* wl_container = &container->wl_container;
148   MutexAutoLock lock(*wl_container->container_lock);
149   if (wl_container->eglwindow) {
150     wl_egl_window_resize(wl_container->eglwindow, width, height, 0, 0);
151   }
152 }
153 
moz_container_wayland_class_init(MozContainerClass * klass)154 void moz_container_wayland_class_init(MozContainerClass* klass) {
155   /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
156     GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */
157   GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
158 
159   widget_class->map = moz_container_wayland_map;
160   widget_class->map_event = moz_container_wayland_map_event;
161   widget_class->destroy = moz_container_wayland_destroy;
162   widget_class->unmap = moz_container_wayland_unmap;
163   widget_class->realize = moz_container_realize;
164   widget_class->size_allocate = moz_container_wayland_size_allocate;
165 }
166 
moz_container_wayland_init(MozContainerWayland * container)167 void moz_container_wayland_init(MozContainerWayland* container) {
168   container->surface = nullptr;
169   container->subsurface = nullptr;
170   container->eglwindow = nullptr;
171   container->frame_callback_handler = nullptr;
172   container->viewport = nullptr;
173   container->ready_to_draw = false;
174   container->opaque_region_needs_updates = false;
175   container->opaque_region_corner_radius = 0;
176   container->opaque_region_used = false;
177   container->subsurface_dx = 0;
178   container->subsurface_dy = 0;
179   container->before_first_size_alloc = true;
180   container->buffer_scale = 1;
181   container->initial_draw_cbs.clear();
182   container->container_lock = new mozilla::Mutex("MozContainer lock");
183   container->commit_to_parent = false;
184 }
185 
moz_container_wayland_destroy(GtkWidget * widget)186 static void moz_container_wayland_destroy(GtkWidget* widget) {
187   MozContainerWayland* container = &MOZ_CONTAINER(widget)->wl_container;
188   if (!container->container_lock) {
189     // moz_container_wayland_init was not called - it's a hidden container.
190     return;
191   }
192   moz_container_wayland_clear_initial_draw_callback(MOZ_CONTAINER(widget));
193   delete container->container_lock;
194   container->container_lock = nullptr;
195 }
196 
moz_container_wayland_add_initial_draw_callback(MozContainer * container,const std::function<void (void)> & initial_draw_cb)197 void moz_container_wayland_add_initial_draw_callback(
198     MozContainer* container, const std::function<void(void)>& initial_draw_cb) {
199   MozContainerWayland* wl_container = &MOZ_CONTAINER(container)->wl_container;
200   {
201     MutexAutoLock lock(*container->wl_container.container_lock);
202     if (wl_container->ready_to_draw && !wl_container->surface) {
203       NS_WARNING(
204           "moz_container_wayland_add_initial_draw_callback: ready to draw "
205           "without wayland surface!");
206     }
207     if (!wl_container->ready_to_draw || !wl_container->surface) {
208       wl_container->initial_draw_cbs.push_back(initial_draw_cb);
209       return;
210     }
211   }
212 
213   // We're ready to draw as
214   // wl_container->ready_to_draw && wl_container->surface
215   // call the callback directly instead of store them.
216   initial_draw_cb();
217 }
218 
moz_container_wayland_clear_initial_draw_callback_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)219 static void moz_container_wayland_clear_initial_draw_callback_locked(
220     const MutexAutoLock& aProofOfLock, MozContainer* container) {
221   MozContainerWayland* wl_container = &MOZ_CONTAINER(container)->wl_container;
222   g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy);
223   wl_container->initial_draw_cbs.clear();
224 }
225 
moz_container_wayland_clear_initial_draw_callback(MozContainer * container)226 void moz_container_wayland_clear_initial_draw_callback(
227     MozContainer* container) {
228   MutexAutoLock lock(*container->wl_container.container_lock);
229   moz_container_wayland_clear_initial_draw_callback_locked(lock, container);
230 }
231 
moz_container_wayland_frame_callback_handler(void * data,struct wl_callback * callback,uint32_t time)232 static void moz_container_wayland_frame_callback_handler(
233     void* data, struct wl_callback* callback, uint32_t time) {
234   MozContainerWayland* wl_container = &MOZ_CONTAINER(data)->wl_container;
235 
236   LOGWAYLAND(
237       "%s [%p] frame_callback_handler %p ready_to_draw %d (set to true)"
238       " initial_draw callback %zd\n",
239       __FUNCTION__, (void*)moz_container_get_nsWindow(MOZ_CONTAINER(data)),
240       (void*)wl_container->frame_callback_handler, wl_container->ready_to_draw,
241       wl_container->initial_draw_cbs.size());
242 
243   std::vector<std::function<void(void)>> cbs;
244   {
245     // Protect mozcontainer internals changes by container_lock.
246     MutexAutoLock lock(*wl_container->container_lock);
247     g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy);
248     if (wl_container->ready_to_draw) {
249       return;
250     }
251     wl_container->ready_to_draw = true;
252     MOZ_DIAGNOSTIC_ASSERT(wl_container->surface,
253                           "Should have surface if we're ready to draw");
254     cbs = std::move(wl_container->initial_draw_cbs);
255   }
256 
257   // Call the callbacks registered by
258   // moz_container_wayland_add_initial_draw_callback().
259   // and we can't do that under mozcontainer lock.
260   for (auto const& cb : cbs) {
261     cb();
262   }
263 }
264 
265 static const struct wl_callback_listener moz_container_frame_listener = {
266     moz_container_wayland_frame_callback_handler};
267 
after_frame_clock_after_paint(GdkFrameClock * clock,MozContainer * container)268 static void after_frame_clock_after_paint(GdkFrameClock* clock,
269                                           MozContainer* container) {
270   struct wl_surface* surface = moz_container_wayland_surface_lock(container);
271   if (surface) {
272     wl_surface_commit(surface);
273     moz_container_wayland_surface_unlock(container, &surface);
274   }
275 }
276 
moz_gdk_wayland_window_add_frame_callback_surface_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)277 static bool moz_gdk_wayland_window_add_frame_callback_surface_locked(
278     const MutexAutoLock& aProofOfLock, MozContainer* container) {
279   static auto sGdkWaylandWindowAddCallbackSurface =
280       (void (*)(GdkWindow*, struct wl_surface*))dlsym(
281           RTLD_DEFAULT, "gdk_wayland_window_add_frame_callback_surface");
282 
283   if (!StaticPrefs::widget_wayland_opaque_region_enabled_AtStartup() ||
284       !sGdkWaylandWindowAddCallbackSurface) {
285     return false;
286   }
287 
288   GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
289   MozContainerWayland* wl_container = &container->wl_container;
290 
291   sGdkWaylandWindowAddCallbackSurface(window, wl_container->surface);
292 
293   GdkFrameClock* frame_clock = gdk_window_get_frame_clock(window);
294   g_signal_connect_after(frame_clock, "after-paint",
295                          G_CALLBACK(after_frame_clock_after_paint), container);
296   return true;
297 }
298 
moz_gdk_wayland_window_remove_frame_callback_surface_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)299 static void moz_gdk_wayland_window_remove_frame_callback_surface_locked(
300     const MutexAutoLock& aProofOfLock, MozContainer* container) {
301   static auto sGdkWaylandWindowRemoveCallbackSurface =
302       (void (*)(GdkWindow*, struct wl_surface*))dlsym(
303           RTLD_DEFAULT, "gdk_wayland_window_remove_frame_callback_surface");
304 
305   if (!sGdkWaylandWindowRemoveCallbackSurface) {
306     return;
307   }
308 
309   GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
310   MozContainerWayland* wl_container = &container->wl_container;
311 
312   if (wl_container->surface) {
313     sGdkWaylandWindowRemoveCallbackSurface(window, wl_container->surface);
314   }
315 
316   GdkFrameClock* frame_clock = gdk_window_get_frame_clock(window);
317   g_signal_handlers_disconnect_by_func(
318       frame_clock, FuncToGpointer(after_frame_clock_after_paint), container);
319 }
320 
moz_container_wayland_unmap_internal(MozContainer * container)321 static void moz_container_wayland_unmap_internal(MozContainer* container) {
322   MozContainerWayland* wl_container = &container->wl_container;
323   MutexAutoLock lock(*wl_container->container_lock);
324 
325   LOGWAYLAND("%s [%p]\n", __FUNCTION__,
326              (void*)moz_container_get_nsWindow(container));
327 
328   moz_container_wayland_clear_initial_draw_callback_locked(lock, container);
329 
330   if (wl_container->opaque_region_used) {
331     moz_gdk_wayland_window_remove_frame_callback_surface_locked(lock,
332                                                                 container);
333   }
334   if (wl_container->commit_to_parent) {
335     wl_container->surface = nullptr;
336   }
337 
338   g_clear_pointer(&wl_container->eglwindow, wl_egl_window_destroy);
339   g_clear_pointer(&wl_container->subsurface, wl_subsurface_destroy);
340   g_clear_pointer(&wl_container->surface, wl_surface_destroy);
341   g_clear_pointer(&wl_container->viewport, wp_viewport_destroy);
342 
343   wl_container->ready_to_draw = false;
344   wl_container->buffer_scale = 1;
345 }
346 
moz_container_wayland_map_event(GtkWidget * widget,GdkEventAny * event)347 static gboolean moz_container_wayland_map_event(GtkWidget* widget,
348                                                 GdkEventAny* event) {
349   MozContainerWayland* wl_container = &MOZ_CONTAINER(widget)->wl_container;
350   MutexAutoLock lock(*wl_container->container_lock);
351 
352   LOGWAYLAND("%s [%p]\n", __FUNCTION__,
353              (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)));
354 
355   // Don't create wl_subsurface in map_event when it's already created or
356   // if we create it for the first time.
357   if (wl_container->ready_to_draw || wl_container->before_first_size_alloc) {
358     return FALSE;
359   }
360 
361   if (!wl_container->surface) {
362     if (!moz_container_wayland_surface_create_locked(lock,
363                                                      MOZ_CONTAINER(widget))) {
364       return FALSE;
365     }
366   }
367 
368   moz_container_wayland_set_scale_factor_locked(MOZ_CONTAINER(widget));
369   moz_container_wayland_set_opaque_region_locked(lock, MOZ_CONTAINER(widget));
370   moz_container_clear_input_region(MOZ_CONTAINER(widget));
371   moz_container_wayland_invalidate(MOZ_CONTAINER(widget));
372   return FALSE;
373 }
374 
moz_container_wayland_map(GtkWidget * widget)375 void moz_container_wayland_map(GtkWidget* widget) {
376   LOGWAYLAND("%s [%p]\n", __FUNCTION__,
377              (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)));
378 
379   g_return_if_fail(IS_MOZ_CONTAINER(widget));
380   gtk_widget_set_mapped(widget, TRUE);
381 
382   if (gtk_widget_get_has_window(widget)) {
383     gdk_window_show(gtk_widget_get_window(widget));
384   }
385 }
386 
moz_container_wayland_unmap(GtkWidget * widget)387 void moz_container_wayland_unmap(GtkWidget* widget) {
388   LOGWAYLAND("%s [%p]\n", __FUNCTION__,
389              (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)));
390 
391   g_return_if_fail(IS_MOZ_CONTAINER(widget));
392   gtk_widget_set_mapped(widget, FALSE);
393 
394   if (gtk_widget_get_has_window(widget)) {
395     gdk_window_hide(gtk_widget_get_window(widget));
396     moz_container_wayland_unmap_internal(MOZ_CONTAINER(widget));
397   }
398 }
399 
moz_container_wayland_size_allocate(GtkWidget * widget,GtkAllocation * allocation)400 void moz_container_wayland_size_allocate(GtkWidget* widget,
401                                          GtkAllocation* allocation) {
402   MozContainer* container;
403   GtkAllocation tmp_allocation;
404 
405   g_return_if_fail(IS_MOZ_CONTAINER(widget));
406 
407   LOGWAYLAND("moz_container_wayland_size_allocate [%p] %d,%d -> %d x %d\n",
408              (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)),
409              allocation->x, allocation->y, allocation->width,
410              allocation->height);
411 
412   /* short circuit if you can */
413   container = MOZ_CONTAINER(widget);
414   gtk_widget_get_allocation(widget, &tmp_allocation);
415   if (!container->children && tmp_allocation.x == allocation->x &&
416       tmp_allocation.y == allocation->y &&
417       tmp_allocation.width == allocation->width &&
418       tmp_allocation.height == allocation->height) {
419     return;
420   }
421   gtk_widget_set_allocation(widget, allocation);
422 
423   if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) {
424     gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x,
425                            allocation->y, allocation->width,
426                            allocation->height);
427     // We need to position our subsurface according to GdkWindow
428     // when offset changes (GdkWindow is maximized for instance).
429     // see gtk-clutter-embed.c for reference.
430     MutexAutoLock lock(*container->wl_container.container_lock);
431     if (!container->wl_container.surface) {
432       if (!moz_container_wayland_surface_create_locked(lock, container)) {
433         return;
434       }
435     }
436     moz_container_wayland_set_scale_factor_locked(container);
437     moz_container_wayland_set_opaque_region_locked(lock, container);
438     moz_container_wayland_move_locked(lock, container, allocation->x,
439                                       allocation->y);
440     moz_container_clear_input_region(container);
441     moz_container_wayland_invalidate(MOZ_CONTAINER(widget));
442     container->wl_container.before_first_size_alloc = false;
443   }
444 }
445 
moz_container_wayland_create_opaque_region(int aX,int aY,int aWidth,int aHeight,int aCornerRadius)446 static wl_region* moz_container_wayland_create_opaque_region(
447     int aX, int aY, int aWidth, int aHeight, int aCornerRadius) {
448   struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor();
449   wl_region* region = wl_compositor_create_region(compositor);
450   wl_region_add(region, aX, aY, aWidth, aHeight);
451   if (aCornerRadius) {
452     wl_region_subtract(region, aX, aY, aCornerRadius, aCornerRadius);
453     wl_region_subtract(region, aX + aWidth - aCornerRadius, aY, aCornerRadius,
454                        aCornerRadius);
455   }
456   return region;
457 }
458 
moz_container_wayland_set_opaque_region_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)459 static void moz_container_wayland_set_opaque_region_locked(
460     const MutexAutoLock& aProofOfLock, MozContainer* container) {
461   MozContainerWayland* wl_container = &container->wl_container;
462 
463   if (!wl_container->opaque_region_needs_updates) {
464     return;
465   }
466 
467   if (!wl_container->opaque_region_used) {
468     wl_container->opaque_region_needs_updates = false;
469     return;
470   }
471 
472   GtkAllocation allocation;
473   gtk_widget_get_allocation(GTK_WIDGET(container), &allocation);
474 
475   wl_region* region = moz_container_wayland_create_opaque_region(
476       0, 0, allocation.width, allocation.height,
477       wl_container->opaque_region_corner_radius);
478   wl_surface_set_opaque_region(wl_container->surface, region);
479   wl_region_destroy(region);
480   wl_container->opaque_region_needs_updates = false;
481 }
482 
moz_container_wayland_set_opaque_region(MozContainer * container)483 static void moz_container_wayland_set_opaque_region(MozContainer* container) {
484   MozContainerWayland* wl_container = &container->wl_container;
485   MutexAutoLock lock(*wl_container->container_lock);
486   if (wl_container->surface) {
487     moz_container_wayland_set_opaque_region_locked(lock, container);
488   }
489 }
490 
moz_container_wayland_set_scale_factor_locked(MozContainer * container)491 void moz_container_wayland_set_scale_factor_locked(MozContainer* container) {
492   if (gfx::gfxVars::UseWebRenderCompositor()) {
493     // the compositor backend handles scaling itself
494     return;
495   }
496 
497   MozContainerWayland* wl_container = &container->wl_container;
498   wl_container->container_lock->AssertCurrentThreadOwns();
499 
500   nsWindow* window = moz_container_get_nsWindow(container);
501 
502   if (window && window->UseFractionalScale()) {
503     if (!wl_container->viewport) {
504       wl_container->viewport = wp_viewporter_get_viewport(
505           WaylandDisplayGet()->GetViewporter(), wl_container->surface);
506     }
507 
508     GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(container));
509     wp_viewport_set_destination(wl_container->viewport,
510                                 gdk_window_get_width(gdkWindow),
511                                 gdk_window_get_height(gdkWindow));
512   } else {
513     int scale = window ? window->GdkCeiledScaleFactor() : 1;
514 
515     if (scale == wl_container->buffer_scale) {
516       return;
517     }
518 
519     LOGWAYLAND("%s [%p] scale %d\n", __FUNCTION__,
520                (void*)moz_container_get_nsWindow(container), scale);
521     wl_surface_set_buffer_scale(wl_container->surface, scale);
522     wl_container->buffer_scale = scale;
523   }
524 }
525 
moz_container_wayland_set_scale_factor(MozContainer * container)526 void moz_container_wayland_set_scale_factor(MozContainer* container) {
527   MutexAutoLock lock(*container->wl_container.container_lock);
528   if (container->wl_container.surface) {
529     moz_container_wayland_set_scale_factor_locked(container);
530   }
531 }
532 
moz_container_wayland_surface_create_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)533 static bool moz_container_wayland_surface_create_locked(
534     const MutexAutoLock& aProofOfLock, MozContainer* container) {
535   MozContainerWayland* wl_container = &container->wl_container;
536 
537   LOGWAYLAND("%s [%p]\n", __FUNCTION__,
538              (void*)moz_container_get_nsWindow(container));
539 
540   GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
541   wl_surface* parent_surface = gdk_wayland_window_get_wl_surface(window);
542   if (!parent_surface) {
543     LOGWAYLAND("    Failed - missing parent surface!");
544     return false;
545   }
546   LOGWAYLAND("    gtk wl_surface %p ID %d\n", (void*)parent_surface,
547              wl_proxy_get_id((struct wl_proxy*)parent_surface));
548 
549   if (wl_container->commit_to_parent) {
550     LOGWAYLAND("    commit to parent");
551     wl_container->surface = parent_surface;
552     NS_DispatchToCurrentThread(NewRunnableFunction(
553         "moz_container_wayland_frame_callback_handler",
554         &moz_container_wayland_frame_callback_handler, container, nullptr, 0));
555     return true;
556   }
557 
558   // Available as of GTK 3.8+
559   struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor();
560   wl_container->surface = wl_compositor_create_surface(compositor);
561   if (!wl_container->surface) {
562     LOGWAYLAND("    Failed - can't create surface!");
563     return false;
564   }
565 
566   wl_container->subsurface =
567       wl_subcompositor_get_subsurface(WaylandDisplayGet()->GetSubcompositor(),
568                                       wl_container->surface, parent_surface);
569   if (!wl_container->subsurface) {
570     g_clear_pointer(&wl_container->surface, wl_surface_destroy);
571     LOGWAYLAND("    Failed - can't create sub-surface!");
572     return false;
573   }
574   wl_subsurface_set_desync(wl_container->subsurface);
575 
576   // Try to guess subsurface offset to avoid potential flickering.
577   int dx, dy;
578   if (moz_container_get_nsWindow(container)->GetCSDDecorationOffset(&dx, &dy)) {
579     wl_container->subsurface_dx = dx;
580     wl_container->subsurface_dy = dy;
581     wl_subsurface_set_position(wl_container->subsurface, dx, dy);
582     LOGWAYLAND("    guessing subsurface position %d %d\n", dx, dy);
583   }
584 
585   // If there's pending frame callback it's for wrong parent surface,
586   // so delete it.
587   if (wl_container->frame_callback_handler) {
588     g_clear_pointer(&wl_container->frame_callback_handler, wl_callback_destroy);
589   }
590   wl_container->frame_callback_handler = wl_surface_frame(parent_surface);
591   wl_callback_add_listener(wl_container->frame_callback_handler,
592                            &moz_container_frame_listener, container);
593   LOGWAYLAND(
594       "    created frame callback ID %d\n",
595       wl_proxy_get_id((struct wl_proxy*)wl_container->frame_callback_handler));
596 
597   wl_surface_commit(wl_container->surface);
598   wl_display_flush(WaylandDisplayGet()->GetDisplay());
599 
600   wl_container->opaque_region_used =
601       moz_gdk_wayland_window_add_frame_callback_surface_locked(aProofOfLock,
602                                                                container);
603 
604   LOGWAYLAND("    created surface %p ID %d\n", (void*)wl_container->surface,
605              wl_proxy_get_id((struct wl_proxy*)wl_container->surface));
606   return true;
607 }
608 
moz_container_wayland_surface_lock(MozContainer * container)609 struct wl_surface* moz_container_wayland_surface_lock(MozContainer* container) {
610   // LOGWAYLAND("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__,
611   //           (void*)container, (void*)container->wl_container.surface,
612   //           container->wl_container.ready_to_draw);
613   if (!container->wl_container.surface ||
614       !container->wl_container.ready_to_draw) {
615     return nullptr;
616   }
617   container->wl_container.container_lock->Lock();
618   return container->wl_container.surface;
619 }
620 
moz_container_wayland_surface_unlock(MozContainer * container,struct wl_surface ** surface)621 void moz_container_wayland_surface_unlock(MozContainer* container,
622                                           struct wl_surface** surface) {
623   // Temporarily disabled to avoid log noise
624   // LOGWAYLAND("%s [%p] surface %p\n", __FUNCTION__, (void*)container,
625   //            (void*)container->wl_container.surface);
626   if (*surface) {
627     container->wl_container.container_lock->Unlock();
628     *surface = nullptr;
629   }
630 }
631 
moz_container_wayland_get_surface_locked(const MutexAutoLock & aProofOfLock,MozContainer * container)632 struct wl_surface* moz_container_wayland_get_surface_locked(
633     const MutexAutoLock& aProofOfLock, MozContainer* container) {
634   LOGWAYLAND("%s [%p] surface %p ready_to_draw %d\n", __FUNCTION__,
635              (void*)moz_container_get_nsWindow(container),
636              (void*)container->wl_container.surface,
637              container->wl_container.ready_to_draw);
638   if (!container->wl_container.surface ||
639       !container->wl_container.ready_to_draw) {
640     return nullptr;
641   }
642   moz_container_wayland_set_scale_factor_locked(container);
643   return container->wl_container.surface;
644 }
645 
moz_container_wayland_lock(MozContainer * container)646 void moz_container_wayland_lock(MozContainer* container) {
647   container->wl_container.container_lock->Lock();
648 }
649 
moz_container_wayland_unlock(MozContainer * container)650 void moz_container_wayland_unlock(MozContainer* container) {
651   container->wl_container.container_lock->Unlock();
652 }
653 
moz_container_wayland_get_egl_window(MozContainer * container,double scale)654 struct wl_egl_window* moz_container_wayland_get_egl_window(
655     MozContainer* container, double scale) {
656   MozContainerWayland* wl_container = &container->wl_container;
657 
658   LOGWAYLAND("%s [%p] eglwindow %p\n", __FUNCTION__,
659              (void*)moz_container_get_nsWindow(container),
660              (void*)wl_container->eglwindow);
661 
662   MutexAutoLock lock(*wl_container->container_lock);
663   if (!wl_container->surface || !wl_container->ready_to_draw) {
664     return nullptr;
665   }
666   if (!wl_container->eglwindow) {
667     GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
668     wl_container->eglwindow = wl_egl_window_create(
669         wl_container->surface, (int)round(gdk_window_get_width(window) * scale),
670         (int)round(gdk_window_get_height(window) * scale));
671 
672     LOGWAYLAND("%s [%p] created eglwindow %p\n", __FUNCTION__,
673                (void*)moz_container_get_nsWindow(container),
674                (void*)wl_container->eglwindow);
675   }
676   return wl_container->eglwindow;
677 }
678 
moz_container_wayland_has_egl_window(MozContainer * container)679 gboolean moz_container_wayland_has_egl_window(MozContainer* container) {
680   return container->wl_container.eglwindow != nullptr;
681 }
682 
moz_container_wayland_update_opaque_region(MozContainer * container,int corner_radius)683 void moz_container_wayland_update_opaque_region(MozContainer* container,
684                                                 int corner_radius) {
685   MozContainerWayland* wl_container = &container->wl_container;
686   wl_container->opaque_region_needs_updates = true;
687   wl_container->opaque_region_corner_radius = corner_radius;
688 
689   // When GL compositor / WebRender is used,
690   // moz_container_wayland_get_egl_window() is called only once when window
691   // is created or resized so update opaque region now.
692   if (moz_container_wayland_has_egl_window(container)) {
693     moz_container_wayland_set_opaque_region(container);
694   }
695 }
696 
moz_container_wayland_can_draw(MozContainer * container)697 gboolean moz_container_wayland_can_draw(MozContainer* container) {
698   MozContainerWayland* wl_container = &container->wl_container;
699   MutexAutoLock lock(*wl_container->container_lock);
700   return wl_container->ready_to_draw;
701 }
702 
moz_container_wayland_get_scale(MozContainer * container)703 double moz_container_wayland_get_scale(MozContainer* container) {
704   nsWindow* window = moz_container_get_nsWindow(container);
705   return window ? window->FractionalScaleFactor() : 1;
706 }
707 
moz_container_wayland_set_commit_to_parent(MozContainer * container)708 void moz_container_wayland_set_commit_to_parent(MozContainer* container) {
709   MozContainerWayland* wl_container = &container->wl_container;
710   MOZ_DIAGNOSTIC_ASSERT(!wl_container->surface);
711   wl_container->commit_to_parent = true;
712 }
713 
moz_container_wayland_is_commiting_to_parent(MozContainer * container)714 bool moz_container_wayland_is_commiting_to_parent(MozContainer* container) {
715   MozContainerWayland* wl_container = &container->wl_container;
716   return wl_container->commit_to_parent;
717 }
718