Line data Source code
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 : #define STATE_FLAG_DIR_LTR (1U << 7)
13 : #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 2 : CreateWindowWidget()
30 : {
31 2 : GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
32 2 : gtk_widget_set_name(widget, "MozillaGtkWidget");
33 2 : return widget;
34 : }
35 :
36 : static GtkWidget*
37 2 : CreateWindowContainerWidget()
38 : {
39 2 : GtkWidget *widget = gtk_fixed_new();
40 2 : gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW)), widget);
41 2 : return widget;
42 : }
43 :
44 : static void
45 17 : AddToWindowContainer(GtkWidget* widget)
46 : {
47 17 : gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_WINDOW_CONTAINER)), widget);
48 17 : }
49 :
50 : static GtkWidget*
51 4 : CreateScrollbarWidget(WidgetNodeType aWidgetType, GtkOrientation aOrientation)
52 : {
53 4 : GtkWidget* widget = gtk_scrollbar_new(aOrientation, nullptr);
54 4 : AddToWindowContainer(widget);
55 4 : return widget;
56 : }
57 :
58 : static GtkWidget*
59 0 : CreateCheckboxWidget()
60 : {
61 0 : GtkWidget* widget = gtk_check_button_new_with_label("M");
62 0 : AddToWindowContainer(widget);
63 0 : return widget;
64 : }
65 :
66 : static GtkWidget*
67 0 : CreateRadiobuttonWidget()
68 : {
69 0 : GtkWidget* widget = gtk_radio_button_new_with_label(nullptr, "M");
70 0 : AddToWindowContainer(widget);
71 0 : return widget;
72 : }
73 :
74 : static GtkWidget*
75 2 : CreateMenuBarWidget()
76 : {
77 2 : GtkWidget* widget = gtk_menu_bar_new();
78 2 : AddToWindowContainer(widget);
79 2 : return widget;
80 : }
81 :
82 : static GtkWidget*
83 2 : CreateMenuPopupWidget()
84 : {
85 2 : GtkWidget* widget = gtk_menu_new();
86 2 : gtk_menu_attach_to_widget(GTK_MENU(widget), GetWidget(MOZ_GTK_WINDOW),
87 2 : nullptr);
88 2 : return widget;
89 : }
90 :
91 : static GtkWidget*
92 0 : CreateProgressWidget()
93 : {
94 0 : GtkWidget* widget = gtk_progress_bar_new();
95 0 : AddToWindowContainer(widget);
96 0 : return widget;
97 : }
98 :
99 : static GtkWidget*
100 0 : CreateTooltipWidget()
101 : {
102 0 : MOZ_ASSERT(gtk_check_version(3, 20, 0) != nullptr,
103 : "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 : }
109 :
110 : static GtkWidget*
111 0 : CreateExpanderWidget()
112 : {
113 0 : GtkWidget* widget = gtk_expander_new("M");
114 0 : AddToWindowContainer(widget);
115 0 : return widget;
116 : }
117 :
118 : static GtkWidget*
119 2 : CreateFrameWidget()
120 : {
121 2 : GtkWidget* widget = gtk_frame_new(nullptr);
122 2 : AddToWindowContainer(widget);
123 2 : return widget;
124 : }
125 :
126 : static GtkWidget*
127 0 : CreateGripperWidget()
128 : {
129 0 : GtkWidget* widget = gtk_handle_box_new();
130 0 : AddToWindowContainer(widget);
131 0 : return widget;
132 : }
133 :
134 : static GtkWidget*
135 0 : CreateToolbarWidget()
136 : {
137 0 : GtkWidget* widget = gtk_toolbar_new();
138 0 : gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_GRIPPER)), widget);
139 0 : return widget;
140 : }
141 :
142 : static GtkWidget*
143 0 : CreateToolbarSeparatorWidget()
144 : {
145 0 : GtkWidget* widget = GTK_WIDGET(gtk_separator_tool_item_new());
146 0 : AddToWindowContainer(widget);
147 0 : return widget;
148 : }
149 :
150 : static GtkWidget*
151 0 : CreateInfoBarWidget()
152 : {
153 0 : GtkWidget* widget = gtk_info_bar_new();
154 0 : AddToWindowContainer(widget);
155 0 : return widget;
156 : }
157 :
158 : static GtkWidget*
159 2 : CreateButtonWidget()
160 : {
161 2 : GtkWidget* widget = gtk_button_new_with_label("M");
162 2 : AddToWindowContainer(widget);
163 2 : return widget;
164 : }
165 :
166 : static GtkWidget*
167 1 : CreateToggleButtonWidget()
168 : {
169 1 : GtkWidget* widget = gtk_toggle_button_new();
170 1 : AddToWindowContainer(widget);
171 1 : return widget;
172 : }
173 :
174 : static GtkWidget*
175 1 : CreateButtonArrowWidget()
176 : {
177 1 : GtkWidget* widget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
178 1 : gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_TOGGLE_BUTTON)), widget);
179 1 : gtk_widget_show(widget);
180 1 : return widget;
181 : }
182 :
183 : static GtkWidget*
184 0 : CreateSpinWidget()
185 : {
186 0 : GtkWidget* widget = gtk_spin_button_new(nullptr, 1, 0);
187 0 : AddToWindowContainer(widget);
188 0 : return widget;
189 : }
190 :
191 : static GtkWidget*
192 0 : CreateEntryWidget()
193 : {
194 0 : GtkWidget* widget = gtk_entry_new();
195 0 : AddToWindowContainer(widget);
196 0 : return widget;
197 : }
198 :
199 : static GtkWidget*
200 0 : CreateComboBoxWidget()
201 : {
202 0 : GtkWidget* widget = gtk_combo_box_new();
203 0 : AddToWindowContainer(widget);
204 0 : return widget;
205 : }
206 :
207 : typedef struct
208 : {
209 : GType type;
210 : GtkWidget** widget;
211 : } GtkInnerWidgetInfo;
212 :
213 : static void
214 4 : GetInnerWidget(GtkWidget* widget, gpointer client_data)
215 : {
216 4 : auto info = static_cast<GtkInnerWidgetInfo*>(client_data);
217 :
218 4 : if (G_TYPE_CHECK_INSTANCE_TYPE(widget, info->type)) {
219 2 : *info->widget = widget;
220 : }
221 4 : }
222 :
223 : static GtkWidget*
224 0 : CreateComboBoxButtonWidget()
225 : {
226 0 : GtkWidget* comboBox = GetWidget(MOZ_GTK_COMBOBOX);
227 0 : GtkWidget* comboBoxButton = nullptr;
228 :
229 : /* 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 :
235 0 : if (!comboBoxButton) {
236 : /* Shouldn't be reached with current internal gtk implementation; we
237 : * use a generic toggle button as last resort fallback to avoid
238 : * crashing. */
239 0 : comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
240 : } else {
241 : /* We need to have pointers to the inner widgets (button, separator, arrow)
242 : * of the ComboBox to get the correct rendering from theme engines which
243 : * special cases their look. Since the inner layout can change, we ask GTK
244 : * to NULL our pointers when they are about to become invalid because the
245 : * corresponding widgets don't exist anymore. It's the role of
246 : * g_object_add_weak_pointer().
247 : * Note that if we don't find the inner widgets (which shouldn't happen), we
248 : * fallback to use generic "non-inner" widgets, and they don't need that kind
249 : * of weak pointer since they are explicit children of gProtoLayout and as
250 : * such GTK holds a strong reference to them. */
251 0 : g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
252 : reinterpret_cast<gpointer *>(sWidgetStorage) +
253 0 : MOZ_GTK_COMBOBOX_BUTTON);
254 : }
255 :
256 0 : return comboBoxButton;
257 : }
258 :
259 : static GtkWidget*
260 0 : CreateComboBoxArrowWidget()
261 : {
262 0 : GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON);
263 0 : GtkWidget* comboBoxArrow = nullptr;
264 :
265 : /* 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 : /* appears-as-list = FALSE, cell-view = TRUE; the button
269 : * contains an hbox. This hbox is there because the ComboBox
270 : * needs to place a cell renderer, a separator, and an arrow in
271 : * 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 : /* appears-as-list = TRUE, or cell-view = FALSE;
278 : * the button only contains an arrow */
279 0 : comboBoxArrow = buttonChild;
280 : }
281 :
282 0 : if (!comboBoxArrow) {
283 : /* Shouldn't be reached with current internal gtk implementation;
284 : * we gButtonArrowWidget as last resort fallback to avoid
285 : * crashing. */
286 0 : comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
287 : } else {
288 0 : g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
289 : reinterpret_cast<gpointer *>(sWidgetStorage) +
290 0 : MOZ_GTK_COMBOBOX_ARROW);
291 : }
292 :
293 0 : return comboBoxArrow;
294 : }
295 :
296 : static GtkWidget*
297 0 : CreateComboBoxSeparatorWidget()
298 : {
299 : // Ensure to search for separator only once as it can fail
300 : // TODO - it won't initialize after ResetWidgetCache() call
301 : static bool isMissingSeparator = false;
302 0 : if (isMissingSeparator)
303 0 : return nullptr;
304 :
305 : /* Get the widgets inside the Button */
306 0 : GtkWidget* comboBoxSeparator = nullptr;
307 : GtkWidget* buttonChild =
308 0 : gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
309 0 : if (GTK_IS_BOX(buttonChild)) {
310 : /* appears-as-list = FALSE, cell-view = TRUE; the button
311 : * contains an hbox. This hbox is there because the ComboBox
312 : * needs to place a cell renderer, a separator, and an arrow in
313 : * 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 : }
319 :
320 0 : if (comboBoxSeparator) {
321 0 : g_object_add_weak_pointer(G_OBJECT(comboBoxSeparator),
322 : reinterpret_cast<gpointer *>(sWidgetStorage) +
323 0 : MOZ_GTK_COMBOBOX_SEPARATOR);
324 : } else {
325 : /* comboBoxSeparator may be NULL
326 : * when "appears-as-list" = TRUE or "cell-view" = FALSE;
327 : * if there is no separator, then we just won't paint it. */
328 0 : isMissingSeparator = true;
329 : }
330 :
331 0 : return comboBoxSeparator;
332 : }
333 :
334 : static GtkWidget*
335 2 : CreateComboBoxEntryWidget()
336 : {
337 2 : GtkWidget* widget = gtk_combo_box_new_with_entry();
338 2 : AddToWindowContainer(widget);
339 2 : return widget;
340 : }
341 :
342 : static GtkWidget*
343 2 : CreateComboBoxEntryTextareaWidget()
344 : {
345 2 : GtkWidget* comboBoxTextarea = nullptr;
346 :
347 : /* Get its inner Entry and Button */
348 2 : GtkInnerWidgetInfo info = { GTK_TYPE_ENTRY,
349 2 : &comboBoxTextarea };
350 2 : gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
351 2 : GetInnerWidget, &info);
352 :
353 2 : if (!comboBoxTextarea) {
354 0 : comboBoxTextarea = GetWidget(MOZ_GTK_ENTRY);
355 : } else {
356 2 : g_object_add_weak_pointer(G_OBJECT(comboBoxTextarea),
357 : reinterpret_cast<gpointer *>(sWidgetStorage) +
358 2 : MOZ_GTK_COMBOBOX_ENTRY);
359 : }
360 :
361 2 : return comboBoxTextarea;
362 : }
363 :
364 : static GtkWidget*
365 0 : CreateComboBoxEntryButtonWidget()
366 : {
367 0 : GtkWidget* comboBoxButton = nullptr;
368 :
369 : /* 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 :
375 0 : if (!comboBoxButton) {
376 0 : comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
377 : } else {
378 0 : g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
379 : reinterpret_cast<gpointer *>(sWidgetStorage) +
380 0 : MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
381 : }
382 :
383 0 : return comboBoxButton;
384 : }
385 :
386 : static GtkWidget*
387 0 : CreateComboBoxEntryArrowWidget()
388 : {
389 0 : GtkWidget* comboBoxArrow = nullptr;
390 :
391 : /* Get the Arrow inside the Button */
392 : GtkWidget* buttonChild =
393 0 : gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON)));
394 :
395 0 : if (GTK_IS_BOX(buttonChild)) {
396 : /* appears-as-list = FALSE, cell-view = TRUE; the button
397 : * contains an hbox. This hbox is there because the ComboBox
398 : * needs to place a cell renderer, a separator, and an arrow in
399 : * 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 : /* appears-as-list = TRUE, or cell-view = FALSE;
406 : * the button only contains an arrow */
407 0 : comboBoxArrow = buttonChild;
408 : }
409 :
410 0 : if (!comboBoxArrow) {
411 : /* Shouldn't be reached with current internal gtk implementation;
412 : * we gButtonArrowWidget as last resort fallback to avoid
413 : * crashing. */
414 0 : comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
415 : } else {
416 0 : g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
417 : reinterpret_cast<gpointer *>(sWidgetStorage) +
418 0 : MOZ_GTK_COMBOBOX_ENTRY_ARROW);
419 : }
420 :
421 0 : return comboBoxArrow;
422 : }
423 :
424 : static GtkWidget*
425 2 : CreateScrolledWindowWidget()
426 : {
427 2 : GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr);
428 2 : AddToWindowContainer(widget);
429 2 : return widget;
430 : }
431 :
432 : static GtkWidget*
433 0 : CreateMenuSeparatorWidget()
434 : {
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 : }
440 :
441 : static GtkWidget*
442 2 : CreateTreeViewWidget()
443 : {
444 2 : GtkWidget* widget = gtk_tree_view_new();
445 2 : AddToWindowContainer(widget);
446 2 : return widget;
447 : }
448 :
449 : static GtkWidget*
450 0 : CreateTreeHeaderCellWidget()
451 : {
452 : /*
453 : * Some GTK engines paint the first and last cell
454 : * of a TreeView header with a highlight.
455 : * Since we do not know where our widget will be relative
456 : * to the other buttons in the TreeView header, we must
457 : * paint it as a button that is between two others,
458 : * thus ensuring it is neither the first or last button
459 : * in the header.
460 : * GTK doesn't give us a way to do this explicitly,
461 : * so we must paint with a button that is between two
462 : * others.
463 : */
464 : GtkTreeViewColumn* firstTreeViewColumn;
465 : GtkTreeViewColumn* middleTreeViewColumn;
466 : GtkTreeViewColumn* lastTreeViewColumn;
467 :
468 0 : GtkWidget *treeView = GetWidget(MOZ_GTK_TREEVIEW);
469 :
470 : /* 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 :
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 :
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 :
486 : /* Use the middle column's header for our button */
487 0 : return gtk_tree_view_column_get_button(middleTreeViewColumn);
488 : }
489 :
490 : static GtkWidget*
491 0 : CreateTreeHeaderSortArrowWidget()
492 : {
493 : /* TODO, but it can't be NULL */
494 0 : GtkWidget* widget = gtk_button_new();
495 0 : AddToWindowContainer(widget);
496 0 : return widget;
497 : }
498 :
499 : static GtkWidget*
500 0 : CreateHPanedWidget()
501 : {
502 0 : GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
503 0 : AddToWindowContainer(widget);
504 0 : return widget;
505 : }
506 :
507 : static GtkWidget*
508 0 : CreateVPanedWidget()
509 : {
510 0 : GtkWidget* widget = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
511 0 : AddToWindowContainer(widget);
512 0 : return widget;
513 : }
514 :
515 : static GtkWidget*
516 0 : CreateScaleWidget(GtkOrientation aOrientation)
517 : {
518 0 : GtkWidget* widget = gtk_scale_new(aOrientation, nullptr);
519 0 : AddToWindowContainer(widget);
520 0 : return widget;
521 : }
522 :
523 : static GtkWidget*
524 0 : CreateNotebookWidget()
525 : {
526 0 : GtkWidget* widget = gtk_notebook_new();
527 0 : AddToWindowContainer(widget);
528 0 : return widget;
529 : }
530 :
531 : static GtkWidget*
532 26 : CreateWidget(WidgetNodeType aWidgetType)
533 : {
534 26 : switch (aWidgetType) {
535 : case MOZ_GTK_WINDOW:
536 2 : return CreateWindowWidget();
537 : case MOZ_GTK_WINDOW_CONTAINER:
538 2 : return CreateWindowContainerWidget();
539 : case MOZ_GTK_CHECKBUTTON_CONTAINER:
540 0 : return CreateCheckboxWidget();
541 : case MOZ_GTK_PROGRESSBAR:
542 0 : return CreateProgressWidget();
543 : case MOZ_GTK_RADIOBUTTON_CONTAINER:
544 0 : return CreateRadiobuttonWidget();
545 : case MOZ_GTK_SCROLLBAR_HORIZONTAL:
546 : return CreateScrollbarWidget(aWidgetType,
547 2 : GTK_ORIENTATION_HORIZONTAL);
548 : case MOZ_GTK_SCROLLBAR_VERTICAL:
549 : return CreateScrollbarWidget(aWidgetType,
550 2 : GTK_ORIENTATION_VERTICAL);
551 : case MOZ_GTK_MENUBAR:
552 2 : return CreateMenuBarWidget();
553 : case MOZ_GTK_MENUPOPUP:
554 2 : return CreateMenuPopupWidget();
555 : case MOZ_GTK_MENUSEPARATOR:
556 0 : return CreateMenuSeparatorWidget();
557 : case MOZ_GTK_EXPANDER:
558 0 : return CreateExpanderWidget();
559 : case MOZ_GTK_FRAME:
560 2 : return CreateFrameWidget();
561 : case MOZ_GTK_GRIPPER:
562 0 : return CreateGripperWidget();
563 : case MOZ_GTK_TOOLBAR:
564 0 : return CreateToolbarWidget();
565 : case MOZ_GTK_TOOLBAR_SEPARATOR:
566 0 : return CreateToolbarSeparatorWidget();
567 : case MOZ_GTK_INFO_BAR:
568 0 : return CreateInfoBarWidget();
569 : case MOZ_GTK_SPINBUTTON:
570 0 : return CreateSpinWidget();
571 : case MOZ_GTK_BUTTON:
572 2 : return CreateButtonWidget();
573 : case MOZ_GTK_TOGGLE_BUTTON:
574 1 : return CreateToggleButtonWidget();
575 : case MOZ_GTK_BUTTON_ARROW:
576 1 : return CreateButtonArrowWidget();
577 : case MOZ_GTK_ENTRY:
578 0 : return CreateEntryWidget();
579 : case MOZ_GTK_SCROLLED_WINDOW:
580 2 : return CreateScrolledWindowWidget();
581 : case MOZ_GTK_TREEVIEW:
582 2 : return CreateTreeViewWidget();
583 : case MOZ_GTK_TREE_HEADER_CELL:
584 0 : return CreateTreeHeaderCellWidget();
585 : case MOZ_GTK_TREE_HEADER_SORTARROW:
586 0 : return CreateTreeHeaderSortArrowWidget();
587 : case MOZ_GTK_SPLITTER_HORIZONTAL:
588 0 : return CreateHPanedWidget();
589 : case MOZ_GTK_SPLITTER_VERTICAL:
590 0 : return CreateVPanedWidget();
591 : case MOZ_GTK_SCALE_HORIZONTAL:
592 0 : return CreateScaleWidget(GTK_ORIENTATION_HORIZONTAL);
593 : case MOZ_GTK_SCALE_VERTICAL:
594 0 : return CreateScaleWidget(GTK_ORIENTATION_VERTICAL);
595 : case MOZ_GTK_NOTEBOOK:
596 0 : return CreateNotebookWidget();
597 : case MOZ_GTK_COMBOBOX:
598 0 : return CreateComboBoxWidget();
599 : case MOZ_GTK_COMBOBOX_BUTTON:
600 0 : return CreateComboBoxButtonWidget();
601 : case MOZ_GTK_COMBOBOX_ARROW:
602 0 : return CreateComboBoxArrowWidget();
603 : case MOZ_GTK_COMBOBOX_SEPARATOR:
604 0 : return CreateComboBoxSeparatorWidget();
605 : case MOZ_GTK_COMBOBOX_ENTRY:
606 2 : return CreateComboBoxEntryWidget();
607 : case MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA:
608 2 : return CreateComboBoxEntryTextareaWidget();
609 : case MOZ_GTK_COMBOBOX_ENTRY_BUTTON:
610 0 : return CreateComboBoxEntryButtonWidget();
611 : case MOZ_GTK_COMBOBOX_ENTRY_ARROW:
612 0 : return CreateComboBoxEntryArrowWidget();
613 : default:
614 : /* Not implemented */
615 0 : return nullptr;
616 : }
617 : }
618 :
619 : GtkWidget*
620 74 : GetWidget(WidgetNodeType aWidgetType)
621 : {
622 74 : GtkWidget* widget = sWidgetStorage[aWidgetType];
623 74 : if (!widget) {
624 26 : widget = CreateWidget(aWidgetType);
625 26 : sWidgetStorage[aWidgetType] = widget;
626 : }
627 74 : return widget;
628 : }
629 :
630 : static void
631 14 : AddStyleClassesFromStyle(GtkStyleContext* aDest, GtkStyleContext* aSrc)
632 : {
633 14 : GList* classes = gtk_style_context_list_classes(aSrc);
634 18 : for (GList* link = classes; link; link = link->next) {
635 4 : gtk_style_context_add_class(aDest, static_cast<gchar*>(link->data));
636 : }
637 14 : g_list_free(classes);
638 14 : }
639 :
640 : GtkStyleContext*
641 14 : CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle)
642 : {
643 : static auto sGtkWidgetClassGetCSSName =
644 2 : reinterpret_cast<const char* (*)(GtkWidgetClass*)>
645 16 : (dlsym(RTLD_DEFAULT, "gtk_widget_class_get_css_name"));
646 :
647 14 : GtkWidgetClass *widgetClass = GTK_WIDGET_GET_CLASS(aWidget);
648 28 : const gchar* name = sGtkWidgetClassGetCSSName ?
649 42 : sGtkWidgetClassGetCSSName(widgetClass) : nullptr;
650 :
651 : GtkStyleContext *context =
652 14 : CreateCSSNode(name, aParentStyle, G_TYPE_FROM_CLASS(widgetClass));
653 :
654 : // Classes are stored on the style context instead of the path so that any
655 : // future gtk_style_context_save() will inherit classes on the head CSS
656 : // node, in the same way as happens when called on a style context owned by
657 : // a widget.
658 : //
659 : // Classes can be stored on a GtkCssNodeDeclaration and/or the path.
660 : // gtk_style_context_save() reuses the GtkCssNodeDeclaration, and appends a
661 : // new object to the path, without copying the classes from the old path
662 : // head. The new head picks up classes from the GtkCssNodeDeclaration, but
663 : // not the path. GtkWidgets store their classes on the
664 : // GtkCssNodeDeclaration, so make sure to add classes there.
665 : //
666 : // Picking up classes from the style context also means that
667 : // https://bugzilla.gnome.org/show_bug.cgi?id=767312, which can stop
668 : // gtk_widget_path_append_for_widget() from finding classes in GTK 3.20,
669 : // is not a problem.
670 14 : GtkStyleContext* widgetStyle = gtk_widget_get_style_context(aWidget);
671 14 : AddStyleClassesFromStyle(context, widgetStyle);
672 :
673 : // Release any floating reference on aWidget.
674 14 : g_object_ref_sink(aWidget);
675 14 : g_object_unref(aWidget);
676 :
677 14 : return context;
678 : }
679 :
680 : static GtkStyleContext*
681 10 : CreateStyleForWidget(GtkWidget* aWidget, WidgetNodeType aParentType)
682 : {
683 10 : return CreateStyleForWidget(aWidget, GetWidgetRootStyle(aParentType));
684 : }
685 :
686 : GtkStyleContext*
687 32 : CreateCSSNode(const char* aName, GtkStyleContext* aParentStyle, GType aType)
688 : {
689 : static auto sGtkWidgetPathIterSetObjectName =
690 2 : reinterpret_cast<void (*)(GtkWidgetPath *, gint, const char *)>
691 34 : (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_set_object_name"));
692 :
693 : GtkWidgetPath* path;
694 32 : if (aParentStyle) {
695 30 : path = gtk_widget_path_copy(gtk_style_context_get_path(aParentStyle));
696 : // Copy classes from the parent style context to its corresponding node in
697 : // the path, because GTK will only match against ancestor classes if they
698 : // are on the path.
699 30 : GList* classes = gtk_style_context_list_classes(aParentStyle);
700 42 : for (GList* link = classes; link; link = link->next) {
701 12 : gtk_widget_path_iter_add_class(path, -1, static_cast<gchar*>(link->data));
702 : }
703 30 : g_list_free(classes);
704 : } else {
705 2 : path = gtk_widget_path_new();
706 : }
707 :
708 32 : gtk_widget_path_append_type(path, aType);
709 :
710 32 : if (sGtkWidgetPathIterSetObjectName) {
711 32 : (*sGtkWidgetPathIterSetObjectName)(path, -1, aName);
712 : }
713 :
714 32 : GtkStyleContext *context = gtk_style_context_new();
715 32 : gtk_style_context_set_path(context, path);
716 32 : gtk_style_context_set_parent(context, aParentStyle);
717 32 : gtk_widget_path_unref(path);
718 :
719 : // In GTK 3.4, gtk_render_* functions use |theming_engine| on the style
720 : // context without ensuring any style resolution sets it appropriately
721 : // in style_data_lookup(). e.g.
722 : // https://git.gnome.org/browse/gtk+/tree/gtk/gtkstylecontext.c?h=3.4.4#n3847
723 : //
724 : // That can result in incorrect drawing on first draw. To work around this,
725 : // force a style look-up to set |theming_engine|. It is sufficient to do
726 : // this only on context creation, instead of after every modification to the
727 : // context, because themes typically (Ambiance and oxygen-gtk, at least) set
728 : // the "engine" property with the '*' selector.
729 32 : if (GTK_MAJOR_VERSION == 3 && gtk_get_minor_version() < 6) {
730 : GdkRGBA unused;
731 0 : gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, &unused);
732 : }
733 :
734 32 : return context;
735 : }
736 :
737 : // Return a style context matching that of the root CSS node of a widget.
738 : // This is used by all GTK versions.
739 : static GtkStyleContext*
740 51 : GetWidgetRootStyle(WidgetNodeType aNodeType)
741 : {
742 51 : GtkStyleContext* style = sStyleStorage[aNodeType];
743 51 : if (style)
744 2 : return style;
745 :
746 49 : switch (aNodeType) {
747 : case MOZ_GTK_MENUBARITEM:
748 2 : style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUBAR);
749 2 : break;
750 : case MOZ_GTK_MENUITEM:
751 2 : style = CreateStyleForWidget(gtk_menu_item_new(), MOZ_GTK_MENUPOPUP);
752 2 : break;
753 : case MOZ_GTK_CHECKMENUITEM:
754 0 : style = CreateStyleForWidget(gtk_check_menu_item_new(), MOZ_GTK_MENUPOPUP);
755 0 : break;
756 : case MOZ_GTK_RADIOMENUITEM:
757 0 : style = CreateStyleForWidget(gtk_radio_menu_item_new(nullptr),
758 0 : MOZ_GTK_MENUPOPUP);
759 0 : break;
760 : case MOZ_GTK_TEXT_VIEW:
761 2 : style = CreateStyleForWidget(gtk_text_view_new(),
762 2 : MOZ_GTK_SCROLLED_WINDOW);
763 2 : break;
764 : case MOZ_GTK_TOOLTIP:
765 2 : if (gtk_check_version(3, 20, 0) != nullptr) {
766 : // The tooltip style class is added first in CreateTooltipWidget()
767 : // and transfered to style in CreateStyleForWidget().
768 0 : GtkWidget* tooltipWindow = CreateTooltipWidget();
769 0 : style = CreateStyleForWidget(tooltipWindow, nullptr);
770 0 : gtk_widget_destroy(tooltipWindow); // Release GtkWindow self-reference.
771 : } else {
772 : // We create this from the path because GtkTooltipWindow is not public.
773 2 : style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP);
774 2 : gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
775 : }
776 2 : break;
777 : case MOZ_GTK_TOOLTIP_BOX:
778 2 : style = CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0),
779 2 : MOZ_GTK_TOOLTIP);
780 2 : break;
781 : case MOZ_GTK_TOOLTIP_BOX_LABEL:
782 2 : style = CreateStyleForWidget(gtk_label_new(nullptr),
783 2 : MOZ_GTK_TOOLTIP_BOX);
784 2 : break;
785 : default:
786 37 : GtkWidget* widget = GetWidget(aNodeType);
787 37 : MOZ_ASSERT(widget);
788 37 : return gtk_widget_get_style_context(widget);
789 : }
790 :
791 12 : MOZ_ASSERT(style);
792 12 : sStyleStorage[aNodeType] = style;
793 12 : return style;
794 : }
795 :
796 : static GtkStyleContext*
797 16 : CreateChildCSSNode(const char* aName, WidgetNodeType aParentNodeType)
798 : {
799 16 : return CreateCSSNode(aName, GetCssNodeStyleInternal(aParentNodeType));
800 : }
801 :
802 : // Create a style context equivalent to a saved root style context of
803 : // |aWidgetType| with |aStyleClass| as an additional class. This is used to
804 : // produce a context equivalent to what GTK versions < 3.20 use for many
805 : // internal parts of widgets.
806 : static GtkStyleContext*
807 0 : CreateSubStyleWithClass(WidgetNodeType aWidgetType, const gchar* aStyleClass)
808 : {
809 : static auto sGtkWidgetPathIterGetObjectName =
810 0 : reinterpret_cast<const char* (*)(const GtkWidgetPath*, gint)>
811 0 : (dlsym(RTLD_DEFAULT, "gtk_widget_path_iter_get_object_name"));
812 :
813 0 : GtkStyleContext* parentStyle = GetWidgetRootStyle(aWidgetType);
814 :
815 : // Create a new context that behaves like |parentStyle| would after
816 : // gtk_style_context_save(parentStyle).
817 : //
818 : // Avoiding gtk_style_context_save() avoids the need to manage the
819 : // restore, and a new context permits caching style resolution.
820 : //
821 : // gtk_style_context_save(context) changes the node hierarchy of |context|
822 : // to add a new GtkCssNodeDeclaration that is a copy of its original node.
823 : // The new node is a child of the original node, and so the new heirarchy is
824 : // one level deeper. The new node receives the same classes as the
825 : // original, but any changes to the classes on |context| will change only
826 : // the new node. The new node inherits properties from the original node
827 : // (which retains the original heirarchy and classes) and matches CSS rules
828 : // with the new heirarchy and any changes to the classes.
829 : //
830 : // The change in hierarchy can produce some surprises in matching theme CSS
831 : // rules (e.g. https://bugzilla.gnome.org/show_bug.cgi?id=761870#c2), but it
832 : // is important here to produce the same behavior so that rules match the
833 : // same widget parts in Gecko as they do in GTK.
834 : //
835 : // When using public GTK API to construct style contexts, a widget path is
836 : // required. CSS rules are not matched against the style context heirarchy
837 : // but according to the heirarchy in the widget path. The path that matches
838 : // the same CSS rules as a saved context is like the path of |parentStyle|
839 : // but with an extra copy of the head (last) object appended. Setting
840 : // |parentStyle| as the parent context provides the same inheritance of
841 : // properties from the widget root node.
842 0 : const GtkWidgetPath* parentPath = gtk_style_context_get_path(parentStyle);
843 0 : const gchar* name = sGtkWidgetPathIterGetObjectName ?
844 0 : sGtkWidgetPathIterGetObjectName(parentPath, -1) : nullptr;
845 0 : GType objectType = gtk_widget_path_get_object_type(parentPath);
846 :
847 0 : GtkStyleContext* style = CreateCSSNode(name, parentStyle, objectType);
848 :
849 : // Start with the same classes on the new node as were on |parentStyle|.
850 : // GTK puts no regions or junction_sides on widget root nodes, and so there
851 : // is no need to copy these.
852 0 : AddStyleClassesFromStyle(style, parentStyle);
853 :
854 0 : gtk_style_context_add_class(style, aStyleClass);
855 0 : return style;
856 : }
857 :
858 : /* GetCssNodeStyleInternal is used by Gtk >= 3.20 */
859 : static GtkStyleContext*
860 72 : GetCssNodeStyleInternal(WidgetNodeType aNodeType)
861 : {
862 72 : GtkStyleContext* style = sStyleStorage[aNodeType];
863 72 : if (style)
864 15 : return style;
865 :
866 57 : switch (aNodeType) {
867 : case MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL:
868 : style = CreateChildCSSNode("contents",
869 2 : MOZ_GTK_SCROLLBAR_HORIZONTAL);
870 2 : break;
871 : case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
872 : style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
873 2 : MOZ_GTK_SCROLLBAR_CONTENTS_HORIZONTAL);
874 2 : break;
875 : case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
876 : style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
877 2 : MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL);
878 2 : break;
879 : case MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL:
880 : style = CreateChildCSSNode("contents",
881 2 : MOZ_GTK_SCROLLBAR_VERTICAL);
882 2 : break;
883 : case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
884 : style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
885 2 : MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
886 2 : break;
887 : case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
888 : style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
889 2 : MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL);
890 2 : break;
891 : case MOZ_GTK_SCROLLBAR_BUTTON:
892 : style = CreateChildCSSNode(GTK_STYLE_CLASS_BUTTON,
893 0 : MOZ_GTK_SCROLLBAR_CONTENTS_VERTICAL);
894 0 : break;
895 : case MOZ_GTK_RADIOBUTTON:
896 : style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO,
897 0 : MOZ_GTK_RADIOBUTTON_CONTAINER);
898 0 : break;
899 : case MOZ_GTK_CHECKBUTTON:
900 : style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK,
901 0 : MOZ_GTK_CHECKBUTTON_CONTAINER);
902 0 : break;
903 : case MOZ_GTK_RADIOMENUITEM_INDICATOR:
904 : style = CreateChildCSSNode(GTK_STYLE_CLASS_RADIO,
905 0 : MOZ_GTK_RADIOMENUITEM);
906 0 : break;
907 : case MOZ_GTK_CHECKMENUITEM_INDICATOR:
908 : style = CreateChildCSSNode(GTK_STYLE_CLASS_CHECK,
909 0 : MOZ_GTK_CHECKMENUITEM);
910 0 : break;
911 : case MOZ_GTK_PROGRESS_TROUGH:
912 : /* Progress bar background (trough) */
913 : style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
914 0 : MOZ_GTK_PROGRESSBAR);
915 0 : break;
916 : case MOZ_GTK_PROGRESS_CHUNK:
917 : style = CreateChildCSSNode("progress",
918 0 : MOZ_GTK_PROGRESS_TROUGH);
919 0 : break;
920 : case MOZ_GTK_GRIPPER:
921 : // TODO - create from CSS node
922 : style = CreateSubStyleWithClass(MOZ_GTK_GRIPPER,
923 0 : GTK_STYLE_CLASS_GRIP);
924 0 : break;
925 : case MOZ_GTK_INFO_BAR:
926 : // TODO - create from CSS node
927 : style = CreateSubStyleWithClass(MOZ_GTK_INFO_BAR,
928 0 : GTK_STYLE_CLASS_INFO);
929 0 : break;
930 : case MOZ_GTK_SPINBUTTON_ENTRY:
931 : // TODO - create from CSS node
932 : style = CreateSubStyleWithClass(MOZ_GTK_SPINBUTTON,
933 0 : GTK_STYLE_CLASS_ENTRY);
934 0 : break;
935 : case MOZ_GTK_SCROLLED_WINDOW:
936 : // TODO - create from CSS node
937 : style = CreateSubStyleWithClass(MOZ_GTK_SCROLLED_WINDOW,
938 0 : GTK_STYLE_CLASS_FRAME);
939 0 : break;
940 : case MOZ_GTK_TEXT_VIEW_TEXT:
941 : case MOZ_GTK_RESIZER:
942 2 : style = CreateChildCSSNode("text", MOZ_GTK_TEXT_VIEW);
943 2 : if (aNodeType == MOZ_GTK_RESIZER) {
944 : // The "grip" class provides the correct builtin icon from
945 : // gtk_render_handle(). The icon is drawn with shaded variants of
946 : // the background color, and so a transparent background would lead to
947 : // a transparent resizer. gtk_render_handle() also uses the
948 : // background color to draw a background, and so this style otherwise
949 : // matches what is used in GtkTextView to match the background with
950 : // textarea elements.
951 : GdkRGBA color;
952 : gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL,
953 0 : &color);
954 0 : if (color.alpha == 0.0) {
955 0 : g_object_unref(style);
956 0 : style = CreateStyleForWidget(gtk_text_view_new(),
957 0 : MOZ_GTK_SCROLLED_WINDOW);
958 : }
959 0 : gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
960 : }
961 2 : break;
962 : case MOZ_GTK_FRAME_BORDER:
963 2 : style = CreateChildCSSNode("border", MOZ_GTK_FRAME);
964 2 : break;
965 : case MOZ_GTK_TREEVIEW_VIEW:
966 : // TODO - create from CSS node
967 : style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
968 0 : GTK_STYLE_CLASS_VIEW);
969 0 : break;
970 : case MOZ_GTK_TREEVIEW_EXPANDER:
971 : // TODO - create from CSS node
972 : style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
973 0 : GTK_STYLE_CLASS_EXPANDER);
974 0 : break;
975 : case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL:
976 : style = CreateChildCSSNode("separator",
977 0 : MOZ_GTK_SPLITTER_HORIZONTAL);
978 0 : break;
979 : case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL:
980 : style = CreateChildCSSNode("separator",
981 0 : MOZ_GTK_SPLITTER_VERTICAL);
982 0 : break;
983 : case MOZ_GTK_SCALE_CONTENTS_HORIZONTAL:
984 : style = CreateChildCSSNode("contents",
985 0 : MOZ_GTK_SCALE_HORIZONTAL);
986 0 : break;
987 : case MOZ_GTK_SCALE_CONTENTS_VERTICAL:
988 : style = CreateChildCSSNode("contents",
989 0 : MOZ_GTK_SCALE_VERTICAL);
990 0 : break;
991 : case MOZ_GTK_SCALE_TROUGH_HORIZONTAL:
992 : style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
993 0 : MOZ_GTK_SCALE_CONTENTS_HORIZONTAL);
994 0 : break;
995 : case MOZ_GTK_SCALE_TROUGH_VERTICAL:
996 : style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
997 0 : MOZ_GTK_SCALE_CONTENTS_VERTICAL);
998 0 : break;
999 : case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
1000 : style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
1001 0 : MOZ_GTK_SCALE_TROUGH_HORIZONTAL);
1002 0 : break;
1003 : case MOZ_GTK_SCALE_THUMB_VERTICAL:
1004 : style = CreateChildCSSNode(GTK_STYLE_CLASS_SLIDER,
1005 0 : MOZ_GTK_SCALE_TROUGH_VERTICAL);
1006 0 : break;
1007 : case MOZ_GTK_TAB_TOP:
1008 : {
1009 : // TODO - create from CSS node
1010 : style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK,
1011 0 : GTK_STYLE_CLASS_TOP);
1012 : gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
1013 0 : static_cast<GtkRegionFlags>(0));
1014 0 : break;
1015 : }
1016 : case MOZ_GTK_TAB_BOTTOM:
1017 : {
1018 : // TODO - create from CSS node
1019 : style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK,
1020 0 : GTK_STYLE_CLASS_BOTTOM);
1021 : gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
1022 0 : static_cast<GtkRegionFlags>(0));
1023 0 : break;
1024 : }
1025 : case MOZ_GTK_NOTEBOOK:
1026 : case MOZ_GTK_NOTEBOOK_HEADER:
1027 : case MOZ_GTK_TABPANELS:
1028 : case MOZ_GTK_TAB_SCROLLARROW:
1029 : {
1030 : // TODO - create from CSS node
1031 0 : GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
1032 0 : return gtk_widget_get_style_context(widget);
1033 : }
1034 : default:
1035 41 : return GetWidgetRootStyle(aNodeType);
1036 : }
1037 :
1038 16 : MOZ_ASSERT(style, "missing style context for node type");
1039 16 : sStyleStorage[aNodeType] = style;
1040 16 : return style;
1041 : }
1042 :
1043 : /* GetWidgetStyleInternal is used by Gtk < 3.20 */
1044 : static GtkStyleContext*
1045 0 : GetWidgetStyleInternal(WidgetNodeType aNodeType)
1046 : {
1047 0 : GtkStyleContext* style = sStyleStorage[aNodeType];
1048 0 : if (style)
1049 0 : return style;
1050 :
1051 0 : switch (aNodeType) {
1052 : case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
1053 : style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_HORIZONTAL,
1054 0 : GTK_STYLE_CLASS_TROUGH);
1055 0 : break;
1056 : case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL:
1057 : style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_HORIZONTAL,
1058 0 : GTK_STYLE_CLASS_SLIDER);
1059 0 : break;
1060 : case MOZ_GTK_SCROLLBAR_TROUGH_VERTICAL:
1061 : style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_VERTICAL,
1062 0 : GTK_STYLE_CLASS_TROUGH);
1063 0 : break;
1064 : case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL:
1065 : style = CreateSubStyleWithClass(MOZ_GTK_SCROLLBAR_VERTICAL,
1066 0 : GTK_STYLE_CLASS_SLIDER);
1067 0 : break;
1068 : case MOZ_GTK_RADIOBUTTON:
1069 : style = CreateSubStyleWithClass(MOZ_GTK_RADIOBUTTON_CONTAINER,
1070 0 : GTK_STYLE_CLASS_RADIO);
1071 0 : break;
1072 : case MOZ_GTK_CHECKBUTTON:
1073 : style = CreateSubStyleWithClass(MOZ_GTK_CHECKBUTTON_CONTAINER,
1074 0 : GTK_STYLE_CLASS_CHECK);
1075 0 : break;
1076 : case MOZ_GTK_RADIOMENUITEM_INDICATOR:
1077 : style = CreateSubStyleWithClass(MOZ_GTK_RADIOMENUITEM,
1078 0 : GTK_STYLE_CLASS_RADIO);
1079 0 : break;
1080 : case MOZ_GTK_CHECKMENUITEM_INDICATOR:
1081 : style = CreateSubStyleWithClass(MOZ_GTK_CHECKMENUITEM,
1082 0 : GTK_STYLE_CLASS_CHECK);
1083 0 : break;
1084 : case MOZ_GTK_PROGRESS_TROUGH:
1085 : style = CreateSubStyleWithClass(MOZ_GTK_PROGRESSBAR,
1086 0 : GTK_STYLE_CLASS_TROUGH);
1087 0 : break;
1088 : case MOZ_GTK_PROGRESS_CHUNK:
1089 : style = CreateSubStyleWithClass(MOZ_GTK_PROGRESSBAR,
1090 0 : GTK_STYLE_CLASS_PROGRESSBAR);
1091 0 : gtk_style_context_remove_class(style, GTK_STYLE_CLASS_TROUGH);
1092 0 : break;
1093 : case MOZ_GTK_GRIPPER:
1094 : style = CreateSubStyleWithClass(MOZ_GTK_GRIPPER,
1095 0 : GTK_STYLE_CLASS_GRIP);
1096 0 : break;
1097 : case MOZ_GTK_INFO_BAR:
1098 : style = CreateSubStyleWithClass(MOZ_GTK_INFO_BAR,
1099 0 : GTK_STYLE_CLASS_INFO);
1100 0 : break;
1101 : case MOZ_GTK_SPINBUTTON_ENTRY:
1102 : style = CreateSubStyleWithClass(MOZ_GTK_SPINBUTTON,
1103 0 : GTK_STYLE_CLASS_ENTRY);
1104 0 : break;
1105 : case MOZ_GTK_SCROLLED_WINDOW:
1106 : style = CreateSubStyleWithClass(MOZ_GTK_SCROLLED_WINDOW,
1107 0 : GTK_STYLE_CLASS_FRAME);
1108 0 : break;
1109 : case MOZ_GTK_TEXT_VIEW_TEXT:
1110 : case MOZ_GTK_RESIZER:
1111 : // GTK versions prior to 3.20 do not have the view class on the root
1112 : // node, but add this to determine the background for the text window.
1113 0 : style = CreateSubStyleWithClass(MOZ_GTK_TEXT_VIEW, GTK_STYLE_CLASS_VIEW);
1114 0 : if (aNodeType == MOZ_GTK_RESIZER) {
1115 : // The "grip" class provides the correct builtin icon from
1116 : // gtk_render_handle(). The icon is drawn with shaded variants of
1117 : // the background color, and so a transparent background would lead to
1118 : // a transparent resizer. gtk_render_handle() also uses the
1119 : // background color to draw a background, and so this style otherwise
1120 : // matches MOZ_GTK_TEXT_VIEW_TEXT to match the background with
1121 : // textarea elements. GtkTextView creates a separate text window and
1122 : // so the background should not be transparent.
1123 0 : gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP);
1124 : }
1125 0 : break;
1126 : case MOZ_GTK_FRAME_BORDER:
1127 0 : return GetWidgetRootStyle(MOZ_GTK_FRAME);
1128 : case MOZ_GTK_TREEVIEW_VIEW:
1129 : style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
1130 0 : GTK_STYLE_CLASS_VIEW);
1131 0 : break;
1132 : case MOZ_GTK_TREEVIEW_EXPANDER:
1133 : style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW,
1134 0 : GTK_STYLE_CLASS_EXPANDER);
1135 0 : break;
1136 : case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL:
1137 : style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_HORIZONTAL,
1138 0 : GTK_STYLE_CLASS_PANE_SEPARATOR);
1139 0 : break;
1140 : case MOZ_GTK_SPLITTER_SEPARATOR_VERTICAL:
1141 : style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_VERTICAL,
1142 0 : GTK_STYLE_CLASS_PANE_SEPARATOR);
1143 0 : break;
1144 : case MOZ_GTK_SCALE_TROUGH_HORIZONTAL:
1145 : style = CreateSubStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL,
1146 0 : GTK_STYLE_CLASS_TROUGH);
1147 0 : break;
1148 : case MOZ_GTK_SCALE_TROUGH_VERTICAL:
1149 : style = CreateSubStyleWithClass(MOZ_GTK_SCALE_VERTICAL,
1150 0 : GTK_STYLE_CLASS_TROUGH);
1151 0 : break;
1152 : case MOZ_GTK_SCALE_THUMB_HORIZONTAL:
1153 : style = CreateSubStyleWithClass(MOZ_GTK_SCALE_HORIZONTAL,
1154 0 : GTK_STYLE_CLASS_SLIDER);
1155 0 : break;
1156 : case MOZ_GTK_SCALE_THUMB_VERTICAL:
1157 : style = CreateSubStyleWithClass(MOZ_GTK_SCALE_VERTICAL,
1158 0 : GTK_STYLE_CLASS_SLIDER);
1159 0 : break;
1160 : case MOZ_GTK_TAB_TOP:
1161 0 : style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK, GTK_STYLE_CLASS_TOP);
1162 : gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
1163 0 : static_cast<GtkRegionFlags>(0));
1164 0 : break;
1165 : case MOZ_GTK_TAB_BOTTOM:
1166 0 : style = CreateSubStyleWithClass(MOZ_GTK_NOTEBOOK, GTK_STYLE_CLASS_BOTTOM);
1167 : gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB,
1168 0 : static_cast<GtkRegionFlags>(0));
1169 0 : break;
1170 : case MOZ_GTK_NOTEBOOK:
1171 : case MOZ_GTK_NOTEBOOK_HEADER:
1172 : case MOZ_GTK_TABPANELS:
1173 : case MOZ_GTK_TAB_SCROLLARROW:
1174 : {
1175 0 : GtkWidget* widget = GetWidget(MOZ_GTK_NOTEBOOK);
1176 0 : return gtk_widget_get_style_context(widget);
1177 : }
1178 : default:
1179 0 : return GetWidgetRootStyle(aNodeType);
1180 : }
1181 :
1182 0 : MOZ_ASSERT(style);
1183 0 : sStyleStorage[aNodeType] = style;
1184 0 : return style;
1185 : }
1186 :
1187 : void
1188 0 : ResetWidgetCache(void)
1189 : {
1190 0 : for (int i = 0; i < MOZ_GTK_WIDGET_NODE_COUNT; i++) {
1191 0 : if (sStyleStorage[i])
1192 0 : g_object_unref(sStyleStorage[i]);
1193 : }
1194 0 : mozilla::PodArrayZero(sStyleStorage);
1195 :
1196 : /* This will destroy all of our widgets */
1197 0 : if (sWidgetStorage[MOZ_GTK_WINDOW])
1198 0 : gtk_widget_destroy(sWidgetStorage[MOZ_GTK_WINDOW]);
1199 :
1200 : /* Clear already freed arrays */
1201 0 : mozilla::PodArrayZero(sWidgetStorage);
1202 0 : }
1203 :
1204 : GtkStyleContext*
1205 56 : ClaimStyleContext(WidgetNodeType aNodeType, GtkTextDirection aDirection,
1206 : GtkStateFlags aStateFlags, StyleFlags aFlags)
1207 : {
1208 : GtkStyleContext* style;
1209 56 : if (gtk_check_version(3, 20, 0) != nullptr) {
1210 0 : style = GetWidgetStyleInternal(aNodeType);
1211 : } else {
1212 56 : style = GetCssNodeStyleInternal(aNodeType);
1213 : }
1214 56 : bool stateChanged = false;
1215 56 : bool stateHasDirection = gtk_get_minor_version() >= 8;
1216 56 : GtkStateFlags oldState = gtk_style_context_get_state(style);
1217 56 : MOZ_ASSERT(!(aStateFlags & (STATE_FLAG_DIR_LTR|STATE_FLAG_DIR_RTL)));
1218 56 : unsigned newState = aStateFlags;
1219 56 : if (stateHasDirection) {
1220 56 : switch (aDirection) {
1221 : case GTK_TEXT_DIR_LTR:
1222 9 : newState |= STATE_FLAG_DIR_LTR;
1223 9 : break;
1224 : case GTK_TEXT_DIR_RTL:
1225 0 : newState |= STATE_FLAG_DIR_RTL;
1226 0 : break;
1227 : default:
1228 0 : MOZ_FALLTHROUGH_ASSERT("Bad GtkTextDirection");
1229 : case GTK_TEXT_DIR_NONE:
1230 : // GtkWidget uses a default direction if neither is explicitly
1231 : // specified, but here DIR_NONE is interpreted as meaning the
1232 : // direction is not important, so don't change the direction
1233 : // unnecessarily.
1234 47 : newState |= oldState & (STATE_FLAG_DIR_LTR|STATE_FLAG_DIR_RTL);
1235 : }
1236 0 : } else if (aDirection != GTK_TEXT_DIR_NONE) {
1237 0 : GtkTextDirection oldDirection = gtk_style_context_get_direction(style);
1238 0 : if (aDirection != oldDirection) {
1239 0 : gtk_style_context_set_direction(style, aDirection);
1240 0 : stateChanged = true;
1241 : }
1242 : }
1243 56 : if (oldState != newState) {
1244 0 : gtk_style_context_set_state(style, static_cast<GtkStateFlags>(newState));
1245 0 : stateChanged = true;
1246 : }
1247 : // This invalidate is necessary for unsaved style contexts from GtkWidgets
1248 : // in pre-3.18 GTK, because automatic invalidation of such contexts
1249 : // was delayed until a resize event runs.
1250 : //
1251 : // https://bugzilla.mozilla.org/show_bug.cgi?id=1272194#c7
1252 : //
1253 : // Avoid calling invalidate on contexts that are not owned and constructed
1254 : // by widgets to avoid performing build_properties() (in 3.16 stylecontext.c)
1255 : // unnecessarily early.
1256 56 : if (stateChanged && sWidgetStorage[aNodeType]) {
1257 0 : gtk_style_context_invalidate(style);
1258 : }
1259 56 : return style;
1260 : }
1261 :
1262 : void
1263 56 : ReleaseStyleContext(GtkStyleContext* aStyleContext)
1264 : {
1265 56 : }
|