main 5041353

browsing_context_policy.go RawHistoryFolder

1package usecase
2
3import (
4	"strings"
5
6	"github.com/bnema/dumber/internal/application/dto"
7)
8
9// BrowsingContextPolicy selects the product host for a normalized browsing-context request.
10type BrowsingContextPolicy struct{}
11
12// Decide returns the host decision without performing any I/O.
13func (BrowsingContextPolicy) Decide(req dto.NewBrowsingContextRequest, namedContextExists bool) dto.HostDecision {
14	decision := dto.HostDecision{
15		SourceHost:                req.SourceHost,
16		RequestContextDisposition: req.RequestContextDisposition,
17		RequiresNativeOpener:      req.RequiresNativeOpener,
18	}
19	if decision.RequestContextDisposition == "" {
20		decision.RequestContextDisposition = dto.RequestContextInheritParent
21	}
22
23	switch {
24	case strings.TrimSpace(req.TargetURI) == "":
25		return completeHostDecision(decision, dto.HostDecisionDeny, dto.HostDecisionReasonEmptyTarget, "empty target URI")
26	case req.AuthIntent:
27		return completeHostDecision(
28			decision,
29			dto.HostDecisionCreateNativePopup,
30			dto.HostDecisionReasonAuthNativePopup,
31			"authentication intent requires a related native popup",
32		)
33	case req.RequiresNativeOpener:
34		return completeHostDecision(
35			decision,
36			dto.HostDecisionCreateNativePopup,
37			dto.HostDecisionReasonNativeOpenerRequired,
38			"native opener relationship is required",
39		)
40	case isAmbiguousNativeBrowsingContext(req):
41		return completeHostDecision(
42			decision,
43			dto.HostDecisionCreateNativePopup,
44			dto.HostDecisionReasonAmbiguousOpenerNative,
45			"ambiguous opener-coupled request prefers a related native popup",
46		)
47	}
48
49	name := ReusableBrowsingContextName(req.TargetFrameName)
50	if namedContextExists && name != "" && !req.NoJavaScriptAccess {
51		decision.ReuseContextName = name
52		decision.BrowsingContextName = name
53		return completeHostDecision(
54			decision,
55			dto.HostDecisionReuseNamedPane,
56			dto.HostDecisionReasonNamedContextReuse,
57			"reuse live named browsing context",
58		)
59	}
60	decision.BrowsingContextName = name
61
62	if req.SourceHost == dto.SourceHostFloating {
63		return decideFloatingHost(decision, req)
64	}
65	return completeHostDecision(
66		decision,
67		dto.HostDecisionCreatePane,
68		dto.HostDecisionReasonWorkspacePane,
69		"workspace request uses pane hosting",
70	)
71}
72
73func decideFloatingHost(decision dto.HostDecision, req dto.NewBrowsingContextRequest) dto.HostDecision {
74	switch {
75	case req.PopupFeatures.State == dto.PopupFeaturesUnknown && req.TriggerKind != dto.TriggerLinkNewPage:
76		return completeHostDecision(
77			decision,
78			dto.HostDecisionAwaitPopupFeatures,
79			dto.HostDecisionReasonFloatingFeaturesPending,
80			"popup features unavailable until ready-to-show",
81		)
82	case popupFeaturesRequestHost(req.PopupFeatures):
83		return completeHostDecision(
84			decision,
85			dto.HostDecisionCreateNativePopup,
86			dto.HostDecisionReasonFloatingPopupFeatures,
87			"explicit popup features requested",
88		)
89	case ReusableBrowsingContextName(req.TargetFrameName) == "":
90		return completeHostDecision(
91			decision,
92			dto.HostDecisionNavigateSource,
93			dto.HostDecisionReasonFloatingFeaturelessBlank,
94			"featureless blank request navigates the source floating pane",
95		)
96	default:
97		return completeHostDecision(
98			decision,
99			dto.HostDecisionCreateBrowserWindow,
100			dto.HostDecisionReasonFloatingBrowserWindow,
101			"floating request opens a detached browser window",
102		)
103	}
104}
105
106func popupFeaturesRequestHost(features dto.PopupFeatures) bool {
107	return features.State == dto.PopupFeaturesSpecified &&
108		((features.WidthSet && features.Width > 0) ||
109			(features.HeightSet && features.Height > 0) ||
110			(features.ToolbarVisibilitySet && !features.ToolbarVisible) ||
111			(features.LocationbarVisibilitySet && !features.LocationbarVisible) ||
112			(features.ResizableSet && !features.Resizable) ||
113			(features.IsPopupSet && features.IsPopup))
114}
115
116func completeHostDecision(
117	decision dto.HostDecision,
118	kind dto.HostDecisionKind,
119	code dto.HostDecisionReasonCode,
120	reason string,
121) dto.HostDecision {
122	decision.Kind = kind
123	decision.ReasonCode = code
124	decision.Reason = reason
125	return decision
126}
127
128func isAmbiguousNativeBrowsingContext(req dto.NewBrowsingContextRequest) bool {
129	return req.TriggerKind == dto.TriggerUnknown && !req.NoJavaScriptAccess
130}
131
132// ReusableBrowsingContextName returns a live-context key for named targets.
133func ReusableBrowsingContextName(frameName string) string {
134	name := strings.TrimSpace(frameName)
135	if name == "" || strings.EqualFold(name, "_blank") {
136		return ""
137	}
138	return name
139}

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