background_guard.go RawHistoryFolder
1package cef
2
3import (
4 "fmt"
5
6 "github.com/bnema/puregotk/v4/gdk"
7 "github.com/bnema/puregotk/v4/gtk"
8)
9
10// opaqueWhiteBackground is Chromium's standard windowless canvas fallback. It is
11// baked into every BrowserSettings.BackgroundColor so pages that paint nothing
12// get an opaque white canvas (never a theme-dark one). A zero value must not be
13// used: in windowless/OSR mode a zero-alpha background enables transparent
14// painting, which this engine does not want.
15const opaqueWhiteBackground uint32 = 0xFFFFFFFF
16
17// flashGuardCSSClassPrefix scopes each WebView's transient background provider to
18// its own render widget so guards from different tabs never collide.
19const flashGuardCSSClassPrefix = "dumber-cef-flash-guard-"
20
21// colorByteMask isolates a single 8-bit channel from a packed ARGB value.
22const colorByteMask = 0xFF
23
24// applyBackgroundFlashGuard paints the theme background color behind the CEF
25// render widget until the first frame is presented. It is the presentation-layer
26// replacement for the former dark BrowserSettings.BackgroundColor: Chromium's
27// canvas is now always opaque white, so the anti-white-flash lives here instead.
28// The bridge widget (a GtkPicture/GtkGLArea) renders its CSS background until an
29// opaque frame texture covers it, at which point markFirstFramePainted tears the
30// guard down. Must run on the GTK main thread.
31func (wv *WebView) applyBackgroundFlashGuard(widget *gtk.Widget) {
32 if wv == nil || widget == nil {
33 return
34 }
35 argb := wv.backgroundColor
36 if argb == 0 {
37 return // No theme color configured; nothing to guard against.
38 }
39 display := gdk.DisplayGetDefault()
40 if display == nil {
41 return
42 }
43
44 a := float64((argb>>24)&colorByteMask) / colorScale
45 r := (argb >> 16) & colorByteMask
46 g := (argb >> 8) & colorByteMask
47 b := argb & colorByteMask
48 class := fmt.Sprintf("%s%d", flashGuardCSSClassPrefix, uint64(wv.id))
49 css := fmt.Sprintf(".%s { background-color: rgba(%d, %d, %d, %.4f); }", class, r, g, b, a)
50
51 provider := gtk.NewCssProvider()
52 if provider == nil {
53 return
54 }
55 provider.LoadFromString(css)
56
57 wv.bgGuardMu.Lock()
58 defer wv.bgGuardMu.Unlock()
59 if wv.firstFramePainted.Load() {
60 return // First frame already arrived before the guard could be installed.
61 }
62 widget.AddCssClass(class)
63 gtk.StyleContextAddProviderForDisplay(display, provider, uint(gtk.STYLE_PROVIDER_PRIORITY_APPLICATION))
64 wv.bgGuardProvider = provider
65 wv.bgGuardWidget = widget
66 wv.bgGuardClass = class
67}
68
69// markFirstFramePainted records that the first CEF frame has been presented and
70// schedules teardown of the background flash guard so later frames render
71// normally. Safe to call from any thread and idempotent.
72func (wv *WebView) markFirstFramePainted() {
73 if wv == nil || !wv.firstFramePainted.CompareAndSwap(false, true) {
74 return
75 }
76 wv.runOnGTK(func() {
77 wv.removeBackgroundFlashGuard()
78 })
79}
80
81// removeBackgroundFlashGuard detaches the transient theme-background provider and
82// clears the render widget's guard class. Must run on the GTK main thread. It is
83// safe to call when no guard is installed.
84func (wv *WebView) removeBackgroundFlashGuard() {
85 if wv == nil {
86 return
87 }
88 wv.bgGuardMu.Lock()
89 provider := wv.bgGuardProvider
90 widget := wv.bgGuardWidget
91 class := wv.bgGuardClass
92 wv.bgGuardProvider = nil
93 wv.bgGuardWidget = nil
94 wv.bgGuardClass = ""
95 wv.bgGuardMu.Unlock()
96
97 if provider == nil {
98 return
99 }
100 if widget != nil && class != "" {
101 widget.RemoveCssClass(class)
102 }
103 if display := gdk.DisplayGetDefault(); display != nil {
104 gtk.StyleContextRemoveProviderForDisplay(display, provider)
105 }
106}