Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/mozcontainer.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 "mozcontainer.h"
9
#include <gtk/gtk.h>
10
#ifdef MOZ_WAYLAND
11
#include <gdk/gdkx.h>
12
#include <gdk/gdkwayland.h>
13
#include <wayland-egl.h>
14
#endif
15
#include <stdio.h>
16
#include <dlfcn.h>
17
18
#ifdef ACCESSIBILITY
19
#include <atk/atk.h>
20
#include "maiRedundantObjectFactory.h"
21
#endif
22
23
/* init methods */
24
static void moz_container_class_init          (MozContainerClass *klass);
25
static void moz_container_init                (MozContainer      *container);
26
27
/* widget class methods */
28
static void moz_container_map                 (GtkWidget         *widget);
29
static void moz_container_unmap               (GtkWidget         *widget);
30
static void moz_container_realize             (GtkWidget         *widget);
31
static void moz_container_size_allocate       (GtkWidget         *widget,
32
                                               GtkAllocation     *allocation);
33
34
/* container class methods */
35
static void moz_container_remove      (GtkContainer      *container,
36
                                       GtkWidget         *child_widget);
37
static void moz_container_forall      (GtkContainer      *container,
38
                                       gboolean           include_internals,
39
                                       GtkCallback        callback,
40
                                       gpointer           callback_data);
41
static void moz_container_add         (GtkContainer      *container,
42
                                        GtkWidget        *widget);
43
44
typedef struct _MozContainerChild MozContainerChild;
45
46
struct _MozContainerChild {
47
    GtkWidget *widget;
48
    gint x;
49
    gint y;
50
};
51
52
static void moz_container_allocate_child (MozContainer      *container,
53
                                          MozContainerChild *child);
54
static MozContainerChild *
55
moz_container_get_child (MozContainer *container, GtkWidget *child);
56
57
/* public methods */
58
59
GType
60
moz_container_get_type(void)
61
0
{
62
0
    static GType moz_container_type = 0;
63
0
64
0
    if (!moz_container_type) {
65
0
        static GTypeInfo moz_container_info = {
66
0
            sizeof(MozContainerClass), /* class_size */
67
0
            NULL, /* base_init */
68
0
            NULL, /* base_finalize */
69
0
            (GClassInitFunc) moz_container_class_init, /* class_init */
70
0
            NULL, /* class_destroy */
71
0
            NULL, /* class_data */
72
0
            sizeof(MozContainer), /* instance_size */
73
0
            0, /* n_preallocs */
74
0
            (GInstanceInitFunc) moz_container_init, /* instance_init */
75
0
            NULL, /* value_table */
76
0
        };
77
0
78
0
        moz_container_type = g_type_register_static (GTK_TYPE_CONTAINER,
79
0
                                                     "MozContainer",
80
0
                                                     &moz_container_info,
81
0
                                                     static_cast<GTypeFlags>(0));
82
0
#ifdef ACCESSIBILITY
83
0
        /* Set a factory to return accessible object with ROLE_REDUNDANT for
84
0
         * MozContainer, so that gail won't send focus notification for it */
85
0
        atk_registry_set_factory_type(atk_get_default_registry(),
86
0
                                      moz_container_type,
87
0
                                      mai_redundant_object_factory_get_type());
88
0
#endif
89
0
    }
90
0
91
0
    return moz_container_type;
92
0
}
93
94
GtkWidget *
95
moz_container_new (void)
96
0
{
97
0
    MozContainer *container;
98
0
99
0
    container = static_cast<MozContainer*>(g_object_new (MOZ_CONTAINER_TYPE, nullptr));
100
0
101
0
    return GTK_WIDGET(container);
102
0
}
103
104
void
105
moz_container_put (MozContainer *container, GtkWidget *child_widget,
106
                   gint x, gint y)
107
0
{
108
0
    MozContainerChild *child;
109
0
110
0
    child = g_new (MozContainerChild, 1);
111
0
112
0
    child->widget = child_widget;
113
0
    child->x = x;
114
0
    child->y = y;
115
0
116
0
    /*  printf("moz_container_put %p %p %d %d\n", (void *)container,
117
0
        (void *)child_widget, x, y); */
118
0
119
0
    container->children = g_list_append (container->children, child);
120
0
121
0
    /* we assume that the caller of this function will have already set
122
0
       the parent GdkWindow because we can have many anonymous children. */
123
0
    gtk_widget_set_parent(child_widget, GTK_WIDGET(container));
124
0
}
125
126
void
127
moz_container_move (MozContainer *container, GtkWidget *child_widget,
128
                    gint x, gint y, gint width, gint height)
129
0
{
130
0
    MozContainerChild *child;
131
0
    GtkAllocation new_allocation;
132
0
133
0
    child = moz_container_get_child (container, child_widget);
134
0
135
0
    child->x = x;
136
0
    child->y = y;
137
0
138
0
    new_allocation.x = x;
139
0
    new_allocation.y = y;
140
0
    new_allocation.width = width;
141
0
    new_allocation.height = height;
142
0
143
0
    /* printf("moz_container_move %p %p will allocate to %d %d %d %d\n",
144
0
       (void *)container, (void *)child_widget,
145
0
       new_allocation.x, new_allocation.y,
146
0
       new_allocation.width, new_allocation.height); */
147
0
148
0
    gtk_widget_size_allocate(child_widget, &new_allocation);
149
0
}
150
151
/* static methods */
152
153
void
154
moz_container_class_init (MozContainerClass *klass)
155
0
{
156
0
    /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
157
0
      GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */
158
0
    GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
159
0
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
160
0
161
0
    widget_class->map = moz_container_map;
162
0
    widget_class->unmap = moz_container_unmap;
163
0
    widget_class->realize = moz_container_realize;
164
0
    widget_class->size_allocate = moz_container_size_allocate;
165
0
166
0
    container_class->remove = moz_container_remove;
167
0
    container_class->forall = moz_container_forall;
168
0
    container_class->add = moz_container_add;
169
0
}
170
171
#if defined(MOZ_WAYLAND)
172
static void
173
registry_handle_global (void *data,
174
                        struct wl_registry *registry,
175
                        uint32_t name,
176
                        const char *interface,
177
                        uint32_t version)
178
{
179
    MozContainer *container = MOZ_CONTAINER(data);
180
    if(strcmp(interface, "wl_subcompositor") == 0) {
181
        container->subcompositor =
182
            static_cast<wl_subcompositor*>(wl_registry_bind(registry,
183
                                           name,
184
                                           &wl_subcompositor_interface,
185
                                           1));
186
    }
187
}
188
189
static void
190
registry_handle_global_remove (void *data,
191
                               struct wl_registry *registry,
192
                               uint32_t name)
193
{
194
}
195
196
static const struct wl_registry_listener registry_listener = {
197
    registry_handle_global,
198
    registry_handle_global_remove
199
};
200
#endif
201
202
void
203
moz_container_init (MozContainer *container)
204
0
{
205
0
    gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE);
206
0
    gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE);
207
0
    gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE);
208
0
209
#if defined(MOZ_WAYLAND)
210
    {
211
      container->subcompositor = nullptr;
212
      container->surface = nullptr;
213
      container->subsurface = nullptr;
214
      container->eglwindow = nullptr;
215
      container->parent_surface_committed = false;
216
217
      GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container));
218
      if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
219
          // Available as of GTK 3.8+
220
          static auto sGdkWaylandDisplayGetWlDisplay =
221
              (wl_display *(*)(GdkDisplay *))
222
              dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
223
224
          wl_display* display = sGdkWaylandDisplayGetWlDisplay(gdk_display);
225
          wl_registry* registry = wl_display_get_registry(display);
226
          wl_registry_add_listener(registry, &registry_listener, container);
227
          wl_display_dispatch(display);
228
          wl_display_roundtrip(display);
229
        }
230
    }
231
#endif
232
}
233
234
#if defined(MOZ_WAYLAND)
235
static void
236
moz_container_commited_handler(GdkFrameClock *clock, MozContainer *container)
237
{
238
    container->parent_surface_committed = true;
239
    g_signal_handler_disconnect(clock,
240
                                container->parent_surface_committed_handler);
241
    container->parent_surface_committed_handler = 0;
242
}
243
244
/* We want to draw to GdkWindow owned by mContainer from Compositor thread but
245
 * Gtk+ can be used in main thread only. So we create wayland wl_surface
246
 * and attach it as an overlay to GdkWindow.
247
 *
248
 * see gtk_clutter_embed_ensure_subsurface() at gtk-clutter-embed.c
249
 * for reference.
250
 */
251
static gboolean
252
moz_container_map_surface(MozContainer *container)
253
{
254
    // Available as of GTK 3.8+
255
    static auto sGdkWaylandDisplayGetWlCompositor =
256
        (wl_compositor *(*)(GdkDisplay *))
257
        dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_compositor");
258
    static auto sGdkWaylandWindowGetWlSurface =
259
        (wl_surface *(*)(GdkWindow *))
260
        dlsym(RTLD_DEFAULT, "gdk_wayland_window_get_wl_surface");
261
    static auto sGdkWindowGetFrameClock =
262
        (GdkFrameClock *(*)(GdkWindow *))
263
        dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock");
264
265
    GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container));
266
    if (GDK_IS_X11_DISPLAY(display))
267
        return false;
268
269
    if (container->subsurface && container->surface)
270
        return true;
271
272
    if (!container->parent_surface_committed) {
273
        if (!container->parent_surface_committed_handler) {
274
            GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
275
            GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
276
            container->parent_surface_committed_handler =
277
                g_signal_connect_after(clock, "after-paint",
278
                                       G_CALLBACK(moz_container_commited_handler),
279
                                       container);
280
        }
281
        return false;
282
    }
283
284
    if (!container->surface) {
285
        struct wl_compositor *compositor;
286
        compositor = sGdkWaylandDisplayGetWlCompositor(display);
287
        container->surface = wl_compositor_create_surface(compositor);
288
    }
289
290
    if (!container->subsurface) {
291
        GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
292
        wl_surface* gtk_surface = sGdkWaylandWindowGetWlSurface(window);
293
        if (!gtk_surface) {
294
          // We requested the underlying wl_surface too early when container
295
          // is not realized yet. We'll try again before first rendering
296
          // to mContainer.
297
          return false;
298
        }
299
300
        container->subsurface =
301
          wl_subcompositor_get_subsurface (container->subcompositor,
302
                                           container->surface,
303
                                           gtk_surface);
304
        gint x, y;
305
        gdk_window_get_position(window, &x, &y);
306
        wl_subsurface_set_position(container->subsurface, x, y);
307
        wl_subsurface_set_desync(container->subsurface);
308
309
        // Route input to parent wl_surface owned by Gtk+ so we get input
310
        // events from Gtk+.
311
        GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET (container));
312
        wl_compositor* compositor = sGdkWaylandDisplayGetWlCompositor(display);
313
        wl_region* region = wl_compositor_create_region(compositor);
314
        wl_surface_set_input_region(container->surface, region);
315
        wl_region_destroy(region);
316
    }
317
    return true;
318
}
319
320
static void
321
moz_container_unmap_surface(MozContainer *container)
322
{
323
    g_clear_pointer(&container->eglwindow, wl_egl_window_destroy);
324
    g_clear_pointer(&container->subsurface, wl_subsurface_destroy);
325
    g_clear_pointer(&container->surface, wl_surface_destroy);
326
327
    if (container->parent_surface_committed_handler) {
328
        static auto sGdkWindowGetFrameClock =
329
            (GdkFrameClock *(*)(GdkWindow *))
330
            dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock");
331
        GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
332
        GdkFrameClock *clock = sGdkWindowGetFrameClock(window);
333
334
        g_signal_handler_disconnect(clock,
335
                                    container->parent_surface_committed_handler);
336
        container->parent_surface_committed_handler = 0;
337
    }
338
    container->parent_surface_committed = false;
339
}
340
341
#endif
342
343
void
344
moz_container_map (GtkWidget *widget)
345
0
{
346
0
    MozContainer *container;
347
0
    GList *tmp_list;
348
0
    GtkWidget *tmp_child;
349
0
350
0
    g_return_if_fail (IS_MOZ_CONTAINER(widget));
351
0
    container = MOZ_CONTAINER (widget);
352
0
353
0
    gtk_widget_set_mapped(widget, TRUE);
354
0
355
0
    tmp_list = container->children;
356
0
    while (tmp_list) {
357
0
        tmp_child = ((MozContainerChild *)tmp_list->data)->widget;
358
0
359
0
        if (gtk_widget_get_visible(tmp_child)) {
360
0
            if (!gtk_widget_get_mapped(tmp_child))
361
0
                gtk_widget_map(tmp_child);
362
0
        }
363
0
        tmp_list = tmp_list->next;
364
0
    }
365
0
366
0
    if (gtk_widget_get_has_window (widget)) {
367
0
        gdk_window_show (gtk_widget_get_window(widget));
368
#if defined(MOZ_WAYLAND)
369
        moz_container_map_surface(MOZ_CONTAINER(widget));
370
#endif
371
    }
372
0
}
373
374
void
375
moz_container_unmap (GtkWidget *widget)
376
{
377
    g_return_if_fail (IS_MOZ_CONTAINER (widget));
378
379
    gtk_widget_set_mapped(widget, FALSE);
380
381
    if (gtk_widget_get_has_window (widget)) {
382
        gdk_window_hide (gtk_widget_get_window(widget));
383
#if defined(MOZ_WAYLAND)
384
        moz_container_unmap_surface(MOZ_CONTAINER(widget));
385
#endif
386
    }
387
}
388
389
void
390
moz_container_realize (GtkWidget *widget)
391
0
{
392
0
    GdkWindow *parent = gtk_widget_get_parent_window (widget);
393
0
    GdkWindow *window;
394
0
395
0
    gtk_widget_set_realized(widget, TRUE);
396
0
397
0
    if (gtk_widget_get_has_window (widget)) {
398
0
        GdkWindowAttr attributes;
399
0
        gint attributes_mask = GDK_WA_VISUAL | GDK_WA_X | GDK_WA_Y;
400
0
        GtkAllocation allocation;
401
0
402
0
        gtk_widget_get_allocation (widget, &allocation);
403
0
        attributes.event_mask = gtk_widget_get_events (widget);
404
0
        attributes.x = allocation.x;
405
0
        attributes.y = allocation.y;
406
0
        attributes.width = allocation.width;
407
0
        attributes.height = allocation.height;
408
0
        attributes.wclass = GDK_INPUT_OUTPUT;
409
0
        attributes.visual = gtk_widget_get_visual (widget);
410
0
        attributes.window_type = GDK_WINDOW_CHILD;
411
0
412
0
        window = gdk_window_new (parent, &attributes, attributes_mask);
413
0
        gdk_window_set_user_data (window, widget);
414
0
    } else {
415
0
        window = parent;
416
0
        g_object_ref (window);
417
0
    }
418
0
419
0
    gtk_widget_set_window (widget, window);
420
0
421
0
}
422
423
void
424
moz_container_size_allocate (GtkWidget     *widget,
425
                             GtkAllocation *allocation)
426
0
{
427
0
    MozContainer   *container;
428
0
    GList          *tmp_list;
429
0
    GtkAllocation   tmp_allocation;
430
0
431
0
    g_return_if_fail (IS_MOZ_CONTAINER (widget));
432
0
433
0
    /*  printf("moz_container_size_allocate %p %d %d %d %d\n",
434
0
        (void *)widget,
435
0
        allocation->x,
436
0
        allocation->y,
437
0
        allocation->width,
438
0
        allocation->height); */
439
0
440
0
    /* short circuit if you can */
441
0
    container = MOZ_CONTAINER (widget);
442
0
    gtk_widget_get_allocation(widget, &tmp_allocation);
443
0
    if (!container->children &&
444
0
        tmp_allocation.x == allocation->x &&
445
0
        tmp_allocation.y == allocation->y &&
446
0
        tmp_allocation.width == allocation->width &&
447
0
        tmp_allocation.height == allocation->height) {
448
0
        return;
449
0
    }
450
0
451
0
    gtk_widget_set_allocation(widget, allocation);
452
0
453
0
    tmp_list = container->children;
454
0
455
0
    while (tmp_list) {
456
0
        MozContainerChild *child = static_cast<MozContainerChild*>(tmp_list->data);
457
0
458
0
        moz_container_allocate_child (container, child);
459
0
460
0
        tmp_list = tmp_list->next;
461
0
    }
462
0
463
0
    if (gtk_widget_get_has_window (widget) &&
464
0
        gtk_widget_get_realized (widget)) {
465
0
466
0
        gdk_window_move_resize(gtk_widget_get_window(widget),
467
0
                               allocation->x,
468
0
                               allocation->y,
469
0
                               allocation->width,
470
0
                               allocation->height);
471
0
    }
472
0
473
#if defined(MOZ_WAYLAND)
474
    // We need to position our subsurface according to GdkWindow
475
    // when offset changes (GdkWindow is maximized for instance).
476
    // see gtk-clutter-embed.c for reference.
477
    if (container->subsurface) {
478
        gint x, y;
479
        gdk_window_get_position(gtk_widget_get_window(widget), &x, &y);
480
        wl_subsurface_set_position(container->subsurface, x, y);
481
    }
482
    if (container->eglwindow) {
483
        wl_egl_window_resize(container->eglwindow,
484
                             allocation->width, allocation->height,
485
                             0, 0);
486
    }
487
#endif
488
}
489
490
void
491
moz_container_remove (GtkContainer *container, GtkWidget *child_widget)
492
{
493
    MozContainerChild *child;
494
    MozContainer *moz_container;
495
    GdkWindow* parent_window;
496
497
    g_return_if_fail (IS_MOZ_CONTAINER(container));
498
    g_return_if_fail (GTK_IS_WIDGET(child_widget));
499
500
    moz_container = MOZ_CONTAINER(container);
501
502
    child = moz_container_get_child (moz_container, child_widget);
503
    g_return_if_fail (child);
504
505
    /* gtk_widget_unparent will remove the parent window (as well as the
506
     * parent widget), but, in Mozilla's window hierarchy, the parent window
507
     * may need to be kept because it may be part of a GdkWindow sub-hierarchy
508
     * that is being moved to another MozContainer.
509
     *
510
     * (In a conventional GtkWidget hierarchy, GdkWindows being reparented
511
     * would have their own GtkWidget and that widget would be the one being
512
     * reparented.  In Mozilla's hierarchy, the parent_window needs to be
513
     * retained so that the GdkWindow sub-hierarchy is maintained.)
514
     */
515
    parent_window = gtk_widget_get_parent_window(child_widget);
516
    if (parent_window)
517
        g_object_ref(parent_window);
518
519
    gtk_widget_unparent(child_widget);
520
521
    if (parent_window) {
522
        /* The child_widget will always still exist because g_signal_emit,
523
         * which invokes this function, holds a reference.
524
         *
525
         * If parent_window is the container's root window then it will not be
526
         * the parent_window if the child_widget is placed in another
527
         * container.
528
         */
529
        if (parent_window != gtk_widget_get_window(GTK_WIDGET(container)))
530
            gtk_widget_set_parent_window(child_widget, parent_window);
531
532
        g_object_unref(parent_window);
533
    }
534
535
    moz_container->children = g_list_remove(moz_container->children, child);
536
    g_free(child);
537
}
538
539
void
540
moz_container_forall (GtkContainer *container, gboolean include_internals,
541
                      GtkCallback  callback, gpointer callback_data)
542
{
543
    MozContainer *moz_container;
544
    GList *tmp_list;
545
546
    g_return_if_fail (IS_MOZ_CONTAINER(container));
547
    g_return_if_fail (callback != NULL);
548
549
    moz_container = MOZ_CONTAINER(container);
550
551
    tmp_list = moz_container->children;
552
    while (tmp_list) {
553
        MozContainerChild *child;
554
        child = static_cast<MozContainerChild*>(tmp_list->data);
555
        tmp_list = tmp_list->next;
556
        (* callback) (child->widget, callback_data);
557
    }
558
}
559
560
static void
561
moz_container_allocate_child (MozContainer *container,
562
                              MozContainerChild *child)
563
0
{
564
0
    GtkAllocation  allocation;
565
0
566
0
    gtk_widget_get_allocation (child->widget, &allocation);
567
0
    allocation.x = child->x;
568
0
    allocation.y = child->y;
569
0
570
0
    gtk_widget_size_allocate (child->widget, &allocation);
571
0
}
572
573
MozContainerChild *
574
moz_container_get_child (MozContainer *container, GtkWidget *child_widget)
575
0
{
576
0
    GList *tmp_list;
577
0
578
0
    tmp_list = container->children;
579
0
    while (tmp_list) {
580
0
        MozContainerChild *child;
581
0
582
0
        child = static_cast<MozContainerChild*>(tmp_list->data);
583
0
        tmp_list = tmp_list->next;
584
0
585
0
        if (child->widget == child_widget)
586
0
            return child;
587
0
    }
588
0
589
0
    return NULL;
590
0
}
591
592
static void
593
moz_container_add(GtkContainer *container, GtkWidget *widget)
594
0
{
595
0
    moz_container_put(MOZ_CONTAINER(container), widget, 0, 0);
596
0
}
597
598
#ifdef MOZ_WAYLAND
599
struct wl_surface*
600
moz_container_get_wl_surface(MozContainer *container)
601
{
602
    if (!container->subsurface || !container->surface) {
603
        GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
604
        if (!gdk_window_is_visible(window))
605
            return nullptr;
606
607
        moz_container_map_surface(container);
608
        // Set the scale factor for the buffer right after we create it.
609
        if (container->surface) {
610
            static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*))
611
            dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor");
612
            if (sGdkWindowGetScaleFactorPtr && window) {
613
              gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window);
614
              wl_surface_set_buffer_scale(container->surface, scaleFactor);
615
            }
616
        }
617
    }
618
619
    return container->surface;
620
}
621
622
struct wl_egl_window *
623
moz_container_get_wl_egl_window(MozContainer *container)
624
{
625
    if (!container->eglwindow) {
626
        struct wl_surface *wlsurf = moz_container_get_wl_surface(container);
627
        if (!wlsurf)
628
            return nullptr;
629
630
      GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(container));
631
      container->eglwindow
632
            = wl_egl_window_create(wlsurf,
633
                                   gdk_window_get_width(window),
634
                                   gdk_window_get_height(window));
635
    }
636
    return container->eglwindow;
637
}
638
639
gboolean
640
moz_container_has_wl_egl_window(MozContainer *container)
641
{
642
    return container->eglwindow ? true : false;
643
}
644
#endif