Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/WidgetStyleCache.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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
#include <dlfcn.h>
8
#include <gtk/gtk.h>
9
#include "WidgetStyleCache.h"
10
#include "gtkdrawing.h"
11
12
0
#define STATE_FLAG_DIR_LTR (1U << 7)
13
0
#define STATE_FLAG_DIR_RTL (1U << 8)
14
#if GTK_CHECK_VERSION(3,8,0)
15
static_assert(GTK_STATE_FLAG_DIR_LTR == STATE_FLAG_DIR_LTR &&
16
              GTK_STATE_FLAG_DIR_RTL == STATE_FLAG_DIR_RTL,
17
              "incorrect direction state flags");
18
#endif
19
20
static GtkWidget* sWidgetStorage[MOZ_GTK_WIDGET_NODE_COUNT];
21
static GtkStyleContext* sStyleStorage[MOZ_GTK_WIDGET_NODE_COUNT];
22
23
static GtkStyleContext*
24
GetWidgetRootStyle(WidgetNodeType aNodeType);
25
static GtkStyleContext*
26
GetCssNodeStyleInternal(WidgetNodeType aNodeType);
27
28
static GtkWidget*
29
CreateWindowWidget()
30
0
{
31
0
  GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
32
0
  gtk_widget_set_name(widget, "MozillaGtkWidget");
33
0
  return widget;
34
0
}
35
36
static GtkWidget*
37
CreateWindowContainerWidget()
38
0
{
39
0
  GtkWidget *widget = gtk_fixed_new();
40
0
  gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW)), widget);
41
0
  return widget;
42
0
}
43
44
static void
45
AddToWindowContainer(GtkWidget* widget)
46
0
{
47
0
  gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW_CONTAINER)), widget);
48
0
}
49
50
static GtkWidget*
51
CreateScrollbarWidget(WidgetNodeType aWidgetType, GtkOrientation aOrientation)
52
0
{
53
0
  GtkWidget* widget = gtk_scrollbar_new(aOrientation, nullptr);
54
0
  AddToWindowContainer(widget);
55
0
  return widget;
56
0
}
57
58
static GtkWidget*
59
CreateCheckboxWidget()
60
0
{
61
0
  GtkWidget* widget = gtk_check_button_new_with_label("M");
62
0
  AddToWindowContainer(widget);
63
0
  return widget;
64
0
}
65
66
static GtkWidget*
67
CreateRadiobuttonWidget()
68
0
{
69
0
  GtkWidget* widget = gtk_radio_button_new_with_label(nullptr, "M");
70
0
  AddToWindowContainer(widget);
71
0
  return widget;
72
0
}
73
74
static GtkWidget*
75
CreateMenuBarWidget()
76
0
{
77
0
  GtkWidget* widget = gtk_menu_bar_new();
78
0
  AddToWindowContainer(widget);
79
0
  return widget;
80
0
}
81
82
static GtkWidget*
83
CreateMenuPopupWidget()
84
0
{
85
0
  GtkWidget* widget = gtk_menu_new();
86
0
  gtk_menu_attach_to_widget(GTK_MENU(widget), GetWidget(MOZ_GTK_WINDOW),
87
0
                            nullptr);
88
0
  return widget;
89
0
}
90
91
static GtkWidget*
92
CreateProgressWidget()
93
0
{
94
0
  GtkWidget* widget = gtk_progress_bar_new();
95
0
  AddToWindowContainer(widget);
96
0
  return widget;
97
0
}
98
99
static GtkWidget*
100
CreateTooltipWidget()
101
0
{
102
0
  MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr,
103
0
             "CreateTooltipWidget should be used for Gtk < 3.20 only.");
104
0
  GtkWidget* widget = CreateWindowWidget();
105
0
  GtkStyleContext* style = gtk_widget_get_style_context(widget);
106
0
  gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP);
107
0
  return widget;
108
0
}
109
110
static GtkWidget*
111
CreateExpanderWidget()
112
0
{
113
0
  GtkWidget* widget = gtk_expander_new("M");
114
0
  AddToWindowContainer(widget);
115
0
  return widget;
116
0
}
117
118
static GtkWidget*
119
CreateFrameWidget()
120
0
{
121
0
  GtkWidget* widget = gtk_frame_new(nullptr);
122
0
  AddToWindowContainer(widget);
123
0
  return widget;
124
0
}
125
126
static GtkWidget*
127
CreateGripperWidget()
128
0
{
129
0
  GtkWidget* widget = gtk_handle_box_new();
130
0
  AddToWindowContainer(widget);
131
0
  return widget;
132
0
}
133
134
static GtkWidget*
135
CreateToolbarWidget()
136
0
{
137
0
  GtkWidget* widget = gtk_toolbar_new();
138
0
  gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_GRIPPER)), widget);
139
0
  return widget;
140
0
}
141
142
static GtkWidget*
143
CreateToolbarSeparatorWidget()
144
0
{
145
0
  GtkWidget* widget = GTK_WIDGET(gtk_separator_tool_item_new());
146
0
  AddToWindowContainer(widget);
147
0
  return widget;
148
0
}
149
150
static GtkWidget*
151
CreateInfoBarWidget()
152
0
{
153
0
  GtkWidget* widget = gtk_info_bar_new();
154
0
  AddToWindowContainer(widget);
155
0
  return widget;
156
0
}
157
158
static GtkWidget*
159
CreateButtonWidget()
160
0
{
161
0
  GtkWidget* widget = gtk_button_new_with_label("M");
162
0
  AddToWindowContainer(widget);
163
0
  return widget;
164
0
}
165
166
static GtkWidget*
167
CreateToggleButtonWidget()
168
0
{
169
0
  GtkWidget* widget = gtk_toggle_button_new();
170
0
  AddToWindowContainer(widget);
171
0
  return widget;
172
0
}
173
174
static GtkWidget*
175
CreateButtonArrowWidget()
176
0
{
177
0
  GtkWidget* widget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
178
0
  gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_TOGGLE_BUTTON)), widget);
179
0
  gtk_widget_show(widget);
180
0
  return widget;
181
0
}
182
183
static GtkWidget*
184
CreateSpinWidget()
185
0
{
186
0
  GtkWidget* widget = gtk_spin_button_new(nullptr, 1, 0);
187
0
  AddToWindowContainer(widget);
188
0
  return widget;
189
0
}
190
191
static GtkWidget*
192
CreateEntryWidget()
193
0
{
194
0
  GtkWidget* widget = gtk_entry_new();
195
0
  AddToWindowContainer(widget);
196
0
  return widget;
197
0
}
198
199
static GtkWidget*
200
CreateComboBoxWidget()
201
0
{
202
0
  GtkWidget* widget = gtk_combo_box_new();
203
0
  AddToWindowContainer(widget);
204
0
  return widget;
205
0
}
206
207
typedef struct
208
{
209
  GType       type;
210
  GtkWidget** widget;
211
} GtkInnerWidgetInfo;
212
213
static void
214
GetInnerWidget(GtkWidget* widget, gpointer client_data)
215
0
{
216
0
  auto info = static_cast<GtkInnerWidgetInfo*>(client_data);
217
0
218
0
  if (G_TYPE_CHECK_INSTANCE_TYPE(widget, info->type)) {
219
0
    *info->widget = widget;
220
0
  }
221
0
}
222
223
static GtkWidget*
224
CreateComboBoxButtonWidget()
225
0
{
226
0
  GtkWidget* comboBox = GetWidget(MOZ_GTK_COMBOBOX);
227
0
  GtkWidget* comboBoxButton = nullptr;
228
0
229
0
  /* Get its inner Button */
230
0
  GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON,
231
0
                              &comboBoxButton };
232
0
  gtk_container_forall(GTK_CONTAINER(comboBox),
233
0
                       GetInnerWidget, &info);
234
0
235
0
  if (!comboBoxButton) {
236
0
    /* Shouldn't be reached with current internal gtk implementation; we
237
0
     * use a generic toggle button as last resort fallback to avoid
238
0
     * crashing. */
239
0
    comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
240
0
  } else {
241
0
    /* We need to have pointers to the inner widgets (button, separator, arrow)
242
0
     * of the ComboBox to get the correct rendering from theme engines which
243
0
     * special cases their look. Since the inner layout can change, we ask GTK
244
0
     * to NULL our pointers when they are about to become invalid because the
245
0
     * corresponding widgets don't exist anymore. It's the role of
246
0
     * g_object_add_weak_pointer().
247
0
     * Note that if we don't find the inner widgets (which shouldn't happen), we
248
0
     * fallback to use generic "non-inner" widgets, and they don't need that kind
249
0
     * of weak pointer since they are explicit children of gProtoLayout and as
250
0
     * such GTK holds a strong reference to them. */
251
0
    g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
252
0
                              reinterpret_cast<gpointer *>(sWidgetStorage) +
253
0
                              MOZ_GTK_COMBOBOX_BUTTON);
254
0
  }
255
0
256
0
  return comboBoxButton;
257
0
}
258
259
static GtkWidget*
260
CreateComboBoxArrowWidget()
261
0
{
262
0
  GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON);
263
0
  GtkWidget* comboBoxArrow = nullptr;
264
0
265
0
  /* Get the widgets inside the Button */
266
0
  GtkWidget* buttonChild = gtk_bin_get_child(GTK_BIN(comboBoxButton));
267
0
  if (GTK_IS_BOX(buttonChild)) {
268
0
    /* appears-as-list = FALSE, cell-view = TRUE; the button
269
0
     * contains an hbox. This hbox is there because the ComboBox
270
0
     * needs to place a cell renderer, a separator, and an arrow in
271
0
     * the button when appears-as-list is FALSE. */
272
0
    GtkInnerWidgetInfo info = { GTK_TYPE_ARROW,
273
0
                                &comboBoxArrow };
274
0
    gtk_container_forall(GTK_CONTAINER(buttonChild),
275
0
                         GetInnerWidget, &info);
276
0
  } else if (GTK_IS_ARROW(buttonChild)) {
277
0
    /* appears-as-list = TRUE, or cell-view = FALSE;
278
0
     * the button only contains an arrow */
279
0
    comboBoxArrow = buttonChild;
280
0
  }
281
0
282
0
  if (!comboBoxArrow) {
283
0
    /* Shouldn't be reached with current internal gtk implementation;
284
0
     * we gButtonArrowWidget as last resort fallback to avoid
285
0
     * crashing. */
286
0
    comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
287
0
  } else {
288
0
    g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
289
0
                              reinterpret_cast<gpointer *>(sWidgetStorage) +
290
0
                              MOZ_GTK_COMBOBOX_ARROW);
291
0
  }
292
0
293
0
  return comboBoxArrow;
294
0
}
295
296
static GtkWidget*
297
CreateComboBoxSeparatorWidget()
298
0
{
299
0
  // Ensure to search for separator only once as it can fail
300
0
  // TODO - it won't initialize after ResetWidgetCache() call
301
0
  static bool isMissingSeparator = false;
302
0
  if (isMissingSeparator)
303
0
    return nullptr;
304
0
305
0
  /* Get the widgets inside the Button */
306
0
  GtkWidget* comboBoxSeparator = nullptr;
307
0
  GtkWidget* buttonChild =
308
0
    gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
309
0
  if (GTK_IS_BOX(buttonChild)) {
310
0
    /* appears-as-list = FALSE, cell-view = TRUE; the button
311
0
     * contains an hbox. This hbox is there because the ComboBox
312
0
     * needs to place a cell renderer, a separator, and an arrow in
313
0
     * the button when appears-as-list is FALSE. */
314
0
    GtkInnerWidgetInfo info = { GTK_TYPE_SEPARATOR,
315
0
                                &comboBoxSeparator };
316
0
    gtk_container_forall(GTK_CONTAINER(buttonChild),
317
0
                         GetInnerWidget, &info);
318
0
  }
319
0
320
0
  if (comboBoxSeparator) {
321
0
    g_object_add_weak_pointer(G_OBJECT(comboBoxSeparator),
322
0
                              reinterpret_cast<gpointer *>(sWidgetStorage) +
323
0
                              MOZ_GTK_COMBOBOX_SEPARATOR);
324
0
  } else {
325
0
    /* comboBoxSeparator may be NULL
326
0
     * when "appears-as-list" = TRUE or "cell-view" = FALSE;
327
0
     * if there is no separator, then we just won't paint it. */
328
0
    isMissingSeparator = true;
329
0
  }
330
0
331
0
  return comboBoxSeparator;
332
0
}
333
334
static GtkWidget*
335
CreateComboBoxEntryWidget()
336
0
{
337
0
  GtkWidget* widget = gtk_combo_box_new_with_entry();
338
0
  AddToWindowContainer(widget);
339
0
  return widget;
340
0
}
341
342
static GtkWidget*
343
CreateComboBoxEntryTextareaWidget()
344
0
{
345
0
  GtkWidget* comboBoxTextarea = nullptr;
346
0
347
0
  /* Get its inner Entry and Button */
348
0
  GtkInnerWidgetInfo info = { GTK_TYPE_ENTRY,
349
0
                              &comboBoxTextarea };
350
0
  gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
351
0
                       GetInnerWidget, &info);
352
0
353
0
  if (!comboBoxTextarea) {
354
0
    comboBoxTextarea = GetWidget(MOZ_GTK_ENTRY);
355
0
  } else {
356
0
    g_object_add_weak_pointer(G_OBJECT(comboBoxTextarea),
357
0
                              reinterpret_cast<gpointer *>(sWidgetStorage) +
358
0
                              MOZ_GTK_COMBOBOX_ENTRY);
359
0
  }
360
0
361
0
  return comboBoxTextarea;
362
0
}
363
364
static GtkWidget*
365
CreateComboBoxEntryButtonWidget()
366
0
{
367
0
  GtkWidget* comboBoxButton = nullptr;
368
0
369
0
  /* Get its inner Entry and Button */
370
0
  GtkInnerWidgetInfo info = { GTK_TYPE_TOGGLE_BUTTON,
371
0
                              &comboBoxButton };
372
0
  gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
373
0
                       GetInnerWidget, &info);
374
0
375
0
  if (!comboBoxButton) {
376
0
    comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
377
0
  } else {
378
0
    g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
379
0
                              reinterpret_cast<gpointer *>(sWidgetStorage) +
380
0
                              MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
381
0
  }
382
0
383
0
  return comboBoxButton;
384
0
}
385
386
static GtkWidget*
387
CreateComboBoxEntryArrowWidget()
388
0
{
389
0
  GtkWidget* comboBoxArrow = nullptr;
390
0
391
0
  /* Get the Arrow inside the Button */
392
0
  GtkWidget* buttonChild =
393
0
    gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON)));
394
0
395
0
  if (GTK_IS_BOX(buttonChild)) {
396
0
   /* appears-as-list = FALSE, cell-view = TRUE; the button
397
0
     * contains an hbox. This hbox is there because the ComboBox
398
0
     * needs to place a cell renderer, a separator, and an arrow in
399
0
     * the button when appears-as-list is FALSE. */
400
0
    GtkInnerWidgetInfo info = { GTK_TYPE_ARROW,
401
0
                                &comboBoxArrow };
402
0
    gtk_container_forall(GTK_CONTAINER(buttonChild),
403
0
                         GetInnerWidget, &info);
404
0
  } else if (GTK_IS_ARROW(buttonChild)) {
405
0
    /* appears-as-list = TRUE, or cell-view = FALSE;
406
0
     * the button only contains an arrow */
407
0
    comboBoxArrow = buttonChild;
408
0
  }
409
0
410
0
  if (!comboBoxArrow) {
411
0
    /* Shouldn't be reached with current internal gtk implementation;
412
0
     * we gButtonArrowWidget as last resort fallback to avoid
413
0
     * crashing. */
414
0
    comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
415
0
  } else {
416
0
    g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
417
0
                              reinterpret_cast<gpointer *>(sWidgetStorage) +
418
0
                              MOZ_GTK_COMBOBOX_ENTRY_ARROW);
419
0
  }
420
0
421
0
  return comboBoxArrow;
422
0
}
423
424
static GtkWidget*
425
CreateScrolledWindowWidget()
426
0
{
427
0
  GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr);
428
0
  AddToWindowContainer(widget);
429
0
  return widget;
430
0
}
431
432
static GtkWidget*
433
CreateMenuSeparatorWidget()
434
0
{
435
0
  GtkWidget* widget = gtk_separator_menu_item_new();
436
0
  gtk_menu_shell_append(GTK_MENU_SHELL(GetWidget(MOZ_GTK_MENUPOPUP)),
437
0
                        widget);
438
0
  return widget;
439
0
}
440
441
static GtkWidget*
442
CreateTreeViewWidget()
443
0
{
444
0
  GtkWidget* widget = gtk_tree_view_new();
445
0
  AddToWindowContainer(widget);
446
0
  return widget;
447
0
}
448
449
static GtkWidget*
450
CreateTreeHeaderCellWidget()
451
0
{
452
0
  /*
453
0
   * Some GTK engines paint the first and last cell
454
0
   * of a TreeView header with a highlight.
455
0
   * Since we do not know where our widget will be relative
456
0
   * to the other buttons in the TreeView header, we must
457
0
   * paint it as a button that is between two others,
458
0
   * thus ensuring it is neither the first or last button
459
0
   * in the header.
460
0
   * GTK doesn't give us a way to do this explicitly,
461
0
   * so we must paint with a button that is between two
462
0
   * others.
463
0
   */
464
0
  GtkTreeViewColumn* firstTreeViewColumn;
465
0
  GtkTreeViewColumn* middleTreeViewColumn;
466
0
  GtkTreeViewColumn* lastTreeViewColumn;
467
0
468
0
  GtkWidget *treeView = GetWidget(MOZ_GTK_TREEVIEW);
469
0
470
0
  /* Create and append our three columns */
471
0
  firstTreeViewColumn = gtk_tree_view_column_new();
472
0
  gtk_tree_view_column_set_title(firstTreeViewColumn, "M");
473
0
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
474
0
                              firstTreeViewColumn);
475
0
476
0
  middleTreeViewColumn = gtk_tree_view_column_new();
477
0
  gtk_tree_view_column_set_title(middleTreeViewColumn, "M");
478
0
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
479
0
                              middleTreeViewColumn);
480
0
481
0
  lastTreeViewColumn = gtk_tree_view_column_new();
482
0
  gtk_tree_view_column_set_title(lastTreeViewColumn, "M");
483
0
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView),
484
0
                              lastTreeViewColumn);
485
0
486
0
  /* Use the middle column's header for our button */
487
0
  return gtk_tree_view_column_get_button(middleTreeViewColumn);
488
0
}
489
490
static GtkWidget*
491
CreateTreeHeaderSortArrowWidget()
492
0
{
493
0
  /* TODO, but it can't be NULL */
494
0
  GtkWidget* widget = gtk_button_new();
495
0
  AddToWindowContainer(widget);
496
0
  return widget;
497
0
}
498
499
static GtkWidget*
500
CreateHPanedWidget()
501
0
{
502
0
  GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
503
0
  AddToWindowContainer(widget);
504
0
  return widget;
505
0
}
506
507
static GtkWidget*
508
CreateVPanedWidget()
509
0
{
510
0
  GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
511
0
  AddToWindowContainer(widget);
512
0
  return widget;
513
0
}
514
515
static GtkWidget*
516
CreateScaleWidget(GtkOrientation aOrientation)
517
0
{
518
0
  GtkWidget* widget = gtk_scale_new(aOrientation, nullptr);
519
0
  AddToWindowContainer(widget);
520
0
  return widget;
521
0
}
522
523
static GtkWidget*
524
CreateNotebookWidget()
525
0
{
526
0
  GtkWidget* widget = gtk_notebook_new();
527
0
  AddToWindowContainer(widget);
528
0
  return widget;
529
0
}
530
531
static void
532
CreateHeaderBarWidget(WidgetNodeType aWidgetType)
533
0
{
534
0
  MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
535
0
             "GtkHeaderBar is only available on GTK 3.10+.");
536
0
  MOZ_ASSERT(sWidgetStorage[aWidgetType] == nullptr,
537
0
             "Header bar widget is already created!");
538
0
539
0
  static auto sGtkHeaderBarNewPtr = (GtkWidget* (*)())
540
0
    dlsym(RTLD_DEFAULT, "gtk_header_bar_new");
541
0
542
0
  GtkWidget* headerbar = sGtkHeaderBarNewPtr();
543
0
  sWidgetStorage[aWidgetType] = headerbar;
544
0
545
0
  GtkWidget *window = gtk_window_new(GTK_WINDOW_POPUP);
546
0
  GtkStyleContext* style = gtk_widget_get_style_context(window);
547
0
548
0
  if (aWidgetType == MOZ_GTK_HEADER_BAR_MAXIMIZED) {
549
0
    gtk_style_context_add_class(style, "maximized");
550
0
    MOZ_ASSERT(sWidgetStorage[MOZ_GTK_HEADERBAR_WINDOW_MAXIMIZED] == nullptr,
551
0
               "Window widget is already created!");
552
0
    sWidgetStorage[MOZ_GTK_HEADERBAR_WINDOW_MAXIMIZED] = window;
553
0
  } else {
554
0
    MOZ_ASSERT(sWidgetStorage[MOZ_GTK_HEADERBAR_WINDOW] == nullptr,
555
0
               "Window widget is already created!");
556
0
    sWidgetStorage[MOZ_GTK_HEADERBAR_WINDOW] = window;
557
0
  }
558
0
559
0
  // Headerbar has to be placed to window with csd or solid-csd style
560
0
  // to properly draw the decorated.
561
0
  GtkStyleContext* windowStyle = GetStyleContext(MOZ_GTK_WINDOW);
562
0
  bool solidDecorations =
563
0
      gtk_style_context_has_class(windowStyle, "solid-csd");
564
0
  gtk_style_context_add_class(style, solidDecorations ? "solid-csd" : "csd");
565
0
566
0
  GtkWidget *fixed = gtk_fixed_new();
567
0
  gtk_container_add(GTK_CONTAINER(window), fixed);
568
0
  gtk_container_add(GTK_CONTAINER(fixed), headerbar);
569
0
570
0
  // Emulate what create_titlebar() at gtkwindow.c does.
571
0
  style = gtk_widget_get_style_context(headerbar);
572
0
  gtk_style_context_add_class(style, "titlebar");
573
0
574
0
  // TODO: Define default-decoration titlebar style as workaround
575
0
  // to ensure the titlebar buttons does not overflow outside.
576
0
  // Recently the titlebar size is calculated as
577
0
  // tab size + titlebar border/padding (default-decoration has 6px padding
578
0
  // at default Adwaita theme).
579
0
  // We need to fix titlebar size calculation to also include
580
0
  // titlebar button sizes. (Bug 1419442)
581
0
  gtk_style_context_add_class(style, "default-decoration");
582
0
}
583
584
0
#define ICON_SCALE_VARIANTS 2
585
586
static void
587
LoadWidgetIconPixbuf(GtkWidget* aWidgetIcon)
588
0
{
589
0
  GtkStyleContext* style = gtk_widget_get_style_context(aWidgetIcon);
590
0
591
0
  const gchar *iconName;
592
0
  GtkIconSize gtkIconSize;
593
0
  gtk_image_get_icon_name(GTK_IMAGE(aWidgetIcon), &iconName, &gtkIconSize);
594
0
595
0
  gint iconWidth, iconHeight;
596
0
  gtk_icon_size_lookup(gtkIconSize, &iconWidth, &iconHeight);
597
0
598
0
  /* Those are available since Gtk+ 3.10 as well as GtkHeaderBar */
599
0
  static auto sGtkIconThemeLookupIconForScalePtr =
600
0
    (GtkIconInfo* (*)(GtkIconTheme *, const gchar *, gint, gint, GtkIconLookupFlags))
601
0
    dlsym(RTLD_DEFAULT, "gtk_icon_theme_lookup_icon_for_scale");
602
0
  static auto sGdkCairoSurfaceCreateFromPixbufPtr =
603
0
    (cairo_surface_t * (*)(const GdkPixbuf *, int, GdkWindow *))
604
0
    dlsym(RTLD_DEFAULT, "gdk_cairo_surface_create_from_pixbuf");
605
0
606
0
  for (int scale = 1; scale < ICON_SCALE_VARIANTS+1; scale++) {
607
0
    GtkIconInfo *gtkIconInfo =
608
0
      sGtkIconThemeLookupIconForScalePtr(gtk_icon_theme_get_default(),
609
0
                                         iconName,
610
0
                                         iconWidth,
611
0
                                         scale,
612
0
                                         (GtkIconLookupFlags)0);
613
0
614
0
    if (!gtkIconInfo) {
615
0
      // We miss the icon, nothing to do here.
616
0
      return;
617
0
    }
618
0
619
0
    gboolean unused;
620
0
    GdkPixbuf *iconPixbuf =
621
0
      gtk_icon_info_load_symbolic_for_context(gtkIconInfo, style,
622
0
                                              &unused, nullptr);
623
0
    g_object_unref(G_OBJECT(gtkIconInfo));
624
0
625
0
    cairo_surface_t* iconSurface =
626
0
      sGdkCairoSurfaceCreateFromPixbufPtr(iconPixbuf, scale, nullptr);
627
0
    g_object_unref(iconPixbuf);
628
0
629
0
    nsAutoCString surfaceName;
630
0
    surfaceName = nsPrintfCString("MozillaIconSurface%d", scale);
631
0
    g_object_set_data_full(G_OBJECT(aWidgetIcon), surfaceName.get(),
632
0
                          iconSurface,
633
0
                          (GDestroyNotify)cairo_surface_destroy);
634
0
  }
635
0
}
636
637
cairo_surface_t*
638
GetWidgetIconSurface(GtkWidget* aWidgetIcon, int aScale)
639
0
{
640
0
  if (aScale > ICON_SCALE_VARIANTS)
641
0
    aScale = ICON_SCALE_VARIANTS;
642
0
643
0
  nsAutoCString surfaceName;
644
0
  surfaceName = nsPrintfCString("MozillaIconSurface%d", aScale);
645
0
  return (cairo_surface_t*)
646
0
    g_object_get_data(G_OBJECT(aWidgetIcon), surfaceName.get());
647
0
}
648
649
static void
650
CreateHeaderBarButton(GtkWidget* aParentWidget,
651
                      WidgetNodeType aWidgetType)
652
0
{
653
0
  GtkWidget* widget = gtk_button_new();
654
0
655
0
  // We have to add button to widget hierarchy now to pick
656
0
  // right icon style at LoadWidgetIconPixbuf().
657
0
  if (GTK_IS_BOX(aParentWidget)) {
658
0
      gtk_box_pack_start(GTK_BOX(aParentWidget), widget, FALSE, FALSE, 0);
659
0
  } else {
660
0
      gtk_container_add(GTK_CONTAINER(aParentWidget), widget);
661
0
  }
662
0
663
0
  // We bypass GetWidget() here because we create all titlebar
664
0
  // buttons at once when a first one is requested.
665
0
  NS_ASSERTION(sWidgetStorage[aWidgetType] == nullptr,
666
0
               "Titlebar button is already created!");
667
0
  sWidgetStorage[aWidgetType] = widget;
668
0
669
0
  // We need to show the button widget now as GtkBox does not
670
0
  // place invisible widgets and we'll miss first-child/last-child
671
0
  // css selectors at the buttons otherwise.
672
0
  gtk_widget_show(widget);
673
0
674
0
  GtkStyleContext* style = gtk_widget_get_style_context(widget);
675
0
  gtk_style_context_add_class(style, "titlebutton");
676
0
677
0
  GtkWidget *image = nullptr;
678
0
  switch (aWidgetType) {
679
0
     case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
680
0
       gtk_style_context_add_class(style, "close");
681
0
       image = gtk_image_new_from_icon_name("window-close-symbolic",
682
0
                                           GTK_ICON_SIZE_MENU);
683
0
       break;
684
0
     case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
685
0
       gtk_style_context_add_class(style, "minimize");
686
0
       image = gtk_image_new_from_icon_name("window-minimize-symbolic",
687
0
                                            GTK_ICON_SIZE_MENU);
688
0
       break;
689
0
690
0
     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
691
0
       gtk_style_context_add_class(style, "maximize");
692
0
       image = gtk_image_new_from_icon_name("window-maximize-symbolic",
693
0
                                            GTK_ICON_SIZE_MENU);
694
0
       break;
695
0
696
0
     case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
697
0
       gtk_style_context_add_class(style, "maximize");
698
0
       image = gtk_image_new_from_icon_name("window-restore-symbolic",
699
0
                                            GTK_ICON_SIZE_MENU);
700
0
       break;
701
0
     default:
702
0
       break;
703
0
   }
704
0
705
0
   gtk_widget_set_valign(widget, GTK_ALIGN_CENTER);
706
0
   g_object_set(image, "use-fallback", TRUE, NULL);
707
0
   gtk_container_add(GTK_CONTAINER (widget), image);
708
0
709
0
   // We bypass GetWidget() here by explicit sWidgetStorage[] update so
710
0
   // invalidate the style as well as GetWidget() does.
711
0
   style = gtk_widget_get_style_context(image);
712
0
   gtk_style_context_invalidate(style);
713
0
714
0
   LoadWidgetIconPixbuf(image);
715
0
}
716
717
static bool
718
IsToolbarButtonEnabled(WidgetNodeType* aButtonLayout, int aButtonNums,
719
                       WidgetNodeType aWidgetType)
720
0
{
721
0
    for (int i = 0; i < aButtonNums; i++) {
722
0
      if (aButtonLayout[i] == aWidgetType) {
723
0
        return true;
724
0
      }
725
0
    }
726
0
    return false;
727
0
}
728
729
static void
730
CreateHeaderBarButtons()
731
0
{
732
0
  MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
733
0
             "GtkHeaderBar is only available on GTK 3.10+.");
734
0
735
0
  GtkWidget* headerBar = sWidgetStorage[MOZ_GTK_HEADER_BAR];
736
0
  MOZ_ASSERT(headerBar != nullptr,
737
0
             "We're missing header bar widget!");
738
0
739
0
  gint buttonSpacing = 6;
740
0
  g_object_get(headerBar, "spacing", &buttonSpacing, nullptr);
741
0
742
0
  GtkWidget *buttonBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, buttonSpacing);
743
0
  gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_HEADER_BAR)), buttonBox);
744
0
745
0
  WidgetNodeType buttonLayout[TOOLBAR_BUTTONS];
746
0
  int activeButtons =
747
0
      GetGtkHeaderBarButtonLayout(buttonLayout, TOOLBAR_BUTTONS);
748
0
749
0
  if (IsToolbarButtonEnabled(buttonLayout, activeButtons,
750
0
                             MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE)) {
751
0
    CreateHeaderBarButton(buttonBox, MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE);
752
0
  }
753
0
  if (IsToolbarButtonEnabled(buttonLayout, activeButtons,
754
0
                             MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE)) {
755
0
    CreateHeaderBarButton(buttonBox, MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE);
756
0
    // We don't pack "restore" headerbar button to box as it's an icon
757
0
    // placeholder. Pack it only to header bar to get correct style.
758
0
    CreateHeaderBarButton(GetWidget(MOZ_GTK_HEADER_BAR),
759
0
                          MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE);
760
0
  }
761
0
  if (IsToolbarButtonEnabled(buttonLayout, activeButtons,
762
0
                             MOZ_GTK_HEADER_BAR_BUTTON_CLOSE)) {
763
0
    CreateHeaderBarButton(buttonBox, MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
764
0
  }
765
0
}
766
767
static void
768
CreateHeaderBar()
769
0
{
770
0
  CreateHeaderBarWidget(MOZ_GTK_HEADER_BAR);
771
0
  CreateHeaderBarWidget(MOZ_GTK_HEADER_BAR_MAXIMIZED);
772
0
  CreateHeaderBarButtons();
773
0
}
774
775
static GtkWidget*
776
CreateWidget(WidgetNodeType aWidgetType)
777
{
778
  switch (aWidgetType) {
779
    case MOZ_GTK_WINDOW:
780
      return CreateWindowWidget();
781
    case MOZ_GTK_WINDOW_CONTAINER:
782
      return CreateWindowContainerWidget();
783
    case MOZ_GTK_CHECKBUTTON_CONTAINER:
784
      return CreateCheckboxWidget();
785
    case MOZ_GTK_PROGRESSBAR:
786
      return CreateProgressWidget();
787
    case MOZ_GTK_RADIOBUTTON_CONTAINER:
788
      return CreateRadiobuttonWidget();
789
    case MOZ_GTK_SCROLLBAR_HORIZONTAL:
790
      return CreateScrollbarWidget(aWidgetType,
791
                                   GTK_ORIENTATION_HORIZONTAL);
792
    case MOZ_GTK_SCROLLBAR_VERTICAL:
793
      return CreateScrollbarWidget(aWidgetType,
794
                                   GTK_ORIENTATION_VERTICAL);
795
    case MOZ_GTK_MENUBAR:
796
      return CreateMenuBarWidget();
797
    case MOZ_GTK_MENUPOPUP:
798
      return CreateMenuPopupWidget();
799
    case MOZ_GTK_MENUSEPARATOR:
800
      return CreateMenuSeparatorWidget();
801
    case MOZ_GTK_EXPANDER:
802
      return CreateExpanderWidget();
803
    case MOZ_GTK_FRAME:
804
      return CreateFrameWidget();
805
    case MOZ_GTK_GRIPPER:
806
      return CreateGripperWidget();
807
    case MOZ_GTK_TOOLBAR:
808
      return CreateToolbarWidget();
809
    case MOZ_GTK_TOOLBAR_SEPARATOR:
810
      return CreateToolbarSeparatorWidget();
811
    case MOZ_GTK_INFO_BAR:
812
      return CreateInfoBarWidget();
813
    case MOZ_GTK_SPINBUTTON:
814
      return CreateSpinWidget();
815
    case MOZ_GTK_BUTTON:
816
      return CreateButtonWidget();
817
    case MOZ_GTK_TOGGLE_BUTTON:
818
      return CreateToggleButtonWidget();
819
    case MOZ_GTK_BUTTON_ARROW:
820
      return CreateButtonArrowWidget();
821
    case MOZ_GTK_ENTRY:
822
      return CreateEntryWidget();
823
    case MOZ_GTK_SCROLLED_WINDOW:
824
      return CreateScrolledWindowWidget();
825
    case MOZ_GTK_TREEVIEW:
826
      return CreateTreeViewWidget();
827
    case MOZ_GTK_TREE_HEADER_CELL:
828
      return CreateTreeHeaderCellWidget();
829
    case MOZ_GTK_TREE_HEADER_SORTARROW:
830
      return CreateTreeHeaderSortArrowWidget();
831
    case MOZ_GTK_SPLITTER_HORIZONTAL:
832
      return CreateHPanedWidget();
833
    case MOZ_GTK_SPLITTER_VERTICAL:
834
      return CreateVPanedWidget();
835
    case MOZ_GTK_SCALE_HORIZONTAL:
836
      return CreateScaleWidget(GTK_ORIENTATION_HORIZONTAL);
837
    case MOZ_GTK_SCALE_VERTICAL:
838
      return CreateScaleWidget(GTK_ORIENTATION_VERTICAL);
839
    case MOZ_GTK_NOTEBOOK:
840
      return CreateNotebookWidget();
841
    case MOZ_GTK_COMBOBOX:
842
      return CreateComboBoxWidget();
843
    case MOZ_GTK_COMBOBOX_BUTTON:
844
      return CreateComboBoxButtonWidget();
845
    case MOZ_GTK_COMBOBOX_ARROW:
846
      return CreateComboBoxArrowWidget();
847
    case MOZ_GTK_COMBOBOX_SEPARATOR:
848
      return CreateComboBoxSeparatorWidget();
849
    case MOZ_GTK_COMBOBOX_ENTRY:
850
      return CreateComboBoxEntryWidget();
851
    case MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA:
852
      return CreateComboBoxEntryTextareaWidget();
853
    case MOZ_GTK_COMBOBOX_ENTRY_BUTTON:
854
      return CreateComboBoxEntryButtonWidget();
855
    case MOZ_GTK_COMBOBOX_ENTRY_ARROW:
856
      return CreateComboBoxEntryArrowWidget();
857
    case MOZ_GTK_HEADER_BAR:
858
    case MOZ_GTK_HEADER_BAR_MAXIMIZED:
859
    case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
860
    case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
861
    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
862
    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
863
      /* Create header bar widgets once and fill with child elements as we need
864
         the header bar fully configured to get a correct style */
865
      CreateHeaderBar();
866
      return sWidgetStorage[aWidgetType];
867
    default:
868
      /* Not implemented */
869
      return nullptr;
870
  }
871
}
872
873
GtkWidget*
874
GetWidget(WidgetNodeType aWidgetType)
875
0
{
876
0
  GtkWidget* widget = sWidgetStorage[aWidgetType];
877
0
  if (!widget) {
878
0
    widget = CreateWidget(aWidgetType);
879
0
    // Some widgets (MOZ_GTK_COMBOBOX_SEPARATOR for instance) may not be
880
0
    // available or implemented.
881
0
    if (!widget)
882
0
      return nullptr;
883
0
    // In GTK versions prior to 3.18, automatic invalidation of style contexts
884
0
    // for widgets was delayed until the next resize event.  Gecko however,
885
0
    // typically uses the style context before the resize event runs and so an
886
0
    // explicit invalidation may be required.  This is necessary if a style
887
0
    // property was retrieved before all changes were made to the style
888
0
    // context.  One such situation is where gtk_button_construct_child()
889
0
    // retrieves the style property "image-spacing" during construction of the
890
0
    // GtkButton, before its parent is set to provide inheritance of ancestor
891
0
    // properties.  More recent GTK versions do not need this, but do not
892
0
    // re-resolve until required and so invalidation does not trigger
893
0
    // unnecessary resolution in general.
894
0
    GtkStyleContext* style = gtk_widget_get_style_context(widget);
895
0
    gtk_style_context_invalidate(style);
896
0
897
0
    sWidgetStorage[aWidgetType] = widget;
898
0
  }
899
0
  return widget;
900
0
}
901
902
static void
903
AddStyleClassesFromStyle(GtkStyleContext* aDest, GtkStyleContext* aSrc)
904
0
{
905
0
  GList* classes = gtk_style_context_list_classes(aSrc);
906
0
  for (GList* link = classes; link; link = link->next) {
907
0
    gtk_style_context_add_class(aDest, static_cast<gchar*>(link->data));
908
0
  }
909
0
  g_list_free(classes);
910
0
}
911
912
GtkStyleContext*
913
CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle)
914
0
{
915
0
  static auto sGtkWidgetClassGetCSSName =
916
0
    reinterpret_cast<const char* (*)(GtkWidgetClass*)>
917
0
    (dlsym(RTLD_DEFAULT, "gtk_widget_class_get_css_name"));
918
0
919
0
  GtkWidgetClass *widgetClass = GTK_WIDGET_GET_CLASS(aWidget);
920
0
  const gchar* name = sGtkWidgetClassGetCSSName ?
921
0
    sGtkWidgetClassGetCSSName(widgetClass) : nullptr;
922
0
923
0
  GtkStyleContext *context =
924
0
    CreateCSSNode(name, aParentStyle, G_TYPE_FROM_CLASS(widgetClass));
925
0
926
0
  // Classes are stored on the style context instead of the path so that any
927
0
  // future gtk_style_context_save() will inherit classes on the head CSS
928
0
  // node, in the same way as happens when called on a style context owned by
929
0
  // a widget.
930
0
  //
931
0
  // Classes can be stored on a GtkCssNodeDeclaration and/or the path.
932
0
  // gtk_style_context_save() reuses the GtkCssNodeDeclaration, and appends a
933
0
  // new object to the path, without copying the classes from the old path
934
0
  // head.  The new head picks up classes from the GtkCssNodeDeclaration, but
935
0
  // not the path.  GtkWidgets store their classes on the
936
0
  // GtkCssNodeDeclaration, so make sure to add classes there.
937
0
  //
938
0
  // Picking up classes from the style context also means that
939
0
  // https://bugzilla.gnome.org/show_bug.cgi?id=767312, which can stop
940
0
  // gtk_widget_path_append_for_widget() from finding classes in GTK 3.20,
941
0
  // is not a problem.
942
0
  GtkStyleContext* widgetStyle = gtk_widget_get_style_context(aWidget);
943
0
  AddStyleClassesFromStyle(context, widgetStyle);
944
0
945
0
  // Release any floating reference on aWidget.
946
0
  g_object_ref_sink(aWidget);
947
0
  g_object_unref(aWidget);
948
0
949
0
  return context;
950
0
}
951
952
static GtkStyleContext*
953
CreateStyleForWidget(GtkWidget* aWidget, WidgetNodeType aParentType)
954
0
{
955
0
  return CreateStyleForWidget(aWidget, GetWidgetRootStyle(aParentType));
956
0
}
957
958
GtkStyleContext*
959
CreateCSSNode(const char* aName, GtkStyleContext* aParentStyle, GType aType)
960
0
{
961
0
  static auto sGtkWidgetPathIterSetObjectName =
962
0
    reinterpret_cast<void (*)(GtkWidgetPath *, gint, const char *)>
963
0
    (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_object_name"));
964
0
965
0
  GtkWidgetPath* path;
966
0
  if (aParentStyle) {
967
0
    path = gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle));
968
0
    // Copy classes from the parent style context to its corresponding node in
969
0
    // the path, because GTK will only match against ancestor classes if they
970
0
    // are on the path.
971
0
    GList* classes = gtk_style_context_list_classes(aParentStyle);
972
0
    for (GList* link = classes; link; link = link->next) {
973
0
      gtk_widget_path_iter_add_class(path, -1, static_cast<gchar*>(link->data));
974
0
    }
975
0
    g_list_free(classes);
976
0
  } else {
977
0
    path = gtk_widget_path_new();
978
0
  }
979
0
980
0
  gtk_widget_path_append_type(path, aType);
981
0
982
0
  if (sGtkWidgetPathIterSetObjectName) {
983
0
    (*sGtkWidgetPathIterSetObjectName)(path, -1, aName);
984
0
  }
985
0
986
0
  GtkStyleContext *context = gtk_style_context_new();
987
0
  gtk_style_context_set_path(context, path);
988
0
  gtk_style_context_set_parent(context, aParentStyle);
989
0
  gtk_widget_path_unref(path);
990
0
991
0
  // In GTK 3.4, gtk_render_* functions use |theming_engine| on the style
992
0
  // context without ensuring any style resolution sets it appropriately
993
0
  // in style_data_lookup(). e.g.
994
0
  // https://git.gnome.org/browse/gtk+/tree/gtk/gtkstylecontext.c?h=3.4.4#n3847
995
0
  //
996
0
  // That can result in incorrect drawing on first draw.  To work around this,
997
0
  // force a style look-up to set |theming_engine|.  It is sufficient to do
998
0
  // this only on context creation, instead of after every modification to the
999
0
  // context, because themes typically (Ambiance and oxygen-gtk, at least) set
1000
0
  // the "engine" property with the '*' selector.
1001
0
  if (GTK_MAJOR_VERSION == 3 && gtk_get_minor_version() < 6) {
1002
0
    GdkRGBA unused;
1003
0
    gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, &unused);
1004
0
  }
1005
0
1006
0
  return context;
1007
0
}
1008
1009
// Return a style context matching that of the root CSS node of a widget.
1010
// This is used by all GTK versions.
1011
static GtkStyleContext*
1012
GetWidgetRootStyle(WidgetNodeType aNodeType)
1013
0
{
1014
0
  GtkStyleContext* style = sStyleStorage[aNodeType];
1015
0
  if (style)
1016
0
    return style;
1017
0
1018
0
  switch (aNodeType) {
1019
0
    case MOZ_GTK_MENUBARITEM:
1020
0
      style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUBAR);
1021
0
      break;
1022
0
    case MOZ_GTK_MENUITEM:
1023
0
      style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUPOPUP);
1024
0
      break;
1025
0
    case MOZ_GTK_CHECKMENUITEM:
1026
0
      style = CreateStyleForWidget(gtk_check_menu_item_new(), MOZ_GTK_MENUPOPUP);
1027
0
      break;
1028
0
    case MOZ_GTK_RADIOMENUITEM:
1029
0
      style = CreateStyleForWidget(gtk_radio_menu_item_new(nullptr),
1030
0
                                   MOZ_GTK_MENUPOPUP);
1031
0
      break;
1032
0
    case MOZ_GTK_TEXT_VIEW:
1033
0
      style = CreateStyleForWidget(gtk_text_view_new(),
1034
0
                                   MOZ_GTK_SCROLLED_WINDOW);
1035
0
      break;
1036
0
    case MOZ_GTK_TOOLTIP:
1037
0
      if (gtk_check_version(3, 20, 0) != nullptr) {
1038
0
          // The tooltip style class is added first in CreateTooltipWidget()
1039
0
          // and transfered to style in CreateStyleForWidget().
1040
0
          GtkWidget* tooltipWindow = CreateTooltipWidget();
1041
0
          style = CreateStyleForWidget(tooltipWindow, nullptr);
1042
0
          gtk_widget_destroy(tooltipWindow); // Release GtkWindow self-reference.
1043
0
      } else {
1044
0
          // We create this from the path because GtkTooltipWindow is not public.
1045
0
          style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP);
1046
0
          gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
1047
0
      }
1048
0
      break;
1049
0
    case MOZ_GTK_TOOLTIP_BOX:
1050
0
      style = CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0),
1051
0
                                   MOZ_GTK_TOOLTIP);
1052
0
      break;
1053
0
    case MOZ_GTK_TOOLTIP_BOX_LABEL:
1054
0
      style = CreateStyleForWidget(gtk_label_new(nullptr),
1055
0
                                   MOZ_GTK_TOOLTIP_BOX);
1056
0
      break;
1057
0
    default:
1058
0
      GtkWidget* widget = GetWidget(aNodeType);
1059
0
      MOZ_ASSERT(widget);
1060
0
      return gtk_widget_get_style_context(widget);
1061
0
  }
1062
0
1063
0
  MOZ_ASSERT(style);
1064
0
  sStyleStorage[aNodeType] = style;
1065
0
  return style;
1066
0
}
1067
1068
static GtkStyleContext*
1069
CreateChildCSSNode(const char* aName, WidgetNodeType aParentNodeType)
1070
0
{
1071
0
  return CreateCSSNode(aName, GetCssNodeStyleInternal(aParentNodeType));
1072
0
}
1073
1074
// Create a style context equivalent to a saved root style context of
1075
// |aWidgetType| with |aStyleClass| as an additional class.  This is used to
1076
// produce a context equivalent to what GTK versions < 3.20 use for many
1077
// internal parts of widgets.
1078
static GtkStyleContext*
1079
CreateSubStyleWithClass(WidgetNodeType aWidgetType, const gchar* aStyleClass)
1080
0
{
1081
0
  static auto sGtkWidgetPathIterGetObjectName =
1082
0
    reinterpret_cast<const char* (*)(const GtkWidgetPath*, gint)>
1083
0
    (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_get_object_name"));
1084
0
1085
0
  GtkStyleContext* parentStyle = GetWidgetRootStyle(aWidgetType);
1086
0
1087
0
  // Create a new context that behaves like |parentStyle| would after
1088
0
  // gtk_style_context_save(parentStyle).
1089
0
  //
1090
0
  // Avoiding gtk_style_context_save() avoids the need to manage the
1091
0
  // restore, and a new context permits caching style resolution.
1092
0
  //
1093
0
  // gtk_style_context_save(context) changes the node hierarchy of |context|
1094
0
  // to add a new GtkCssNodeDeclaration that is a copy of its original node.
1095
0
  // The new node is a child of the original node, and so the new heirarchy is
1096
0
  // one level deeper.  The new node receives the same classes as the
1097
0
  // original, but any changes to the classes on |context| will change only
1098
0
  // the new node.  The new node inherits properties from the original node
1099
0
  // (which retains the original heirarchy and classes) and matches CSS rules
1100
0
  // with the new heirarchy and any changes to the classes.
1101
0
  //
1102
0
  // The change in hierarchy can produce some surprises in matching theme CSS
1103
0
  // rules (e.g. https://bugzilla.gnome.org/show_bug.cgi?id=761870#c2), but it
1104
0
  // is important here to produce the same behavior so that rules match the
1105
0
  // same widget parts in Gecko as they do in GTK.
1106
0
  //
1107
0
  // When using public GTK API to construct style contexts, a widget path is
1108
0
  // required.  CSS rules are not matched against the style context heirarchy
1109
0
  // but according to the heirarchy in the widget path.  The path that matches
1110
0
  // the same CSS rules as a saved context is like the path of |parentStyle|
1111
0
  // but with an extra copy of the head (last) object appended.  Setting
1112
0
  // |parentStyle| as the parent context provides the same inheritance of
1113
0
  // properties from the widget root node.
1114
0
  const GtkWidgetPath* parentPath = gtk_style_context_get_path(parentStyle);
1115
0
  const gchar* name = sGtkWidgetPathIterGetObjectName ?
1116
0
    sGtkWidgetPathIterGetObjectName(parentPath, -1) : nullptr;
1117
0
  GType objectType = gtk_widget_path_get_object_type(parentPath);
1118
0
1119
0
  GtkStyleContext* style = CreateCSSNode(name, parentStyle, objectType);
1120
0
1121
0
  // Start with the same classes on the new node as were on |parentStyle|.
1122
0
  // GTK puts no regions or junction_sides on widget root nodes, and so there
1123
0
  // is no need to copy these.
1124
0
  AddStyleClassesFromStyle(style, parentStyle);
1125
0
1126
0
  gtk_style_context_add_class(style, aStyleClass);
1127
0
  return style;
1128
0
}
1129
1130
/* GetCssNodeStyleInternal is used by Gtk >= 3.20 */
1131
static GtkStyleContext*
1132
GetCssNodeStyleInternal(WidgetNodeType aNodeType)
1133
0
{
1134
0
  GtkStyleContext* style = sStyleStorage[aNodeType];
1135
0
  if (style)
1136
0
    return style;
1137
0
1138
0
  switch (aNodeType) {
1139
0
    case MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL:
1140
0
      style = CreateChildCSSNode("contents",
1141
0
                                 MOZ_GTK_SCROLLBAR_HORIZONTAL);
1142
0
      break;
1143
0
    case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
1144
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
1145
0
                                 MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL);
1146
0
      break;
1147
0
    case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
1148
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
1149
0
                                 MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL);
1150
0
      break;
1151
0
    case MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL:
1152
0
      style = CreateChildCSSNode("contents",
1153
0
                                 MOZ_GTK_SCROLLBAR_VERTICAL);
1154
0
      break;
1155
0
    case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
1156
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
1157
0
                                 MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
1158
0
      break;
1159
0
    case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
1160
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
1161
0
                                 MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL);
1162
0
      break;
1163
0
    case MOZ_GTK_SCROLLBAR_BUTTON:
1164
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_BUTTON,
1165
0
                                 MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
1166
0
      break;
1167
0
    case MOZ_GTK_RADIOBUTTON:
1168
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO,
1169
0
                                 MOZ_GTK_RADIOBUTTON_CONTAINER);
1170
0
      break;
1171
0
    case MOZ_GTK_CHECKBUTTON:
1172
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK,
1173
0
                                 MOZ_GTK_CHECKBUTTON_CONTAINER);
1174
0
      break;
1175
0
    case MOZ_GTK_RADIOMENUITEM_INDICATOR:
1176
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO,
1177
0
                                 MOZ_GTK_RADIOMENUITEM);
1178
0
      break;
1179
0
    case MOZ_GTK_CHECKMENUITEM_INDICATOR:
1180
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK,
1181
0
                                 MOZ_GTK_CHECKMENUITEM);
1182
0
      break;
1183
0
    case MOZ_GTK_PROGRESS_TROUGH:
1184
0
      /* Progress bar background (trough) */
1185
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
1186
0
                                 MOZ_GTK_PROGRESSBAR);
1187
0
      break;
1188
0
    case MOZ_GTK_PROGRESS_CHUNK:
1189
0
      style = CreateChildCSSNode("progress",
1190
0
                                 MOZ_GTK_PROGRESS_TROUGH);
1191
0
      break;
1192
0
    case MOZ_GTK_GRIPPER:
1193
0
      // TODO - create from CSS node
1194
0
      style = CreateSubStyleWithClass(MOZ_GTK_GRIPPER,
1195
0
                                      GTK_STYLE_CLASS_GRIP);
1196
0
      break;
1197
0
    case MOZ_GTK_INFO_BAR:
1198
0
      // TODO - create from CSS node
1199
0
      style = CreateSubStyleWithClass(MOZ_GTK_INFO_BAR,
1200
0
                                      GTK_STYLE_CLASS_INFO);
1201
0
      break;
1202
0
    case MOZ_GTK_SPINBUTTON_ENTRY:
1203
0
      // TODO - create from CSS node
1204
0
      style = CreateSubStyleWithClass(MOZ_GTK_SPINBUTTON,
1205
0
                                      GTK_STYLE_CLASS_ENTRY);
1206
0
      break;
1207
0
    case MOZ_GTK_SCROLLED_WINDOW:
1208
0
      // TODO - create from CSS node
1209
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCROLLED_WINDOW,
1210
0
                                      GTK_STYLE_CLASS_FRAME);
1211
0
      break;
1212
0
    case MOZ_GTK_TEXT_VIEW_TEXT:
1213
0
    case MOZ_GTK_RESIZER:
1214
0
      style = CreateChildCSSNode("text", MOZ_GTK_TEXT_VIEW);
1215
0
      if (aNodeType == MOZ_GTK_RESIZER) {
1216
0
        // The "grip" class provides the correct builtin icon from
1217
0
        // gtk_render_handle().  The icon is drawn with shaded variants of
1218
0
        // the background color, and so a transparent background would lead to
1219
0
        // a transparent resizer.  gtk_render_handle() also uses the
1220
0
        // background color to draw a background, and so this style otherwise
1221
0
        // matches what is used in GtkTextView to match the background with
1222
0
        // textarea elements.
1223
0
        GdkRGBA color;
1224
0
        gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL,
1225
0
                                               &color);
1226
0
        if (color.alpha == 0.0) {
1227
0
          g_object_unref(style);
1228
0
          style = CreateStyleForWidget(gtk_text_view_new(),
1229
0
                                       MOZ_GTK_SCROLLED_WINDOW);
1230
0
        }
1231
0
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
1232
0
      }
1233
0
      break;
1234
0
    case MOZ_GTK_FRAME_BORDER:
1235
0
      style = CreateChildCSSNode("border", MOZ_GTK_FRAME);
1236
0
      break;
1237
0
    case MOZ_GTK_TREEVIEW_VIEW:
1238
0
      // TODO - create from CSS node
1239
0
      style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
1240
0
                                      GTK_STYLE_CLASS_VIEW);
1241
0
      break;
1242
0
    case MOZ_GTK_TREEVIEW_EXPANDER:
1243
0
      // TODO - create from CSS node
1244
0
      style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
1245
0
                                      GTK_STYLE_CLASS_EXPANDER);
1246
0
      break;
1247
0
    case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL:
1248
0
      style = CreateChildCSSNode("separator",
1249
0
                                 MOZ_GTK_SPLITTER_HORIZONTAL);
1250
0
      break;
1251
0
    case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL:
1252
0
      style = CreateChildCSSNode("separator",
1253
0
                                 MOZ_GTK_SPLITTER_VERTICAL);
1254
0
      break;
1255
0
    case MOZ_GTK_SCALE_CONTENTS_HORIZONTAL:
1256
0
      style = CreateChildCSSNode("contents",
1257
0
                                 MOZ_GTK_SCALE_HORIZONTAL);
1258
0
      break;
1259
0
    case MOZ_GTK_SCALE_CONTENTS_VERTICAL:
1260
0
      style = CreateChildCSSNode("contents",
1261
0
                                 MOZ_GTK_SCALE_VERTICAL);
1262
0
      break;
1263
0
    case MOZ_GTK_SCALE_TROUGH_HORIZONTAL:
1264
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
1265
0
                                 MOZ_GTK_SCALE_CONTENTS_HORIZONTAL);
1266
0
      break;
1267
0
    case MOZ_GTK_SCALE_TROUGH_VERTICAL:
1268
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
1269
0
                                 MOZ_GTK_SCALE_CONTENTS_VERTICAL);
1270
0
      break;
1271
0
    case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
1272
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
1273
0
                                 MOZ_GTK_SCALE_TROUGH_HORIZONTAL);
1274
0
      break;
1275
0
    case MOZ_GTK_SCALE_THUMB_VERTICAL:
1276
0
      style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
1277
0
                                 MOZ_GTK_SCALE_TROUGH_VERTICAL);
1278
0
      break;
1279
0
    case MOZ_GTK_TAB_TOP:
1280
0
    {
1281
0
      // TODO - create from CSS node
1282
0
      style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK,
1283
0
                                      GTK_STYLE_CLASS_TOP);
1284
0
      gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
1285
0
                                   static_cast<GtkRegionFlags>(0));
1286
0
      break;
1287
0
    }
1288
0
    case MOZ_GTK_TAB_BOTTOM:
1289
0
    {
1290
0
      // TODO - create from CSS node
1291
0
      style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK,
1292
0
                                      GTK_STYLE_CLASS_BOTTOM);
1293
0
      gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
1294
0
                                   static_cast<GtkRegionFlags>(0));
1295
0
      break;
1296
0
    }
1297
0
    case MOZ_GTK_NOTEBOOK:
1298
0
    case MOZ_GTK_NOTEBOOK_HEADER:
1299
0
    case MOZ_GTK_TABPANELS:
1300
0
    case MOZ_GTK_TAB_SCROLLARROW:
1301
0
    {
1302
0
      // TODO - create from CSS node
1303
0
      GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
1304
0
      return gtk_widget_get_style_context(widget);
1305
0
    }
1306
0
    case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE:
1307
0
    {
1308
0
      NS_ASSERTION(false,
1309
0
          "MOZ_GTK_HEADER_BAR_BUTTON_RESTORE is used as an icon only!");
1310
0
      return nullptr;
1311
0
    }
1312
0
    case MOZ_GTK_WINDOW_DECORATION:
1313
0
    {
1314
0
      GtkStyleContext* parentStyle =
1315
0
          CreateSubStyleWithClass(MOZ_GTK_WINDOW, "csd");
1316
0
      style = CreateCSSNode("decoration", parentStyle);
1317
0
      g_object_unref(parentStyle);
1318
0
      break;
1319
0
    }
1320
0
    case MOZ_GTK_WINDOW_DECORATION_SOLID:
1321
0
    {
1322
0
      GtkStyleContext* parentStyle =
1323
0
          CreateSubStyleWithClass(MOZ_GTK_WINDOW, "solid-csd");
1324
0
      style = CreateCSSNode("decoration", parentStyle);
1325
0
      g_object_unref(parentStyle);
1326
0
      break;
1327
0
    }
1328
0
    default:
1329
0
      return GetWidgetRootStyle(aNodeType);
1330
0
  }
1331
0
1332
0
  MOZ_ASSERT(style, "missing style context for node type");
1333
0
  sStyleStorage[aNodeType] = style;
1334
0
  return style;
1335
0
}
1336
1337
/* GetWidgetStyleInternal is used by Gtk < 3.20 */
1338
static GtkStyleContext*
1339
GetWidgetStyleInternal(WidgetNodeType aNodeType)
1340
0
{
1341
0
  GtkStyleContext* style = sStyleStorage[aNodeType];
1342
0
  if (style)
1343
0
    return style;
1344
0
1345
0
  switch (aNodeType) {
1346
0
    case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
1347
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_HORIZONTAL,
1348
0
                                      GTK_STYLE_CLASS_TROUGH);
1349
0
      break;
1350
0
    case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
1351
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_HORIZONTAL,
1352
0
                                      GTK_STYLE_CLASS_SLIDER);
1353
0
      break;
1354
0
    case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
1355
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_VERTICAL,
1356
0
                                      GTK_STYLE_CLASS_TROUGH);
1357
0
      break;
1358
0
    case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
1359
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_VERTICAL,
1360
0
                                      GTK_STYLE_CLASS_SLIDER);
1361
0
      break;
1362
0
    case MOZ_GTK_RADIOBUTTON:
1363
0
      style = CreateSubStyleWithClass(MOZ_GTK_RADIOBUTTON_CONTAINER,
1364
0
                                      GTK_STYLE_CLASS_RADIO);
1365
0
      break;
1366
0
    case MOZ_GTK_CHECKBUTTON:
1367
0
      style = CreateSubStyleWithClass(MOZ_GTK_CHECKBUTTON_CONTAINER,
1368
0
                                      GTK_STYLE_CLASS_CHECK);
1369
0
      break;
1370
0
    case MOZ_GTK_RADIOMENUITEM_INDICATOR:
1371
0
      style = CreateSubStyleWithClass(MOZ_GTK_RADIOMENUITEM,
1372
0
                                      GTK_STYLE_CLASS_RADIO);
1373
0
      break;
1374
0
    case MOZ_GTK_CHECKMENUITEM_INDICATOR:
1375
0
      style = CreateSubStyleWithClass(MOZ_GTK_CHECKMENUITEM,
1376
0
                                      GTK_STYLE_CLASS_CHECK);
1377
0
      break;
1378
0
    case MOZ_GTK_PROGRESS_TROUGH:
1379
0
      style = CreateSubStyleWithClass(MOZ_GTK_PROGRESSBAR,
1380
0
                                      GTK_STYLE_CLASS_TROUGH);
1381
0
      break;
1382
0
    case MOZ_GTK_PROGRESS_CHUNK:
1383
0
      style = CreateSubStyleWithClass(MOZ_GTK_PROGRESSBAR,
1384
0
                                      GTK_STYLE_CLASS_PROGRESSBAR);
1385
0
      gtk_style_context_remove_class(style, GTK_STYLE_CLASS_TROUGH);
1386
0
      break;
1387
0
    case MOZ_GTK_GRIPPER:
1388
0
      style = CreateSubStyleWithClass(MOZ_GTK_GRIPPER,
1389
0
                                      GTK_STYLE_CLASS_GRIP);
1390
0
      break;
1391
0
    case MOZ_GTK_INFO_BAR:
1392
0
      style = CreateSubStyleWithClass(MOZ_GTK_INFO_BAR,
1393
0
                                      GTK_STYLE_CLASS_INFO);
1394
0
      break;
1395
0
    case MOZ_GTK_SPINBUTTON_ENTRY:
1396
0
      style = CreateSubStyleWithClass(MOZ_GTK_SPINBUTTON,
1397
0
                                      GTK_STYLE_CLASS_ENTRY);
1398
0
      break;
1399
0
    case MOZ_GTK_SCROLLED_WINDOW:
1400
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCROLLED_WINDOW,
1401
0
                                      GTK_STYLE_CLASS_FRAME);
1402
0
      break;
1403
0
    case MOZ_GTK_TEXT_VIEW_TEXT:
1404
0
    case MOZ_GTK_RESIZER:
1405
0
      // GTK versions prior to 3.20 do not have the view class on the root
1406
0
      // node, but add this to determine the background for the text window.
1407
0
      style = CreateSubStyleWithClass(MOZ_GTK_TEXT_VIEW, GTK_STYLE_CLASS_VIEW);
1408
0
      if (aNodeType == MOZ_GTK_RESIZER) {
1409
0
        // The "grip" class provides the correct builtin icon from
1410
0
        // gtk_render_handle().  The icon is drawn with shaded variants of
1411
0
        // the background color, and so a transparent background would lead to
1412
0
        // a transparent resizer.  gtk_render_handle() also uses the
1413
0
        // background color to draw a background, and so this style otherwise
1414
0
        // matches MOZ_GTK_TEXT_VIEW_TEXT to match the background with
1415
0
        // textarea elements.  GtkTextView creates a separate text window and
1416
0
        // so the background should not be transparent.
1417
0
        gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
1418
0
      }
1419
0
      break;
1420
0
    case MOZ_GTK_FRAME_BORDER:
1421
0
      return GetWidgetRootStyle(MOZ_GTK_FRAME);
1422
0
    case MOZ_GTK_TREEVIEW_VIEW:
1423
0
      style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
1424
0
                                      GTK_STYLE_CLASS_VIEW);
1425
0
      break;
1426
0
    case MOZ_GTK_TREEVIEW_EXPANDER:
1427
0
      style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
1428
0
                                      GTK_STYLE_CLASS_EXPANDER);
1429
0
      break;
1430
0
    case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL:
1431
0
      style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_HORIZONTAL,
1432
0
                                      GTK_STYLE_CLASS_PANE_SEPARATOR);
1433
0
      break;
1434
0
    case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL:
1435
0
      style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_VERTICAL,
1436
0
                                      GTK_STYLE_CLASS_PANE_SEPARATOR);
1437
0
      break;
1438
0
    case MOZ_GTK_SCALE_TROUGH_HORIZONTAL:
1439
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL,
1440
0
                                      GTK_STYLE_CLASS_TROUGH);
1441
0
      break;
1442
0
    case MOZ_GTK_SCALE_TROUGH_VERTICAL:
1443
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCALE_VERTICAL,
1444
0
                                      GTK_STYLE_CLASS_TROUGH);
1445
0
      break;
1446
0
    case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
1447
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL,
1448
0
                                      GTK_STYLE_CLASS_SLIDER);
1449
0
      break;
1450
0
    case MOZ_GTK_SCALE_THUMB_VERTICAL:
1451
0
      style = CreateSubStyleWithClass(MOZ_GTK_SCALE_VERTICAL,
1452
0
                                      GTK_STYLE_CLASS_SLIDER);
1453
0
      break;
1454
0
    case MOZ_GTK_TAB_TOP:
1455
0
      style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK, GTK_STYLE_CLASS_TOP);
1456
0
      gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
1457
0
                                   static_cast<GtkRegionFlags>(0));
1458
0
      break;
1459
0
    case MOZ_GTK_TAB_BOTTOM:
1460
0
      style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK, GTK_STYLE_CLASS_BOTTOM);
1461
0
      gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
1462
0
                                   static_cast<GtkRegionFlags>(0));
1463
0
      break;
1464
0
    case MOZ_GTK_NOTEBOOK:
1465
0
    case MOZ_GTK_NOTEBOOK_HEADER:
1466
0
    case MOZ_GTK_TABPANELS:
1467
0
    case MOZ_GTK_TAB_SCROLLARROW:
1468
0
    {
1469
0
      GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
1470
0
      return gtk_widget_get_style_context(widget);
1471
0
    }
1472
0
    default:
1473
0
      return GetWidgetRootStyle(aNodeType);
1474
0
  }
1475
0
1476
0
  MOZ_ASSERT(style);
1477
0
  sStyleStorage[aNodeType] = style;
1478
0
  return style;
1479
0
}
1480
1481
void
1482
ResetWidgetCache(void)
1483
0
{
1484
0
  for (int i = 0; i < MOZ_GTK_WIDGET_NODE_COUNT; i++) {
1485
0
    if (sStyleStorage[i])
1486
0
      g_object_unref(sStyleStorage[i]);
1487
0
  }
1488
0
  mozilla::PodArrayZero(sStyleStorage);
1489
0
1490
0
  /* This will destroy all of our widgets */
1491
0
  if (sWidgetStorage[MOZ_GTK_WINDOW]) {
1492
0
    gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW]);
1493
0
  }
1494
0
  if (sWidgetStorage[MOZ_GTK_HEADERBAR_WINDOW]) {
1495
0
    gtk_widget_destroy(sWidgetStorage[MOZ_GTK_HEADERBAR_WINDOW]);
1496
0
  }
1497
0
  if (sWidgetStorage[MOZ_GTK_HEADERBAR_WINDOW_MAXIMIZED]) {
1498
0
    gtk_widget_destroy(sWidgetStorage[MOZ_GTK_HEADERBAR_WINDOW_MAXIMIZED]);
1499
0
  }
1500
0
1501
0
  /* Clear already freed arrays */
1502
0
  mozilla::PodArrayZero(sWidgetStorage);
1503
0
}
1504
1505
GtkStyleContext*
1506
GetStyleContext(WidgetNodeType aNodeType, GtkTextDirection aDirection,
1507
                GtkStateFlags aStateFlags, StyleFlags aFlags)
1508
0
{
1509
0
  GtkStyleContext* style;
1510
0
  if (gtk_check_version(3, 20, 0) != nullptr) {
1511
0
    style = GetWidgetStyleInternal(aNodeType);
1512
0
  } else {
1513
0
    style = GetCssNodeStyleInternal(aNodeType);
1514
0
  }
1515
0
  bool stateChanged = false;
1516
0
  bool stateHasDirection = gtk_get_minor_version() >= 8;
1517
0
  GtkStateFlags oldState = gtk_style_context_get_state(style);
1518
0
  MOZ_ASSERT(!(aStateFlags & (STATE_FLAG_DIR_LTR|STATE_FLAG_DIR_RTL)));
1519
0
  unsigned newState = aStateFlags;
1520
0
  if (stateHasDirection) {
1521
0
    switch (aDirection) {
1522
0
      case GTK_TEXT_DIR_LTR:
1523
0
        newState |= STATE_FLAG_DIR_LTR;
1524
0
        break;
1525
0
      case GTK_TEXT_DIR_RTL:
1526
0
        newState |= STATE_FLAG_DIR_RTL;
1527
0
        break;
1528
0
      default:
1529
0
        MOZ_FALLTHROUGH_ASSERT("Bad GtkTextDirection");
1530
0
      case GTK_TEXT_DIR_NONE:
1531
0
        // GtkWidget uses a default direction if neither is explicitly
1532
0
        // specified, but here DIR_NONE is interpreted as meaning the
1533
0
        // direction is not important, so don't change the direction
1534
0
        // unnecessarily.
1535
0
        newState |= oldState & (STATE_FLAG_DIR_LTR|STATE_FLAG_DIR_RTL);
1536
0
    }
1537
0
  } else if (aDirection != GTK_TEXT_DIR_NONE) {
1538
0
    GtkTextDirection oldDirection = gtk_style_context_get_direction(style);
1539
0
    if (aDirection != oldDirection) {
1540
0
      gtk_style_context_set_direction(style, aDirection);
1541
0
      stateChanged = true;
1542
0
    }
1543
0
  }
1544
0
  if (oldState != newState) {
1545
0
    gtk_style_context_set_state(style, static_cast<GtkStateFlags>(newState));
1546
0
    stateChanged = true;
1547
0
  }
1548
0
  // This invalidate is necessary for unsaved style contexts from GtkWidgets
1549
0
  // in pre-3.18 GTK, because automatic invalidation of such contexts
1550
0
  // was delayed until a resize event runs.
1551
0
  //
1552
0
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1272194#c7
1553
0
  //
1554
0
  // Avoid calling invalidate on contexts that are not owned and constructed
1555
0
  // by widgets to avoid performing build_properties() (in 3.16 stylecontext.c)
1556
0
  // unnecessarily early.
1557
0
  if (stateChanged && sWidgetStorage[aNodeType]) {
1558
0
    gtk_style_context_invalidate(style);
1559
0
  }
1560
0
  return style;
1561
0
}
1562
1563
GtkStyleContext*
1564
CreateStyleContextWithStates(WidgetNodeType aNodeType, GtkTextDirection aDirection,
1565
                             GtkStateFlags aStateFlags)
1566
0
{
1567
0
  GtkStyleContext* style = GetStyleContext(aNodeType, aDirection, aStateFlags);
1568
0
  GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(style));
1569
0
1570
0
  if (gtk_check_version(3, 14, 0) == nullptr) {
1571
0
1572
0
    static auto sGtkWidgetPathIterGetState =
1573
0
      (GtkStateFlags (*)(const GtkWidgetPath*, gint))
1574
0
      dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_get_state");
1575
0
    static auto sGtkWidgetPathIterSetState =
1576
0
      (void (*)(GtkWidgetPath*, gint, GtkStateFlags))
1577
0
      dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_state");
1578
0
1579
0
    int pathLength = gtk_widget_path_length(path);
1580
0
    for(int i = 0; i < pathLength; i++) {
1581
0
      unsigned state = aStateFlags | sGtkWidgetPathIterGetState(path, i);
1582
0
      sGtkWidgetPathIterSetState(path, i, GtkStateFlags(state));
1583
0
    }
1584
0
  }
1585
0
1586
0
  style = gtk_style_context_new();
1587
0
  gtk_style_context_set_path(style, path);
1588
0
  gtk_widget_path_unref(path);
1589
0
1590
0
  return style;
1591
0
}