detached 03caa44

fix(cef): run injected javascript on ui thread

commit
03caa44a9537046cafe3b6e6b797fb24b41056fb
author
brice · · 2026-06-24 05:02 UTC
parent
85f934a
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +57 -0
M internal/infrastructure/cef/webview.go +17 -0
@@ -1055,6 +1055,23 @@ func (wv *WebView) RunJavaScript(_ context.Context, script string) {
10551055 if wv.destroyed.Load() {
10561056 return
10571057 }
1058+ if wv.engine == nil {
1059+ wv.executeJavaScriptNow(script)
1060+ return
1061+ }
1062+ task := cefNewTask(cefTaskFunc(func() {
1063+ wv.executeJavaScriptNow(script)
1064+ }))
1065+ if task == nil {
1066+ return
1067+ }
1068+ cefPostTask(purecef.ThreadIDTidUi, task)
1069+}
1070+
1071+func (wv *WebView) executeJavaScriptNow(script string) {
1072+ if wv == nil || wv.destroyed.Load() {
1073+ return
1074+ }
10581075 wv.mu.RLock()
10591076 browser := wv.browser
10601077 wv.mu.RUnlock()
@@ -1055,6 +1055,23 @@ func (wv *WebView) RunJavaScript(_ context.Context, script string) {
1055 if wv.destroyed.Load() {1055 if wv.destroyed.Load() {
1056 return1056 return
1057 }1057 }
1058+ if wv.engine == nil {
1059+ wv.executeJavaScriptNow(script)
1060+ return
1061+ }
1062+ task := cefNewTask(cefTaskFunc(func() {
1063+ wv.executeJavaScriptNow(script)
1064+ }))
1065+ if task == nil {
1066+ return
1067+ }
1068+ cefPostTask(purecef.ThreadIDTidUi, task)
1069+}
1070+
1071+func (wv *WebView) executeJavaScriptNow(script string) {
1072+ if wv == nil || wv.destroyed.Load() {
1073+ return
1074+ }
1058 wv.mu.RLock()1075 wv.mu.RLock()
1059 browser := wv.browser1076 browser := wv.browser
1060 wv.mu.RUnlock()1077 wv.mu.RUnlock()
A internal/infrastructure/cef/webview_javascript_test.go +40 -0
@@ -0,0 +1,40 @@
1+package cef
2+
3+import (
4+ "context"
5+ "testing"
6+
7+ purecef "github.com/bnema/purego-cef/cef"
8+ cefmocks "github.com/bnema/purego-cef/cef/mocks"
9+ "github.com/stretchr/testify/require"
10+)
11+
12+func TestWebViewRunJavaScript_PostsExecutionToCefUIThread(t *testing.T) {
13+ browser := cefmocks.NewMockBrowser(t)
14+ frame := cefmocks.NewMockFrame(t)
15+ const script = "document.body.dataset.dumber = '1'"
16+
17+ oldTask := cefNewTask
18+ oldPost := cefPostTask
19+ defer func() {
20+ cefNewTask = oldTask
21+ cefPostTask = oldPost
22+ }()
23+ cefNewTask = func(task purecef.Task) purecef.Task { return task }
24+
25+ var scheduled purecef.Task
26+ cefPostTask = func(threadID purecef.ThreadID, task purecef.Task) int32 {
27+ require.Equal(t, purecef.ThreadIDTidUi, threadID)
28+ require.NotNil(t, task)
29+ scheduled = task
30+ return 1
31+ }
32+
33+ wv := &WebView{ctx: context.Background(), engine: &Engine{}, browser: browser}
34+ wv.RunJavaScript(context.Background(), script)
35+
36+ require.NotNil(t, scheduled)
37+ browser.EXPECT().GetMainFrame().Return(frame).Once()
38+ frame.EXPECT().ExecuteJavaScript(script, "", int32(0)).Once()
39+ scheduled.Execute()
40+}
@@ -0,0 +1,40 @@
1+package cef
2+
3+import (
4+ "context"
5+ "testing"
6+
7+ purecef "github.com/bnema/purego-cef/cef"
8+ cefmocks "github.com/bnema/purego-cef/cef/mocks"
9+ "github.com/stretchr/testify/require"
10+)
11+
12+func TestWebViewRunJavaScript_PostsExecutionToCefUIThread(t *testing.T) {
13+ browser := cefmocks.NewMockBrowser(t)
14+ frame := cefmocks.NewMockFrame(t)
15+ const script = "document.body.dataset.dumber = '1'"
16+
17+ oldTask := cefNewTask
18+ oldPost := cefPostTask
19+ defer func() {
20+ cefNewTask = oldTask
21+ cefPostTask = oldPost
22+ }()
23+ cefNewTask = func(task purecef.Task) purecef.Task { return task }
24+
25+ var scheduled purecef.Task
26+ cefPostTask = func(threadID purecef.ThreadID, task purecef.Task) int32 {
27+ require.Equal(t, purecef.ThreadIDTidUi, threadID)
28+ require.NotNil(t, task)
29+ scheduled = task
30+ return 1
31+ }
32+
33+ wv := &WebView{ctx: context.Background(), engine: &Engine{}, browser: browser}
34+ wv.RunJavaScript(context.Background(), script)
35+
36+ require.NotNil(t, scheduled)
37+ browser.EXPECT().GetMainFrame().Return(frame).Once()
38+ frame.EXPECT().ExecuteJavaScript(script, "", int32(0)).Once()
39+ scheduled.Execute()
40+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close