v0.30.0 eb3068f

browsing_context_policy.go RawHistoryFolder

1package content
2
3import (
4	"strings"
5
6	"github.com/bnema/dumber/internal/application/dto"
7	"github.com/bnema/dumber/internal/application/port"
8)
9
10type browsingContextPolicy struct{}
11
12func (browsingContextPolicy) Decide(req dto.NewBrowsingContextRequest, namedContextExists bool) dto.HostDecision {
13	decision := dto.HostDecision{
14		RequestContextDisposition: req.RequestContextDisposition,
15		RequiresNativeOpener:      req.RequiresNativeOpener,
16	}
17	if decision.RequestContextDisposition == "" {
18		decision.RequestContextDisposition = dto.RequestContextInheritParent
19	}
20
21	if strings.TrimSpace(req.TargetURI) == "" {
22		decision.Kind = dto.HostDecisionDeny
23		decision.Reason = "empty target URI"
24		return decision
25	}
26
27	if req.AuthIntent {
28		decision.Kind = dto.HostDecisionCreateNativeWin
29		decision.Reason = "auth intent requires native window"
30		return decision
31	}
32	if req.RequiresNativeOpener {
33		decision.Kind = dto.HostDecisionCreateNativeWin
34		decision.Reason = "native opener required"
35		return decision
36	}
37	if isAmbiguousNativeBrowsingContext(req) {
38		decision.Kind = dto.HostDecisionCreateNativeWin
39		decision.Reason = "ambiguous opener-coupled request prefers native window"
40		return decision
41	}
42
43	name := reusableBrowsingContextName(req.TargetFrameName)
44	if namedContextExists && name != "" {
45		decision.Kind = dto.HostDecisionReuseNamedPane
46		decision.ReuseContextName = name
47		decision.BrowsingContextName = name
48		decision.Reason = "reuse named browsing context in current window"
49		return decision
50	}
51
52	decision.Kind = dto.HostDecisionCreatePane
53	decision.BrowsingContextName = name
54	decision.Reason = "pane-hosted browsing context"
55	return decision
56}
57
58func buildPopupBrowsingContextRequest(req port.PopupRequest) dto.NewBrowsingContextRequest {
59	return dto.NewBrowsingContextRequest{
60		ParentWebViewID:           uint64(req.ParentViewID),
61		SourceBrowserID:           req.SourceBrowserID,
62		SourceFrameID:             req.SourceFrameID,
63		SourceFrameURL:            req.SourceFrameURL,
64		TargetURI:                 req.TargetURI,
65		TargetFrameName:           req.FrameName,
66		TargetDisposition:         inferPopupWindowDisposition(req),
67		IsUserGesture:             req.IsUserGesture,
68		NoJavaScriptAccess:        req.NoJavaScriptAccess,
69		WindowFeatures:            req.WindowFeatures,
70		TriggerKind:               inferPopupTriggerKind(req),
71		AuthIntent:                IsOAuthURL(req.TargetURI),
72		RequiresNativeOpener:      false,
73		RequestContextDisposition: dto.RequestContextInheritParent,
74	}
75}
76
77func buildLinkBrowsingContextRequest(parentWebViewID port.WebViewID, uri string) dto.NewBrowsingContextRequest {
78	return dto.NewBrowsingContextRequest{
79		ParentWebViewID:           uint64(parentWebViewID),
80		TargetURI:                 uri,
81		TargetFrameName:           "_blank",
82		TargetDisposition:         dto.WindowDispositionNewTab,
83		IsUserGesture:             true,
84		NoJavaScriptAccess:        true,
85		TriggerKind:               dto.TriggerLinkNewPage,
86		AuthIntent:                IsOAuthURL(uri),
87		RequestContextDisposition: dto.RequestContextInheritParent,
88	}
89}
90
91func inferPopupWindowDisposition(req port.PopupRequest) dto.WindowDisposition {
92	if req.TargetDisposition != "" {
93		return req.TargetDisposition
94	}
95	if strings.EqualFold(strings.TrimSpace(req.FrameName), "_blank") {
96		return dto.WindowDispositionNewTab
97	}
98	return dto.WindowDispositionNewPopup
99}
100
101func inferPopupTriggerKind(req port.PopupRequest) dto.TriggerKind {
102	switch inferPopupWindowDisposition(req) {
103	case dto.WindowDispositionCurrentTab:
104		if reusableBrowsingContextName(req.FrameName) != "" {
105			return dto.TriggerNamedTargetNavigation
106		}
107		return dto.TriggerLinkNewPage
108	case dto.WindowDispositionNewTab:
109		if reusableBrowsingContextName(req.FrameName) != "" {
110			return dto.TriggerNamedTargetNavigation
111		}
112		return dto.TriggerLinkNewPage
113	case dto.WindowDispositionNewPopup, dto.WindowDispositionNewWindow:
114		if reusableBrowsingContextName(req.FrameName) != "" {
115			return dto.TriggerNamedTargetNavigation
116		}
117		return dto.TriggerScriptWindowOpen
118	default:
119		if reusableBrowsingContextName(req.FrameName) != "" {
120			return dto.TriggerNamedTargetNavigation
121		}
122		return dto.TriggerUnknown
123	}
124}
125
126func isAmbiguousNativeBrowsingContext(req dto.NewBrowsingContextRequest) bool {
127	return req.TriggerKind == dto.TriggerUnknown && !req.NoJavaScriptAccess
128}
129
130func reusableBrowsingContextName(frameName string) string {
131	name := strings.TrimSpace(frameName)
132	if name == "" || strings.EqualFold(name, "_blank") {
133		return ""
134	}
135	return name
136}

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