v0.33.0 62a09f5

popup_opener_renderer.go RawHistoryFolder

1package cef
2
3import (
4	"context"
5	"sync"
6
7	purecef "github.com/bnema/purego-cef/cef"
8
9	"github.com/bnema/dumber/internal/logging"
10)
11
12const (
13	popupOpenerExtraInfoEnabledKey     = "dumber.popup_opener.enabled"
14	popupOpenerExtraInfoParentURIKey   = "dumber.popup_opener.parent_uri"
15	popupOpenerExtraInfoBridgeNonceKey = "dumber.popup_opener.bridge_nonce"
16	popupOpenerRenderScriptURL         = "dumber://popup-opener-renderer"
17)
18
19type popupOpenerRenderMetadata struct {
20	ParentURI   string
21	BridgeNonce string
22}
23
24var popupOpenerRenderExtraInfoBuilder = buildPopupOpenerRenderExtraInfo
25
26func buildPopupOpenerRenderExtraInfo(parentURI, bridgeNonce string) purecef.DictionaryValue {
27	if parentURI == "" || bridgeNonce == "" {
28		return nil
29	}
30	extraInfo := purecef.DictionaryValueCreate()
31	if extraInfo == nil {
32		return nil
33	}
34	extraInfo.SetBool(popupOpenerExtraInfoEnabledKey, 1)
35	extraInfo.SetString(popupOpenerExtraInfoParentURIKey, parentURI)
36	extraInfo.SetString(popupOpenerExtraInfoBridgeNonceKey, bridgeNonce)
37	return extraInfo
38}
39
40func decodePopupOpenerRenderMetadata(extraInfo purecef.DictionaryValue) (popupOpenerRenderMetadata, bool) {
41	var metadata popupOpenerRenderMetadata
42	if extraInfo == nil || extraInfo.GetBool(popupOpenerExtraInfoEnabledKey) != 1 {
43		return metadata, false
44	}
45	metadata.ParentURI = extraInfo.GetString(popupOpenerExtraInfoParentURIKey)
46	metadata.BridgeNonce = extraInfo.GetString(popupOpenerExtraInfoBridgeNonceKey)
47	if metadata.ParentURI == "" || metadata.BridgeNonce == "" {
48		return popupOpenerRenderMetadata{}, false
49	}
50	return metadata, true
51}
52
53// composedRenderProcessHandler keeps the popup-opener handler as the primary
54// CEF renderer handler and adds only editable-focus reporting from the legacy
55// renderer bridge. Embedding preserves every popup callback without enabling
56// the bridge's V8 extension in OnWebKitInitialized.
57type composedRenderProcessHandler struct {
58	*popupOpenerRenderProcessHandler
59	editableFocusHandler *rendererBridgeProcessHandler
60}
61
62func newComposedRenderProcessHandler() *composedRenderProcessHandler {
63	return &composedRenderProcessHandler{
64		popupOpenerRenderProcessHandler: newPopupOpenerRenderProcessHandler(),
65		editableFocusHandler:            &rendererBridgeProcessHandler{},
66	}
67}
68
69func (h *composedRenderProcessHandler) OnFocusedNodeChanged(
70	browser purecef.Browser,
71	frame purecef.Frame,
72	node purecef.Domnode,
73) {
74	h.popupOpenerRenderProcessHandler.OnFocusedNodeChanged(browser, frame, node)
75	h.editableFocusHandler.OnFocusedNodeChanged(browser, frame, node)
76}
77
78type popupOpenerRenderProcessHandler struct {
79	mu          sync.RWMutex
80	byBrowserID map[int32]popupOpenerRenderMetadata
81}
82
83func newPopupOpenerRenderProcessHandler() *popupOpenerRenderProcessHandler {
84	return &popupOpenerRenderProcessHandler{byBrowserID: make(map[int32]popupOpenerRenderMetadata)}
85}
86
87func (*popupOpenerRenderProcessHandler) OnWebKitInitialized() {}
88
89func (h *popupOpenerRenderProcessHandler) OnBrowserCreated(browser purecef.Browser, extraInfo purecef.DictionaryValue) {
90	if h == nil || browser == nil {
91		return
92	}
93	metadata, ok := decodePopupOpenerRenderMetadata(extraInfo)
94	if !ok {
95		return
96	}
97	browserID := browser.GetIdentifier()
98	h.mu.Lock()
99	h.byBrowserID[browserID] = metadata
100	h.mu.Unlock()
101}
102
103func (h *popupOpenerRenderProcessHandler) OnBrowserDestroyed(browser purecef.Browser) {
104	if h == nil || browser == nil {
105		return
106	}
107	browserID := browser.GetIdentifier()
108	h.mu.Lock()
109	delete(h.byBrowserID, browserID)
110	h.mu.Unlock()
111}
112
113func (*popupOpenerRenderProcessHandler) GetLoadHandler() purecef.LoadHandler { return nil }
114
115func (h *popupOpenerRenderProcessHandler) OnContextCreated(browser purecef.Browser, frame purecef.Frame, ctx purecef.V8Context) {
116	if h == nil || browser == nil || frame == nil || !frame.IsMain() || ctx == nil || !ctx.IsValid() {
117		return
118	}
119	browserID := browser.GetIdentifier()
120	h.mu.RLock()
121	metadata, ok := h.byBrowserID[browserID]
122	h.mu.RUnlock()
123	if !ok {
124		return
125	}
126
127	script := buildPopupOpenerBridgeJS(metadata.BridgeNonce, metadata.ParentURI)
128	if script == "" {
129		return
130	}
131	if ctx.Enter() != 1 {
132		logging.FromContext(context.Background()).Warn().
133			Int32("browser_id", browserID).
134			Msg("cef: popup opener renderer bridge could not enter V8 context")
135		return
136	}
137	defer ctx.Exit()
138	if ctx.Eval(script, popupOpenerRenderScriptURL, 0, nil, nil) != 1 {
139		logging.FromContext(context.Background()).Warn().
140			Int32("browser_id", browserID).
141			Msg("cef: popup opener renderer bridge eval failed")
142	}
143}
144
145func (*popupOpenerRenderProcessHandler) OnContextReleased(purecef.Browser, purecef.Frame, purecef.V8Context) {
146}
147
148func (*popupOpenerRenderProcessHandler) OnUncaughtException(
149	purecef.Browser,
150	purecef.Frame,
151	purecef.V8Context,
152	purecef.V8Exception,
153	purecef.V8StackTrace,
154) {
155}
156
157func (*popupOpenerRenderProcessHandler) OnFocusedNodeChanged(purecef.Browser, purecef.Frame, purecef.Domnode) {
158}
159
160func (*popupOpenerRenderProcessHandler) OnProcessMessageReceived(
161	purecef.Browser,
162	purecef.Frame,
163	purecef.ProcessID,
164	purecef.ProcessMessage,
165) int32 {
166	return 0
167}

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