detached 467e6a6

Merge pull request #198 from bnema/fix/permission-origin-and-dep-chain

fix: permission origin + cookie policy + puregotk migration
commit
467e6a6f51f10b2fd7bdaa727e68b56ac65e1398
author
bnema · · 2026-03-25 21:21 UTC
committer
GitHub · · 2026-03-25 21:21 UTC
merge of
2f35bdf + a6de727 · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
70 files · +256 -188
M cmd/dumber/main.go +1 -1
@@ -34,7 +34,7 @@ import (
3434 "github.com/bnema/dumber/internal/ui/adapter"
3535 "github.com/bnema/dumber/internal/ui/component"
3636 "github.com/bnema/dumber/internal/ui/theme"
37- "github.com/jwijenbergh/puregotk/v4/gtk"
37+ "github.com/bnema/puregotk/v4/gtk"
3838 "github.com/rs/zerolog"
3939 )
4040
@@ -34,7 +34,7 @@ import (
34 "github.com/bnema/dumber/internal/ui/adapter"34 "github.com/bnema/dumber/internal/ui/adapter"
35 "github.com/bnema/dumber/internal/ui/component"35 "github.com/bnema/dumber/internal/ui/component"
36 "github.com/bnema/dumber/internal/ui/theme"36 "github.com/bnema/dumber/internal/ui/theme"
37- "github.com/jwijenbergh/puregotk/v4/gtk"37+ "github.com/bnema/puregotk/v4/gtk"
38 "github.com/rs/zerolog"38 "github.com/rs/zerolog"
39 )39 )
40 40
M go.mod +5 -5
@@ -1,16 +1,16 @@
11 module github.com/bnema/dumber
22
3-go 1.26
3+go 1.26.1
44
55 require (
6- github.com/bnema/puregotk-webkit v0.0.14
6+ github.com/bnema/puregotk v0.2.0
7+ github.com/bnema/puregotk-webkit v0.0.15
78 github.com/charmbracelet/bubbles v1.0.0
89 github.com/charmbracelet/bubbletea v1.3.10
910 github.com/charmbracelet/lipgloss v1.1.0
11+ github.com/ebitengine/purego v0.10.0
1012 github.com/fsnotify/fsnotify v1.9.0
1113 github.com/godbus/dbus/v5 v5.2.2
12- github.com/jwijenbergh/purego v0.0.0-20251017112123-b71757b9ba42
13- github.com/jwijenbergh/puregotk v0.0.0-20260212181341-fb13c245f9f0
1414 github.com/ncruces/go-sqlite3 v0.32.0
1515 github.com/pelletier/go-toml/v2 v2.2.4
1616 github.com/pressly/goose/v3 v3.27.0
@@ -23,7 +23,7 @@ require (
2323 gopkg.in/yaml.v3 v3.0.1
2424 )
2525
26-replace github.com/jwijenbergh/puregotk => github.com/bnema/puregotk v0.1.0
26+replace github.com/ebitengine/purego => github.com/bnema/purego v0.0.0-20260323095903-91aa177a2704
2727
2828 require (
2929 github.com/atotto/clipboard v0.1.4 // indirect
@@ -1,16 +1,16 @@
1 module github.com/bnema/dumber1 module github.com/bnema/dumber
2 2
3-go 1.263+go 1.26.1
4 4
5 require (5 require (
6- github.com/bnema/puregotk-webkit v0.0.146+ github.com/bnema/puregotk v0.2.0
7+ github.com/bnema/puregotk-webkit v0.0.15
7 github.com/charmbracelet/bubbles v1.0.08 github.com/charmbracelet/bubbles v1.0.0
8 github.com/charmbracelet/bubbletea v1.3.109 github.com/charmbracelet/bubbletea v1.3.10
9 github.com/charmbracelet/lipgloss v1.1.010 github.com/charmbracelet/lipgloss v1.1.0
11+ github.com/ebitengine/purego v0.10.0
10 github.com/fsnotify/fsnotify v1.9.012 github.com/fsnotify/fsnotify v1.9.0
11 github.com/godbus/dbus/v5 v5.2.213 github.com/godbus/dbus/v5 v5.2.2
12- github.com/jwijenbergh/purego v0.0.0-20251017112123-b71757b9ba42
13- github.com/jwijenbergh/puregotk v0.0.0-20260212181341-fb13c245f9f0
14 github.com/ncruces/go-sqlite3 v0.32.014 github.com/ncruces/go-sqlite3 v0.32.0
15 github.com/pelletier/go-toml/v2 v2.2.415 github.com/pelletier/go-toml/v2 v2.2.4
16 github.com/pressly/goose/v3 v3.27.016 github.com/pressly/goose/v3 v3.27.0
@@ -23,7 +23,7 @@ require (
23 gopkg.in/yaml.v3 v3.0.123 gopkg.in/yaml.v3 v3.0.1
24 )24 )
25 25
26-replace github.com/jwijenbergh/puregotk => github.com/bnema/puregotk v0.1.026+replace github.com/ebitengine/purego => github.com/bnema/purego v0.0.0-20260323095903-91aa177a2704
27 27
28 require (28 require (
29 github.com/atotto/clipboard v0.1.4 // indirect29 github.com/atotto/clipboard v0.1.4 // indirect
M go.sum +6 -6 generated file: collapsed
M internal/application/port/mocks/mocks.go +14 -8
@@ -8211,8 +8211,8 @@ func (_m *MockPermissionDialogPresenter) EXPECT() *MockPermissionDialogPresenter
82118211 }
82128212
82138213 // ShowPermissionDialog provides a mock function for the type MockPermissionDialogPresenter
8214-func (_mock *MockPermissionDialogPresenter) ShowPermissionDialog(ctx context.Context, origin string, permTypes []entity.PermissionType, callback func(result port.PermissionDialogResult)) {
8215- _mock.Called(ctx, origin, permTypes, callback)
8214+func (_mock *MockPermissionDialogPresenter) ShowPermissionDialog(ctx context.Context, origin string, permTypes []entity.PermissionType, metadata entity.PermissionMetadata, callback func(result port.PermissionDialogResult)) {
8215+ _mock.Called(ctx, origin, permTypes, metadata, callback)
82168216 return
82178217 }
82188218
@@ -8225,12 +8225,13 @@ type MockPermissionDialogPresenter_ShowPermissionDialog_Call struct {
82258225 // - ctx context.Context
82268226 // - origin string
82278227 // - permTypes []entity.PermissionType
8228+// - metadata entity.PermissionMetadata
82288229 // - callback func(result port.PermissionDialogResult)
8229-func (_e *MockPermissionDialogPresenter_Expecter) ShowPermissionDialog(ctx interface{}, origin interface{}, permTypes interface{}, callback interface{}) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
8230- return &MockPermissionDialogPresenter_ShowPermissionDialog_Call{Call: _e.mock.On("ShowPermissionDialog", ctx, origin, permTypes, callback)}
8230+func (_e *MockPermissionDialogPresenter_Expecter) ShowPermissionDialog(ctx interface{}, origin interface{}, permTypes interface{}, metadata interface{}, callback interface{}) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
8231+ return &MockPermissionDialogPresenter_ShowPermissionDialog_Call{Call: _e.mock.On("ShowPermissionDialog", ctx, origin, permTypes, metadata, callback)}
82318232 }
82328233
8233-func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) Run(run func(ctx context.Context, origin string, permTypes []entity.PermissionType, callback func(result port.PermissionDialogResult))) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
8234+func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) Run(run func(ctx context.Context, origin string, permTypes []entity.PermissionType, metadata entity.PermissionMetadata, callback func(result port.PermissionDialogResult))) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
82348235 _c.Call.Run(func(args mock.Arguments) {
82358236 var arg0 context.Context
82368237 if args[0] != nil {
@@ -8244,15 +8245,20 @@ func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) Run(run func(
82448245 if args[2] != nil {
82458246 arg2 = args[2].([]entity.PermissionType)
82468247 }
8247- var arg3 func(result port.PermissionDialogResult)
8248+ var arg3 entity.PermissionMetadata
82488249 if args[3] != nil {
8249- arg3 = args[3].(func(result port.PermissionDialogResult))
8250+ arg3 = args[3].(entity.PermissionMetadata)
8251+ }
8252+ var arg4 func(result port.PermissionDialogResult)
8253+ if args[4] != nil {
8254+ arg4 = args[4].(func(result port.PermissionDialogResult))
82508255 }
82518256 run(
82528257 arg0,
82538258 arg1,
82548259 arg2,
82558260 arg3,
8261+ arg4,
82568262 )
82578263 })
82588264 return _c
@@ -8263,7 +8269,7 @@ func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) Return() *Moc
82638269 return _c
82648270 }
82658271
8266-func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) RunAndReturn(run func(ctx context.Context, origin string, permTypes []entity.PermissionType, callback func(result port.PermissionDialogResult))) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
8272+func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) RunAndReturn(run func(ctx context.Context, origin string, permTypes []entity.PermissionType, metadata entity.PermissionMetadata, callback func(result port.PermissionDialogResult))) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
82678273 _c.Run(run)
82688274 return _c
82698275 }
@@ -8211,8 +8211,8 @@ func (_m *MockPermissionDialogPresenter) EXPECT() *MockPermissionDialogPresenter
8211 }8211 }
8212 8212
8213 // ShowPermissionDialog provides a mock function for the type MockPermissionDialogPresenter8213 // ShowPermissionDialog provides a mock function for the type MockPermissionDialogPresenter
8214-func (_mock *MockPermissionDialogPresenter) ShowPermissionDialog(ctx context.Context, origin string, permTypes []entity.PermissionType, callback func(result port.PermissionDialogResult)) {8214+func (_mock *MockPermissionDialogPresenter) ShowPermissionDialog(ctx context.Context, origin string, permTypes []entity.PermissionType, metadata entity.PermissionMetadata, callback func(result port.PermissionDialogResult)) {
8215- _mock.Called(ctx, origin, permTypes, callback)8215+ _mock.Called(ctx, origin, permTypes, metadata, callback)
8216 return8216 return
8217 }8217 }
8218 8218
@@ -8225,12 +8225,13 @@ type MockPermissionDialogPresenter_ShowPermissionDialog_Call struct {
8225 // - ctx context.Context8225 // - ctx context.Context
8226 // - origin string8226 // - origin string
8227 // - permTypes []entity.PermissionType8227 // - permTypes []entity.PermissionType
8228+// - metadata entity.PermissionMetadata
8228 // - callback func(result port.PermissionDialogResult)8229 // - callback func(result port.PermissionDialogResult)
8229-func (_e *MockPermissionDialogPresenter_Expecter) ShowPermissionDialog(ctx interface{}, origin interface{}, permTypes interface{}, callback interface{}) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {8230+func (_e *MockPermissionDialogPresenter_Expecter) ShowPermissionDialog(ctx interface{}, origin interface{}, permTypes interface{}, metadata interface{}, callback interface{}) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
8230- return &MockPermissionDialogPresenter_ShowPermissionDialog_Call{Call: _e.mock.On("ShowPermissionDialog", ctx, origin, permTypes, callback)}8231+ return &MockPermissionDialogPresenter_ShowPermissionDialog_Call{Call: _e.mock.On("ShowPermissionDialog", ctx, origin, permTypes, metadata, callback)}
8231 }8232 }
8232 8233
8233-func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) Run(run func(ctx context.Context, origin string, permTypes []entity.PermissionType, callback func(result port.PermissionDialogResult))) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {8234+func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) Run(run func(ctx context.Context, origin string, permTypes []entity.PermissionType, metadata entity.PermissionMetadata, callback func(result port.PermissionDialogResult))) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
8234 _c.Call.Run(func(args mock.Arguments) {8235 _c.Call.Run(func(args mock.Arguments) {
8235 var arg0 context.Context8236 var arg0 context.Context
8236 if args[0] != nil {8237 if args[0] != nil {
@@ -8244,15 +8245,20 @@ func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) Run(run func(
8244 if args[2] != nil {8245 if args[2] != nil {
8245 arg2 = args[2].([]entity.PermissionType)8246 arg2 = args[2].([]entity.PermissionType)
8246 }8247 }
8247- var arg3 func(result port.PermissionDialogResult)8248+ var arg3 entity.PermissionMetadata
8248 if args[3] != nil {8249 if args[3] != nil {
8249- arg3 = args[3].(func(result port.PermissionDialogResult))8250+ arg3 = args[3].(entity.PermissionMetadata)
8251+ }
8252+ var arg4 func(result port.PermissionDialogResult)
8253+ if args[4] != nil {
8254+ arg4 = args[4].(func(result port.PermissionDialogResult))
8250 }8255 }
8251 run(8256 run(
8252 arg0,8257 arg0,
8253 arg1,8258 arg1,
8254 arg2,8259 arg2,
8255 arg3,8260 arg3,
8261+ arg4,
8256 )8262 )
8257 })8263 })
8258 return _c8264 return _c
@@ -8263,7 +8269,7 @@ func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) Return() *Moc
8263 return _c8269 return _c
8264 }8270 }
8265 8271
8266-func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) RunAndReturn(run func(ctx context.Context, origin string, permTypes []entity.PermissionType, callback func(result port.PermissionDialogResult))) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {8272+func (_c *MockPermissionDialogPresenter_ShowPermissionDialog_Call) RunAndReturn(run func(ctx context.Context, origin string, permTypes []entity.PermissionType, metadata entity.PermissionMetadata, callback func(result port.PermissionDialogResult))) *MockPermissionDialogPresenter_ShowPermissionDialog_Call {
8267 _c.Run(run)8273 _c.Run(run)
8268 return _c8274 return _c
8269 }8275 }
M internal/application/port/permission.go +3 -0
@@ -26,11 +26,14 @@ type PermissionDialogPresenter interface {
2626 // - ctx: context for cancellation and logging
2727 // - origin: the website origin requesting permission (e.g., "https://meet.example.com")
2828 // - permTypes: the types of permissions being requested
29+ // - metadata: permission-type-specific context; for website_data_access both
30+ // entity.PermissionMetadataKeyRequestingDomain and entity.PermissionMetadataKeyCurrentDomain must be set
2931 // - callback: invoked when the user makes a decision or dialog is dismissed
3032 ShowPermissionDialog(
3133 ctx context.Context,
3234 origin string,
3335 permTypes []entity.PermissionType,
36+ metadata entity.PermissionMetadata,
3437 callback func(result PermissionDialogResult),
3538 )
3639 }
@@ -26,11 +26,14 @@ type PermissionDialogPresenter interface {
26 // - ctx: context for cancellation and logging26 // - ctx: context for cancellation and logging
27 // - origin: the website origin requesting permission (e.g., "https://meet.example.com")27 // - origin: the website origin requesting permission (e.g., "https://meet.example.com")
28 // - permTypes: the types of permissions being requested28 // - permTypes: the types of permissions being requested
29+ // - metadata: permission-type-specific context; for website_data_access both
30+ // entity.PermissionMetadataKeyRequestingDomain and entity.PermissionMetadataKeyCurrentDomain must be set
29 // - callback: invoked when the user makes a decision or dialog is dismissed31 // - callback: invoked when the user makes a decision or dialog is dismissed
30 ShowPermissionDialog(32 ShowPermissionDialog(
31 ctx context.Context,33 ctx context.Context,
32 origin string,34 origin string,
33 permTypes []entity.PermissionType,35 permTypes []entity.PermissionType,
36+ metadata entity.PermissionMetadata,
34 callback func(result PermissionDialogResult),37 callback func(result PermissionDialogResult),
35 )38 )
36 }39 }
M internal/application/port/webview.go +3 -1
@@ -107,7 +107,9 @@ type WebViewCallbacks struct {
107107 // OnPermissionRequest is called when a site requests permission (mic, camera, screen sharing).
108108 // Return true to indicate the request was handled. Call allow()/deny() to respond.
109109 // The permission types are passed as strings ("microphone", "camera", "display", etc.).
110- OnPermissionRequest func(origin string, permTypes []string, allow, deny func()) bool
110+ // The metadata map carries permission-type-specific context; for website_data_access both
111+ // entity.PermissionMetadataKeyRequestingDomain and entity.PermissionMetadataKeyCurrentDomain must be populated.
112+ OnPermissionRequest func(origin string, permTypes []string, metadata map[string]string, allow, deny func()) bool
111113
112114 // OnLinkMiddleClick is called when a link is middle-clicked.
113115 // Return true if handled (blocks default navigation).
@@ -107,7 +107,9 @@ type WebViewCallbacks struct {
107 // OnPermissionRequest is called when a site requests permission (mic, camera, screen sharing).107 // OnPermissionRequest is called when a site requests permission (mic, camera, screen sharing).
108 // Return true to indicate the request was handled. Call allow()/deny() to respond.108 // Return true to indicate the request was handled. Call allow()/deny() to respond.
109 // The permission types are passed as strings ("microphone", "camera", "display", etc.).109 // The permission types are passed as strings ("microphone", "camera", "display", etc.).
110- OnPermissionRequest func(origin string, permTypes []string, allow, deny func()) bool110+ // The metadata map carries permission-type-specific context; for website_data_access both
111+ // entity.PermissionMetadataKeyRequestingDomain and entity.PermissionMetadataKeyCurrentDomain must be populated.
112+ OnPermissionRequest func(origin string, permTypes []string, metadata map[string]string, allow, deny func()) bool
111 113
112 // OnLinkMiddleClick is called when a link is middle-clicked.114 // OnLinkMiddleClick is called when a link is middle-clicked.
113 // Return true if handled (blocks default navigation).115 // Return true if handled (blocks default navigation).
M internal/application/usecase/handle_permission.go +7 -2
@@ -79,11 +79,15 @@ func (uc *HandlePermissionUseCase) getDialog() port.PermissionDialogPresenter {
7979 // - ctx: context for cancellation and logging
8080 // - origin: the website origin (URI) requesting permission
8181 // - permTypes: the types of permissions being requested
82+// - metadata: permission-type-specific context; for website_data_access both keys must be present:
83+// entity.PermissionMetadataKeyRequestingDomain — the origin requesting cross-site data access
84+// entity.PermissionMetadataKeyCurrentDomain — the domain currently loaded in the WebView
8285 // - callback: callbacks to allow or deny the request (must call one)
8386 func (uc *HandlePermissionUseCase) HandlePermissionRequest(
8487 ctx context.Context,
8588 origin string,
8689 permTypes []entity.PermissionType,
90+ metadata entity.PermissionMetadata,
8791 callback PermissionCallback,
8892 ) {
8993 log := uc.logger(ctx).With().
@@ -135,7 +139,7 @@ func (uc *HandlePermissionUseCase) HandlePermissionRequest(
135139 }
136140
137141 // Show dialog for undecided permissions
138- uc.showPermissionDialog(ctx, origin, permTypes, callback)
142+ uc.showPermissionDialog(ctx, origin, permTypes, metadata, callback)
139143 }
140144
141145 // QueryPermissionState returns the current permission state for the W3C Permissions API.
@@ -249,6 +253,7 @@ func (uc *HandlePermissionUseCase) showPermissionDialog(
249253 ctx context.Context,
250254 origin string,
251255 permTypes []entity.PermissionType,
256+ metadata entity.PermissionMetadata,
252257 callback PermissionCallback,
253258 ) {
254259 log := uc.logger(ctx)
@@ -262,7 +267,7 @@ func (uc *HandlePermissionUseCase) showPermissionDialog(
262267 return
263268 }
264269
265- dialog.ShowPermissionDialog(ctx, origin, permTypes, func(result port.PermissionDialogResult) {
270+ dialog.ShowPermissionDialog(ctx, origin, permTypes, metadata, func(result port.PermissionDialogResult) {
266271 if result.Allowed {
267272 log.Debug().Bool("persistent", result.Persistent).Msg("user allowed permission")
268273 callback.Allow()
@@ -79,11 +79,15 @@ func (uc *HandlePermissionUseCase) getDialog() port.PermissionDialogPresenter {
79 // - ctx: context for cancellation and logging79 // - ctx: context for cancellation and logging
80 // - origin: the website origin (URI) requesting permission80 // - origin: the website origin (URI) requesting permission
81 // - permTypes: the types of permissions being requested81 // - permTypes: the types of permissions being requested
82+// - metadata: permission-type-specific context; for website_data_access both keys must be present:
83+// entity.PermissionMetadataKeyRequestingDomain — the origin requesting cross-site data access
84+// entity.PermissionMetadataKeyCurrentDomain — the domain currently loaded in the WebView
82 // - callback: callbacks to allow or deny the request (must call one)85 // - callback: callbacks to allow or deny the request (must call one)
83 func (uc *HandlePermissionUseCase) HandlePermissionRequest(86 func (uc *HandlePermissionUseCase) HandlePermissionRequest(
84 ctx context.Context,87 ctx context.Context,
85 origin string,88 origin string,
86 permTypes []entity.PermissionType,89 permTypes []entity.PermissionType,
90+ metadata entity.PermissionMetadata,
87 callback PermissionCallback,91 callback PermissionCallback,
88 ) {92 ) {
89 log := uc.logger(ctx).With().93 log := uc.logger(ctx).With().
@@ -135,7 +139,7 @@ func (uc *HandlePermissionUseCase) HandlePermissionRequest(
135 }139 }
136 140
137 // Show dialog for undecided permissions141 // Show dialog for undecided permissions
138- uc.showPermissionDialog(ctx, origin, permTypes, callback)142+ uc.showPermissionDialog(ctx, origin, permTypes, metadata, callback)
139 }143 }
140 144
141 // QueryPermissionState returns the current permission state for the W3C Permissions API.145 // QueryPermissionState returns the current permission state for the W3C Permissions API.
@@ -249,6 +253,7 @@ func (uc *HandlePermissionUseCase) showPermissionDialog(
249 ctx context.Context,253 ctx context.Context,
250 origin string,254 origin string,
251 permTypes []entity.PermissionType,255 permTypes []entity.PermissionType,
256+ metadata entity.PermissionMetadata,
252 callback PermissionCallback,257 callback PermissionCallback,
253 ) {258 ) {
254 log := uc.logger(ctx)259 log := uc.logger(ctx)
@@ -262,7 +267,7 @@ func (uc *HandlePermissionUseCase) showPermissionDialog(
262 return267 return
263 }268 }
264 269
265- dialog.ShowPermissionDialog(ctx, origin, permTypes, func(result port.PermissionDialogResult) {270+ dialog.ShowPermissionDialog(ctx, origin, permTypes, metadata, func(result port.PermissionDialogResult) {
266 if result.Allowed {271 if result.Allowed {
267 log.Debug().Bool("persistent", result.Persistent).Msg("user allowed permission")272 log.Debug().Bool("persistent", result.Persistent).Msg("user allowed permission")
268 callback.Allow()273 callback.Allow()
M internal/application/usecase/handle_permission_test.go +32 -26
@@ -39,7 +39,7 @@ func TestHandlePermissionUseCase_AutoAllowDisplayCapture(t *testing.T) {
3939 // Display capture should be auto-allowed (portal handles UI)
4040 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
4141 entity.PermissionTypeDisplay,
42- }, callback)
42+ }, nil, callback)
4343
4444 assert.True(t, allowed, "display capture should be auto-allowed")
4545 assert.False(t, denied)
@@ -65,7 +65,7 @@ func TestHandlePermissionUseCase_AutoAllowDeviceInfo(t *testing.T) {
6565 // Device info should be auto-allowed
6666 uc.HandlePermissionRequest(ctx, "https://example.com", []entity.PermissionType{
6767 entity.PermissionTypeDeviceInfo,
68- }, callback)
68+ }, nil, callback)
6969
7070 assert.True(t, allowed, "device info should be auto-allowed")
7171 }
@@ -94,7 +94,7 @@ func TestHandlePermissionUseCase_StoredPermissionGranted(t *testing.T) {
9494
9595 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
9696 entity.PermissionTypeMicrophone,
97- }, callback)
97+ }, nil, callback)
9898
9999 assert.True(t, allowed, "should use stored granted permission")
100100 dialog.AssertNotCalled(t, "ShowPermissionDialog")
@@ -124,7 +124,7 @@ func TestHandlePermissionUseCase_StoredPermissionDenied(t *testing.T) {
124124
125125 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
126126 entity.PermissionTypeCamera,
127- }, callback)
127+ }, nil, callback)
128128
129129 assert.True(t, denied, "should use stored denied permission")
130130 dialog.AssertNotCalled(t, "ShowPermissionDialog")
@@ -150,13 +150,13 @@ func TestHandlePermissionUseCase_ShowDialogForPrompt(t *testing.T) {
150150 // Dialog shows and user clicks Allow (not persistent)
151151 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{
152152 entity.PermissionTypeMicrophone,
153- }, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, callback func(port.PermissionDialogResult)) {
153+ }, mock.Anything, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, _ entity.PermissionMetadata, callback func(port.PermissionDialogResult)) {
154154 callback(port.PermissionDialogResult{Allowed: true, Persistent: false})
155155 })
156156
157157 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
158158 entity.PermissionTypeMicrophone,
159- }, callback)
159+ }, nil, callback)
160160
161161 assert.True(t, allowed, "user allowed permission")
162162 // Should not persist since Persistent=false
@@ -183,7 +183,7 @@ func TestHandlePermissionUseCase_PersistAllowed(t *testing.T) {
183183 // Dialog shows and user clicks "Always Allow"
184184 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{
185185 entity.PermissionTypeMicrophone,
186- }, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, cb func(port.PermissionDialogResult)) {
186+ }, mock.Anything, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, _ entity.PermissionMetadata, cb func(port.PermissionDialogResult)) {
187187 cb(port.PermissionDialogResult{Allowed: true, Persistent: true})
188188 })
189189
@@ -197,7 +197,7 @@ func TestHandlePermissionUseCase_PersistAllowed(t *testing.T) {
197197
198198 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
199199 entity.PermissionTypeMicrophone,
200- }, callback)
200+ }, nil, callback)
201201
202202 assert.True(t, allowed)
203203 }
@@ -222,7 +222,7 @@ func TestHandlePermissionUseCase_PersistDenied(t *testing.T) {
222222 // Dialog shows and user clicks "Always Deny"
223223 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{
224224 entity.PermissionTypeCamera,
225- }, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, cb func(port.PermissionDialogResult)) {
225+ }, mock.Anything, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, _ entity.PermissionMetadata, cb func(port.PermissionDialogResult)) {
226226 // User denied but wants to persist this decision
227227 cb(port.PermissionDialogResult{Allowed: false, Persistent: true})
228228 })
@@ -235,7 +235,7 @@ func TestHandlePermissionUseCase_PersistDenied(t *testing.T) {
235235
236236 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
237237 entity.PermissionTypeCamera,
238- }, callback)
238+ }, nil, callback)
239239
240240 assert.True(t, denied)
241241 }
@@ -256,7 +256,7 @@ func TestHandlePermissionUseCase_EmptyOrigin(t *testing.T) {
256256 // Empty origin should be denied
257257 uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{
258258 entity.PermissionTypeMicrophone,
259- }, callback)
259+ }, nil, callback)
260260
261261 assert.True(t, denied, "empty origin should be denied")
262262 }
@@ -292,7 +292,7 @@ func TestHandlePermissionUseCase_CombinedPermissions(t *testing.T) {
292292 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
293293 entity.PermissionTypeMicrophone,
294294 entity.PermissionTypeCamera,
295- }, callback)
295+ }, nil, callback)
296296
297297 assert.True(t, denied, "if any permission is denied, whole request is denied")
298298 }
@@ -398,7 +398,7 @@ func TestHandlePermissionUseCase_NonPersistableNotSaved(t *testing.T) {
398398
399399 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
400400 entity.PermissionTypeDisplay,
401- }, callback)
401+ }, nil, callback)
402402
403403 assert.True(t, allowed)
404404 permRepo.AssertNotCalled(t, "Set")
@@ -423,7 +423,7 @@ func TestHandlePermissionUseCase_NoDialogPresenter(t *testing.T) {
423423
424424 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
425425 entity.PermissionTypeMicrophone,
426- }, callback)
426+ }, nil, callback)
427427
428428 assert.True(t, denied, "should deny when no dialog presenter is available")
429429 }
@@ -450,7 +450,7 @@ func TestHandlePermissionUseCase_DisplayOverrideDenied(t *testing.T) {
450450
451451 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
452452 entity.PermissionTypeDisplay,
453- }, callback)
453+ }, nil, callback)
454454
455455 assert.True(t, denied, "display should be denied when manual override is denied")
456456 dialog.AssertNotCalled(t, "ShowPermissionDialog")
@@ -526,7 +526,7 @@ func TestHandlePermissionUseCase_UsesInjectedLoggerFactory(t *testing.T) {
526526 }
527527
528528 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, loggerFactory)
529- uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{entity.PermissionTypeMicrophone}, usecase.PermissionCallback{
529+ uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{entity.PermissionTypeMicrophone}, nil, usecase.PermissionCallback{
530530 Allow: func() {},
531531 Deny: func() {},
532532 })
@@ -542,7 +542,7 @@ func TestHandlePermissionUseCase_WebsiteDataAccess_ShowsDialogAndPersists(t *tes
542542 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
543543
544544 // No stored permission
545- permRepo.EXPECT().Get(mock.Anything, "https://accounts.google.com", entity.PermissionTypeWebsiteDataAccess).
545+ permRepo.EXPECT().Get(mock.Anything, "https://shop.example.com", entity.PermissionTypeWebsiteDataAccess).
546546 Return(nil, nil)
547547
548548 allowed := false
@@ -551,27 +551,33 @@ func TestHandlePermissionUseCase_WebsiteDataAccess_ShowsDialogAndPersists(t *tes
551551 Deny: func() {},
552552 }
553553
554+ meta := entity.PermissionMetadata{
555+ entity.PermissionMetadataKeyRequestingDomain: "accounts.google.com",
556+ entity.PermissionMetadataKeyCurrentDomain: "shop.example.com",
557+ }
558+
554559 // Dialog shows and user clicks "Always Allow"
555560 dialog.EXPECT().ShowPermissionDialog(
556561 mock.Anything,
557- "https://accounts.google.com",
562+ "https://shop.example.com",
558563 []entity.PermissionType{entity.PermissionTypeWebsiteDataAccess},
564+ meta,
559565 mock.Anything,
560- ).Run(func(_ context.Context, _ string, _ []entity.PermissionType, cb func(port.PermissionDialogResult)) {
566+ ).Run(func(_ context.Context, _ string, _ []entity.PermissionType, _ entity.PermissionMetadata, cb func(port.PermissionDialogResult)) {
561567 cb(port.PermissionDialogResult{Allowed: true, Persistent: true})
562568 })
563569
564570 // Should persist the granted permission
565571 permRepo.EXPECT().Set(mock.Anything, mock.AnythingOfType("*entity.PermissionRecord")).
566572 Run(func(_ context.Context, r *entity.PermissionRecord) {
567- assert.Equal(t, "https://accounts.google.com", r.Origin)
573+ assert.Equal(t, "https://shop.example.com", r.Origin)
568574 assert.Equal(t, entity.PermissionTypeWebsiteDataAccess, r.Type)
569575 assert.Equal(t, entity.PermissionGranted, r.Decision)
570576 }).Return(nil)
571577
572- uc.HandlePermissionRequest(ctx, "https://accounts.google.com", []entity.PermissionType{
578+ uc.HandlePermissionRequest(ctx, "https://shop.example.com", []entity.PermissionType{
573579 entity.PermissionTypeWebsiteDataAccess,
574- }, callback)
580+ }, meta, callback)
575581
576582 assert.True(t, allowed)
577583 }
@@ -584,9 +590,9 @@ func TestHandlePermissionUseCase_WebsiteDataAccess_UsesStoredGrant(t *testing.T)
584590 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
585591
586592 // Stored granted permission
587- permRepo.EXPECT().Get(mock.Anything, "https://accounts.google.com", entity.PermissionTypeWebsiteDataAccess).
593+ permRepo.EXPECT().Get(mock.Anything, "https://shop.example.com", entity.PermissionTypeWebsiteDataAccess).
588594 Return(&entity.PermissionRecord{
589- Origin: "https://accounts.google.com",
595+ Origin: "https://shop.example.com",
590596 Type: entity.PermissionTypeWebsiteDataAccess,
591597 Decision: entity.PermissionGranted,
592598 }, nil)
@@ -597,9 +603,9 @@ func TestHandlePermissionUseCase_WebsiteDataAccess_UsesStoredGrant(t *testing.T)
597603 Deny: func() {},
598604 }
599605
600- uc.HandlePermissionRequest(ctx, "https://accounts.google.com", []entity.PermissionType{
606+ uc.HandlePermissionRequest(ctx, "https://shop.example.com", []entity.PermissionType{
601607 entity.PermissionTypeWebsiteDataAccess,
602- }, callback)
608+ }, entity.PermissionMetadata{entity.PermissionMetadataKeyRequestingDomain: "accounts.google.com", entity.PermissionMetadataKeyCurrentDomain: "shop.example.com"}, callback)
603609
604610 assert.True(t, allowed, "should use stored granted permission without dialog")
605611 dialog.AssertNotCalled(t, "ShowPermissionDialog")
@@ -39,7 +39,7 @@ func TestHandlePermissionUseCase_AutoAllowDisplayCapture(t *testing.T) {
39 // Display capture should be auto-allowed (portal handles UI)39 // Display capture should be auto-allowed (portal handles UI)
40 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{40 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
41 entity.PermissionTypeDisplay,41 entity.PermissionTypeDisplay,
42- }, callback)42+ }, nil, callback)
43 43
44 assert.True(t, allowed, "display capture should be auto-allowed")44 assert.True(t, allowed, "display capture should be auto-allowed")
45 assert.False(t, denied)45 assert.False(t, denied)
@@ -65,7 +65,7 @@ func TestHandlePermissionUseCase_AutoAllowDeviceInfo(t *testing.T) {
65 // Device info should be auto-allowed65 // Device info should be auto-allowed
66 uc.HandlePermissionRequest(ctx, "https://example.com", []entity.PermissionType{66 uc.HandlePermissionRequest(ctx, "https://example.com", []entity.PermissionType{
67 entity.PermissionTypeDeviceInfo,67 entity.PermissionTypeDeviceInfo,
68- }, callback)68+ }, nil, callback)
69 69
70 assert.True(t, allowed, "device info should be auto-allowed")70 assert.True(t, allowed, "device info should be auto-allowed")
71 }71 }
@@ -94,7 +94,7 @@ func TestHandlePermissionUseCase_StoredPermissionGranted(t *testing.T) {
94 94
95 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{95 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
96 entity.PermissionTypeMicrophone,96 entity.PermissionTypeMicrophone,
97- }, callback)97+ }, nil, callback)
98 98
99 assert.True(t, allowed, "should use stored granted permission")99 assert.True(t, allowed, "should use stored granted permission")
100 dialog.AssertNotCalled(t, "ShowPermissionDialog")100 dialog.AssertNotCalled(t, "ShowPermissionDialog")
@@ -124,7 +124,7 @@ func TestHandlePermissionUseCase_StoredPermissionDenied(t *testing.T) {
124 124
125 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{125 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
126 entity.PermissionTypeCamera,126 entity.PermissionTypeCamera,
127- }, callback)127+ }, nil, callback)
128 128
129 assert.True(t, denied, "should use stored denied permission")129 assert.True(t, denied, "should use stored denied permission")
130 dialog.AssertNotCalled(t, "ShowPermissionDialog")130 dialog.AssertNotCalled(t, "ShowPermissionDialog")
@@ -150,13 +150,13 @@ func TestHandlePermissionUseCase_ShowDialogForPrompt(t *testing.T) {
150 // Dialog shows and user clicks Allow (not persistent)150 // Dialog shows and user clicks Allow (not persistent)
151 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{151 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{
152 entity.PermissionTypeMicrophone,152 entity.PermissionTypeMicrophone,
153- }, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, callback func(port.PermissionDialogResult)) {153+ }, mock.Anything, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, _ entity.PermissionMetadata, callback func(port.PermissionDialogResult)) {
154 callback(port.PermissionDialogResult{Allowed: true, Persistent: false})154 callback(port.PermissionDialogResult{Allowed: true, Persistent: false})
155 })155 })
156 156
157 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{157 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
158 entity.PermissionTypeMicrophone,158 entity.PermissionTypeMicrophone,
159- }, callback)159+ }, nil, callback)
160 160
161 assert.True(t, allowed, "user allowed permission")161 assert.True(t, allowed, "user allowed permission")
162 // Should not persist since Persistent=false162 // Should not persist since Persistent=false
@@ -183,7 +183,7 @@ func TestHandlePermissionUseCase_PersistAllowed(t *testing.T) {
183 // Dialog shows and user clicks "Always Allow"183 // Dialog shows and user clicks "Always Allow"
184 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{184 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{
185 entity.PermissionTypeMicrophone,185 entity.PermissionTypeMicrophone,
186- }, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, cb func(port.PermissionDialogResult)) {186+ }, mock.Anything, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, _ entity.PermissionMetadata, cb func(port.PermissionDialogResult)) {
187 cb(port.PermissionDialogResult{Allowed: true, Persistent: true})187 cb(port.PermissionDialogResult{Allowed: true, Persistent: true})
188 })188 })
189 189
@@ -197,7 +197,7 @@ func TestHandlePermissionUseCase_PersistAllowed(t *testing.T) {
197 197
198 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{198 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
199 entity.PermissionTypeMicrophone,199 entity.PermissionTypeMicrophone,
200- }, callback)200+ }, nil, callback)
201 201
202 assert.True(t, allowed)202 assert.True(t, allowed)
203 }203 }
@@ -222,7 +222,7 @@ func TestHandlePermissionUseCase_PersistDenied(t *testing.T) {
222 // Dialog shows and user clicks "Always Deny"222 // Dialog shows and user clicks "Always Deny"
223 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{223 dialog.EXPECT().ShowPermissionDialog(mock.Anything, "https://meet.example.com", []entity.PermissionType{
224 entity.PermissionTypeCamera,224 entity.PermissionTypeCamera,
225- }, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, cb func(port.PermissionDialogResult)) {225+ }, mock.Anything, mock.Anything).Run(func(_ context.Context, _ string, _ []entity.PermissionType, _ entity.PermissionMetadata, cb func(port.PermissionDialogResult)) {
226 // User denied but wants to persist this decision226 // User denied but wants to persist this decision
227 cb(port.PermissionDialogResult{Allowed: false, Persistent: true})227 cb(port.PermissionDialogResult{Allowed: false, Persistent: true})
228 })228 })
@@ -235,7 +235,7 @@ func TestHandlePermissionUseCase_PersistDenied(t *testing.T) {
235 235
236 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{236 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
237 entity.PermissionTypeCamera,237 entity.PermissionTypeCamera,
238- }, callback)238+ }, nil, callback)
239 239
240 assert.True(t, denied)240 assert.True(t, denied)
241 }241 }
@@ -256,7 +256,7 @@ func TestHandlePermissionUseCase_EmptyOrigin(t *testing.T) {
256 // Empty origin should be denied256 // Empty origin should be denied
257 uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{257 uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{
258 entity.PermissionTypeMicrophone,258 entity.PermissionTypeMicrophone,
259- }, callback)259+ }, nil, callback)
260 260
261 assert.True(t, denied, "empty origin should be denied")261 assert.True(t, denied, "empty origin should be denied")
262 }262 }
@@ -292,7 +292,7 @@ func TestHandlePermissionUseCase_CombinedPermissions(t *testing.T) {
292 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{292 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
293 entity.PermissionTypeMicrophone,293 entity.PermissionTypeMicrophone,
294 entity.PermissionTypeCamera,294 entity.PermissionTypeCamera,
295- }, callback)295+ }, nil, callback)
296 296
297 assert.True(t, denied, "if any permission is denied, whole request is denied")297 assert.True(t, denied, "if any permission is denied, whole request is denied")
298 }298 }
@@ -398,7 +398,7 @@ func TestHandlePermissionUseCase_NonPersistableNotSaved(t *testing.T) {
398 398
399 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{399 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
400 entity.PermissionTypeDisplay,400 entity.PermissionTypeDisplay,
401- }, callback)401+ }, nil, callback)
402 402
403 assert.True(t, allowed)403 assert.True(t, allowed)
404 permRepo.AssertNotCalled(t, "Set")404 permRepo.AssertNotCalled(t, "Set")
@@ -423,7 +423,7 @@ func TestHandlePermissionUseCase_NoDialogPresenter(t *testing.T) {
423 423
424 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{424 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
425 entity.PermissionTypeMicrophone,425 entity.PermissionTypeMicrophone,
426- }, callback)426+ }, nil, callback)
427 427
428 assert.True(t, denied, "should deny when no dialog presenter is available")428 assert.True(t, denied, "should deny when no dialog presenter is available")
429 }429 }
@@ -450,7 +450,7 @@ func TestHandlePermissionUseCase_DisplayOverrideDenied(t *testing.T) {
450 450
451 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{451 uc.HandlePermissionRequest(ctx, "https://meet.example.com", []entity.PermissionType{
452 entity.PermissionTypeDisplay,452 entity.PermissionTypeDisplay,
453- }, callback)453+ }, nil, callback)
454 454
455 assert.True(t, denied, "display should be denied when manual override is denied")455 assert.True(t, denied, "display should be denied when manual override is denied")
456 dialog.AssertNotCalled(t, "ShowPermissionDialog")456 dialog.AssertNotCalled(t, "ShowPermissionDialog")
@@ -526,7 +526,7 @@ func TestHandlePermissionUseCase_UsesInjectedLoggerFactory(t *testing.T) {
526 }526 }
527 527
528 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, loggerFactory)528 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, loggerFactory)
529- uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{entity.PermissionTypeMicrophone}, usecase.PermissionCallback{529+ uc.HandlePermissionRequest(ctx, "", []entity.PermissionType{entity.PermissionTypeMicrophone}, nil, usecase.PermissionCallback{
530 Allow: func() {},530 Allow: func() {},
531 Deny: func() {},531 Deny: func() {},
532 })532 })
@@ -542,7 +542,7 @@ func TestHandlePermissionUseCase_WebsiteDataAccess_ShowsDialogAndPersists(t *tes
542 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)542 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
543 543
544 // No stored permission544 // No stored permission
545- permRepo.EXPECT().Get(mock.Anything, "https://accounts.google.com", entity.PermissionTypeWebsiteDataAccess).545+ permRepo.EXPECT().Get(mock.Anything, "https://shop.example.com", entity.PermissionTypeWebsiteDataAccess).
546 Return(nil, nil)546 Return(nil, nil)
547 547
548 allowed := false548 allowed := false
@@ -551,27 +551,33 @@ func TestHandlePermissionUseCase_WebsiteDataAccess_ShowsDialogAndPersists(t *tes
551 Deny: func() {},551 Deny: func() {},
552 }552 }
553 553
554+ meta := entity.PermissionMetadata{
555+ entity.PermissionMetadataKeyRequestingDomain: "accounts.google.com",
556+ entity.PermissionMetadataKeyCurrentDomain: "shop.example.com",
557+ }
558+
554 // Dialog shows and user clicks "Always Allow"559 // Dialog shows and user clicks "Always Allow"
555 dialog.EXPECT().ShowPermissionDialog(560 dialog.EXPECT().ShowPermissionDialog(
556 mock.Anything,561 mock.Anything,
557- "https://accounts.google.com",562+ "https://shop.example.com",
558 []entity.PermissionType{entity.PermissionTypeWebsiteDataAccess},563 []entity.PermissionType{entity.PermissionTypeWebsiteDataAccess},
564+ meta,
559 mock.Anything,565 mock.Anything,
560- ).Run(func(_ context.Context, _ string, _ []entity.PermissionType, cb func(port.PermissionDialogResult)) {566+ ).Run(func(_ context.Context, _ string, _ []entity.PermissionType, _ entity.PermissionMetadata, cb func(port.PermissionDialogResult)) {
561 cb(port.PermissionDialogResult{Allowed: true, Persistent: true})567 cb(port.PermissionDialogResult{Allowed: true, Persistent: true})
562 })568 })
563 569
564 // Should persist the granted permission570 // Should persist the granted permission
565 permRepo.EXPECT().Set(mock.Anything, mock.AnythingOfType("*entity.PermissionRecord")).571 permRepo.EXPECT().Set(mock.Anything, mock.AnythingOfType("*entity.PermissionRecord")).
566 Run(func(_ context.Context, r *entity.PermissionRecord) {572 Run(func(_ context.Context, r *entity.PermissionRecord) {
567- assert.Equal(t, "https://accounts.google.com", r.Origin)573+ assert.Equal(t, "https://shop.example.com", r.Origin)
568 assert.Equal(t, entity.PermissionTypeWebsiteDataAccess, r.Type)574 assert.Equal(t, entity.PermissionTypeWebsiteDataAccess, r.Type)
569 assert.Equal(t, entity.PermissionGranted, r.Decision)575 assert.Equal(t, entity.PermissionGranted, r.Decision)
570 }).Return(nil)576 }).Return(nil)
571 577
572- uc.HandlePermissionRequest(ctx, "https://accounts.google.com", []entity.PermissionType{578+ uc.HandlePermissionRequest(ctx, "https://shop.example.com", []entity.PermissionType{
573 entity.PermissionTypeWebsiteDataAccess,579 entity.PermissionTypeWebsiteDataAccess,
574- }, callback)580+ }, meta, callback)
575 581
576 assert.True(t, allowed)582 assert.True(t, allowed)
577 }583 }
@@ -584,9 +590,9 @@ func TestHandlePermissionUseCase_WebsiteDataAccess_UsesStoredGrant(t *testing.T)
584 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)590 uc := usecase.NewHandlePermissionUseCase(permRepo, dialog, permissionLoggerFromContext)
585 591
586 // Stored granted permission592 // Stored granted permission
587- permRepo.EXPECT().Get(mock.Anything, "https://accounts.google.com", entity.PermissionTypeWebsiteDataAccess).593+ permRepo.EXPECT().Get(mock.Anything, "https://shop.example.com", entity.PermissionTypeWebsiteDataAccess).
588 Return(&entity.PermissionRecord{594 Return(&entity.PermissionRecord{
589- Origin: "https://accounts.google.com",595+ Origin: "https://shop.example.com",
590 Type: entity.PermissionTypeWebsiteDataAccess,596 Type: entity.PermissionTypeWebsiteDataAccess,
591 Decision: entity.PermissionGranted,597 Decision: entity.PermissionGranted,
592 }, nil)598 }, nil)
@@ -597,9 +603,9 @@ func TestHandlePermissionUseCase_WebsiteDataAccess_UsesStoredGrant(t *testing.T)
597 Deny: func() {},603 Deny: func() {},
598 }604 }
599 605
600- uc.HandlePermissionRequest(ctx, "https://accounts.google.com", []entity.PermissionType{606+ uc.HandlePermissionRequest(ctx, "https://shop.example.com", []entity.PermissionType{
601 entity.PermissionTypeWebsiteDataAccess,607 entity.PermissionTypeWebsiteDataAccess,
602- }, callback)608+ }, entity.PermissionMetadata{entity.PermissionMetadataKeyRequestingDomain: "accounts.google.com", entity.PermissionMetadataKeyCurrentDomain: "shop.example.com"}, callback)
603 609
604 assert.True(t, allowed, "should use stored granted permission without dialog")610 assert.True(t, allowed, "should use stored granted permission without dialog")
605 dialog.AssertNotCalled(t, "ShowPermissionDialog")611 dialog.AssertNotCalled(t, "ShowPermissionDialog")
M internal/domain/entity/permission.go +17 -0
@@ -38,6 +38,23 @@ const (
3838 PermissionTypeWebsiteDataAccess PermissionType = "website_data_access"
3939 )
4040
41+// Metadata keys for PermissionMetadata.
42+const (
43+ // PermissionMetadataKeyRequestingDomain is the origin that is requesting cross-site
44+ // data access (i.e. the third-party domain asking for its cookies/storage).
45+ PermissionMetadataKeyRequestingDomain = "requesting_domain"
46+
47+ // PermissionMetadataKeyCurrentDomain is the first-party domain currently loaded in
48+ // the WebView — the context in which the cross-site request is happening.
49+ PermissionMetadataKeyCurrentDomain = "current_domain"
50+)
51+
52+// PermissionMetadata carries extra context for specific permission types.
53+// For website_data_access, callers must populate both keys:
54+// - PermissionMetadataKeyRequestingDomain: the origin requesting cross-site data access
55+// - PermissionMetadataKeyCurrentDomain: the domain currently loaded in the WebView
56+type PermissionMetadata map[string]string
57+
4158 // PermissionDecision represents the user's decision for a permission.
4259 type PermissionDecision string
4360
@@ -38,6 +38,23 @@ const (
38 PermissionTypeWebsiteDataAccess PermissionType = "website_data_access"38 PermissionTypeWebsiteDataAccess PermissionType = "website_data_access"
39 )39 )
40 40
41+// Metadata keys for PermissionMetadata.
42+const (
43+ // PermissionMetadataKeyRequestingDomain is the origin that is requesting cross-site
44+ // data access (i.e. the third-party domain asking for its cookies/storage).
45+ PermissionMetadataKeyRequestingDomain = "requesting_domain"
46+
47+ // PermissionMetadataKeyCurrentDomain is the first-party domain currently loaded in
48+ // the WebView — the context in which the cross-site request is happening.
49+ PermissionMetadataKeyCurrentDomain = "current_domain"
50+)
51+
52+// PermissionMetadata carries extra context for specific permission types.
53+// For website_data_access, callers must populate both keys:
54+// - PermissionMetadataKeyRequestingDomain: the origin requesting cross-site data access
55+// - PermissionMetadataKeyCurrentDomain: the domain currently loaded in the WebView
56+type PermissionMetadata map[string]string
57+
41 // PermissionDecision represents the user's decision for a permission.58 // PermissionDecision represents the user's decision for a permission.
42 type PermissionDecision string59 type PermissionDecision string
43 60
M internal/infrastructure/colorscheme/detector_adwaita.go +1 -1
@@ -3,7 +3,7 @@ package colorscheme
33 import (
44 "sync/atomic"
55
6- "github.com/jwijenbergh/puregotk/v4/adw"
6+ "github.com/bnema/puregotk/v4/adw"
77 )
88
99 const (
@@ -3,7 +3,7 @@ package colorscheme
3 import (3 import (
4 "sync/atomic"4 "sync/atomic"
5 5
6- "github.com/jwijenbergh/puregotk/v4/adw"6+ "github.com/bnema/puregotk/v4/adw"
7 )7 )
8 8
9 const (9 const (
M internal/infrastructure/config/defaults.go +4 -1
@@ -147,7 +147,10 @@ func DefaultConfig() *Config {
147147 Profile: ProfileDefault,
148148 PoolPrewarmCount: defaultWebViewPoolPrewarmCount,
149149 ZoomCacheSize: defaultZoomCacheSize,
150- CookiePolicy: CookiePolicyNoThirdParty,
150+ // With ITP enabled, WebKit ignores ACCEPT_NO_THIRD_PARTY — ITP handles
151+ // third-party cookie isolation more intelligently. Using Always + ITP
152+ // matches Epiphany's model and avoids a misleading setting.
153+ CookiePolicy: CookiePolicyAlways,
151154 WebKit: WebKitEngineConfig{
152155 ITPEnabled: true,
153156 SkiaCPUPaintingThreads: defaultSkiaCPUPaintingThreads,
@@ -147,7 +147,10 @@ func DefaultConfig() *Config {
147 Profile: ProfileDefault,147 Profile: ProfileDefault,
148 PoolPrewarmCount: defaultWebViewPoolPrewarmCount,148 PoolPrewarmCount: defaultWebViewPoolPrewarmCount,
149 ZoomCacheSize: defaultZoomCacheSize,149 ZoomCacheSize: defaultZoomCacheSize,
150- CookiePolicy: CookiePolicyNoThirdParty,150+ // With ITP enabled, WebKit ignores ACCEPT_NO_THIRD_PARTY — ITP handles
151+ // third-party cookie isolation more intelligently. Using Always + ITP
152+ // matches Epiphany's model and avoids a misleading setting.
153+ CookiePolicy: CookiePolicyAlways,
151 WebKit: WebKitEngineConfig{154 WebKit: WebKitEngineConfig{
152 ITPEnabled: true,155 ITPEnabled: true,
153 SkiaCPUPaintingThreads: defaultSkiaCPUPaintingThreads,156 SkiaCPUPaintingThreads: defaultSkiaCPUPaintingThreads,
M internal/infrastructure/config/defaults_test.go +1 -1
@@ -15,7 +15,7 @@ func TestDefaultConfig_CoreDefaults(t *testing.T) {
1515 // Engine defaults (replaces old Performance/Privacy sections)
1616 assert.Equal(t, "webkit", cfg.Engine.Type)
1717 assert.Equal(t, ProfileDefault, cfg.Engine.Profile)
18- assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)
18+ assert.Equal(t, CookiePolicyAlways, cfg.Engine.CookiePolicy)
1919 assert.True(t, cfg.Engine.WebKit.ITPEnabled)
2020
2121 // Old sections (Rendering, Privacy, Performance, Runtime) have been removed from Config.
@@ -15,7 +15,7 @@ func TestDefaultConfig_CoreDefaults(t *testing.T) {
15 // Engine defaults (replaces old Performance/Privacy sections)15 // Engine defaults (replaces old Performance/Privacy sections)
16 assert.Equal(t, "webkit", cfg.Engine.Type)16 assert.Equal(t, "webkit", cfg.Engine.Type)
17 assert.Equal(t, ProfileDefault, cfg.Engine.Profile)17 assert.Equal(t, ProfileDefault, cfg.Engine.Profile)
18- assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)18+ assert.Equal(t, CookiePolicyAlways, cfg.Engine.CookiePolicy)
19 assert.True(t, cfg.Engine.WebKit.ITPEnabled)19 assert.True(t, cfg.Engine.WebKit.ITPEnabled)
20 20
21 // Old sections (Rendering, Privacy, Performance, Runtime) have been removed from Config.21 // Old sections (Rendering, Privacy, Performance, Runtime) have been removed from Config.
M internal/infrastructure/config/loader.go +15 -4
@@ -224,16 +224,27 @@ func normalizeEngineConfig(config *Config) {
224224 config.Engine.WebKit.GSKRenderer = GSKRendererAuto
225225 }
226226
227- // Normalize cookie policy (engine)
227+ // Normalize cookie policy (engine).
228+ // When no value (or an invalid value) is set, respect the ITP setting: if ITP is
229+ // disabled the effective default is to block third-party cookies; if ITP is enabled
230+ // all cookies are accepted by default. An explicit "always" value always wins.
231+ itpDefault := func() CookiePolicy {
232+ if config.Engine.WebKit.ITPEnabled {
233+ return CookiePolicyAlways
234+ }
235+ return CookiePolicyNoThirdParty
236+ }
228237 switch strings.ToLower(string(config.Engine.CookiePolicy)) {
229- case "", string(CookiePolicyNoThirdParty):
230- config.Engine.CookiePolicy = CookiePolicyNoThirdParty
238+ case "":
239+ config.Engine.CookiePolicy = itpDefault()
231240 case string(CookiePolicyAlways):
232241 config.Engine.CookiePolicy = CookiePolicyAlways
242+ case string(CookiePolicyNoThirdParty):
243+ config.Engine.CookiePolicy = CookiePolicyNoThirdParty
233244 case string(CookiePolicyNever):
234245 config.Engine.CookiePolicy = CookiePolicyNever
235246 default:
236- config.Engine.CookiePolicy = CookiePolicyNoThirdParty
247+ config.Engine.CookiePolicy = itpDefault()
237248 }
238249
239250 // Normalize performance profile (engine)
@@ -224,16 +224,27 @@ func normalizeEngineConfig(config *Config) {
224 config.Engine.WebKit.GSKRenderer = GSKRendererAuto224 config.Engine.WebKit.GSKRenderer = GSKRendererAuto
225 }225 }
226 226
227- // Normalize cookie policy (engine)227+ // Normalize cookie policy (engine).
228+ // When no value (or an invalid value) is set, respect the ITP setting: if ITP is
229+ // disabled the effective default is to block third-party cookies; if ITP is enabled
230+ // all cookies are accepted by default. An explicit "always" value always wins.
231+ itpDefault := func() CookiePolicy {
232+ if config.Engine.WebKit.ITPEnabled {
233+ return CookiePolicyAlways
234+ }
235+ return CookiePolicyNoThirdParty
236+ }
228 switch strings.ToLower(string(config.Engine.CookiePolicy)) {237 switch strings.ToLower(string(config.Engine.CookiePolicy)) {
229- case "", string(CookiePolicyNoThirdParty):238+ case "":
230- config.Engine.CookiePolicy = CookiePolicyNoThirdParty239+ config.Engine.CookiePolicy = itpDefault()
231 case string(CookiePolicyAlways):240 case string(CookiePolicyAlways):
232 config.Engine.CookiePolicy = CookiePolicyAlways241 config.Engine.CookiePolicy = CookiePolicyAlways
242+ case string(CookiePolicyNoThirdParty):
243+ config.Engine.CookiePolicy = CookiePolicyNoThirdParty
233 case string(CookiePolicyNever):244 case string(CookiePolicyNever):
234 config.Engine.CookiePolicy = CookiePolicyNever245 config.Engine.CookiePolicy = CookiePolicyNever
235 default:246 default:
236- config.Engine.CookiePolicy = CookiePolicyNoThirdParty247+ config.Engine.CookiePolicy = itpDefault()
237 }248 }
238 249
239 // Normalize performance profile (engine)250 // Normalize performance profile (engine)
M internal/infrastructure/config/loader_test.go +2 -2
@@ -14,7 +14,7 @@ func TestSetEngineDefaults(t *testing.T) {
1414 // Universal engine fields
1515 assert.Equal(t, "webkit", mgr.viper.GetString("engine.type"))
1616 assert.Equal(t, "default", mgr.viper.GetString("engine.profile"))
17- assert.Equal(t, "no_third_party", mgr.viper.GetString("engine.cookie_policy"))
17+ assert.Equal(t, "always", mgr.viper.GetString("engine.cookie_policy"))
1818 assert.Equal(t, 4, mgr.viper.GetInt("engine.pool_prewarm_count"))
1919 assert.Equal(t, 256, mgr.viper.GetInt("engine.zoom_cache_size"))
2020
@@ -37,5 +37,5 @@ func TestNormalizeConfig_EngineCookiePolicy(t *testing.T) {
3737
3838 normalizeConfig(cfg)
3939
40- assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)
40+ assert.Equal(t, CookiePolicyAlways, cfg.Engine.CookiePolicy)
4141 }
@@ -14,7 +14,7 @@ func TestSetEngineDefaults(t *testing.T) {
14 // Universal engine fields14 // Universal engine fields
15 assert.Equal(t, "webkit", mgr.viper.GetString("engine.type"))15 assert.Equal(t, "webkit", mgr.viper.GetString("engine.type"))
16 assert.Equal(t, "default", mgr.viper.GetString("engine.profile"))16 assert.Equal(t, "default", mgr.viper.GetString("engine.profile"))
17- assert.Equal(t, "no_third_party", mgr.viper.GetString("engine.cookie_policy"))17+ assert.Equal(t, "always", mgr.viper.GetString("engine.cookie_policy"))
18 assert.Equal(t, 4, mgr.viper.GetInt("engine.pool_prewarm_count"))18 assert.Equal(t, 4, mgr.viper.GetInt("engine.pool_prewarm_count"))
19 assert.Equal(t, 256, mgr.viper.GetInt("engine.zoom_cache_size"))19 assert.Equal(t, 256, mgr.viper.GetInt("engine.zoom_cache_size"))
20 20
@@ -37,5 +37,5 @@ func TestNormalizeConfig_EngineCookiePolicy(t *testing.T) {
37 37
38 normalizeConfig(cfg)38 normalizeConfig(cfg)
39 39
40- assert.Equal(t, CookiePolicyNoThirdParty, cfg.Engine.CookiePolicy)40+ assert.Equal(t, CookiePolicyAlways, cfg.Engine.CookiePolicy)
41 }41 }
M internal/infrastructure/filtering/store.go +1 -1
@@ -7,7 +7,7 @@ import (
77
88 "github.com/bnema/dumber/internal/logging"
99 "github.com/bnema/puregotk-webkit/webkit"
10- "github.com/jwijenbergh/puregotk/v4/gio"
10+ "github.com/bnema/puregotk/v4/gio"
1111 )
1212
1313 // Store wraps webkit.UserContentFilterStore with Go-friendly async operations.
@@ -7,7 +7,7 @@ import (
7 7
8 "github.com/bnema/dumber/internal/logging"8 "github.com/bnema/dumber/internal/logging"
9 "github.com/bnema/puregotk-webkit/webkit"9 "github.com/bnema/puregotk-webkit/webkit"
10- "github.com/jwijenbergh/puregotk/v4/gio"10+ "github.com/bnema/puregotk/v4/gio"
11 )11 )
12 12
13 // Store wraps webkit.UserContentFilterStore with Go-friendly async operations.13 // Store wraps webkit.UserContentFilterStore with Go-friendly async operations.
M internal/infrastructure/textinput/gtk_entry_target.go +1 -1
@@ -5,7 +5,7 @@ import (
55
66 "github.com/bnema/dumber/internal/application/port"
77 "github.com/bnema/dumber/internal/logging"
8- "github.com/jwijenbergh/puregotk/v4/gtk"
8+ "github.com/bnema/puregotk/v4/gtk"
99 )
1010
1111 // GTKEntryTarget implements TextInputTarget for GTK SearchEntry widgets.
@@ -5,7 +5,7 @@ import (
5 5
6 "github.com/bnema/dumber/internal/application/port"6 "github.com/bnema/dumber/internal/application/port"
7 "github.com/bnema/dumber/internal/logging"7 "github.com/bnema/dumber/internal/logging"
8- "github.com/jwijenbergh/puregotk/v4/gtk"8+ "github.com/bnema/puregotk/v4/gtk"
9 )9 )
10 10
11 // GTKEntryTarget implements TextInputTarget for GTK SearchEntry widgets.11 // GTKEntryTarget implements TextInputTarget for GTK SearchEntry widgets.
M internal/infrastructure/webkit/bgcolor.go +1 -1
@@ -3,7 +3,7 @@ package webkit
33 import (
44 "sync"
55
6- "github.com/jwijenbergh/puregotk/v4/gdk"
6+ "github.com/bnema/puregotk/v4/gdk"
77 )
88
99 // bgColor holds RGBA components for WebView background color (eliminates white flash).
@@ -3,7 +3,7 @@ package webkit
3 import (3 import (
4 "sync"4 "sync"
5 5
6- "github.com/jwijenbergh/puregotk/v4/gdk"6+ "github.com/bnema/puregotk/v4/gdk"
7 )7 )
8 8
9 // bgColor holds RGBA components for WebView background color (eliminates white flash).9 // bgColor holds RGBA components for WebView background color (eliminates white flash).
M internal/infrastructure/webkit/engine_adapters.go +1 -1
@@ -7,7 +7,7 @@ import (
77 "github.com/bnema/dumber/internal/application/port"
88 "github.com/bnema/dumber/internal/infrastructure/filtering"
99 "github.com/bnema/dumber/internal/logging"
10- "github.com/jwijenbergh/puregotk/v4/gio"
10+ "github.com/bnema/puregotk/v4/gio"
1111 )
1212
1313 // --- WebViewFactory adapter ---
@@ -7,7 +7,7 @@ import (
7 "github.com/bnema/dumber/internal/application/port"7 "github.com/bnema/dumber/internal/application/port"
8 "github.com/bnema/dumber/internal/infrastructure/filtering"8 "github.com/bnema/dumber/internal/infrastructure/filtering"
9 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
10- "github.com/jwijenbergh/puregotk/v4/gio"10+ "github.com/bnema/puregotk/v4/gio"
11 )11 )
12 12
13 // --- WebViewFactory adapter ---13 // --- WebViewFactory adapter ---
M internal/infrastructure/webkit/engine_init.go +1 -1
@@ -12,7 +12,7 @@ import (
1212 "github.com/bnema/dumber/internal/infrastructure/filtering"
1313 "github.com/bnema/dumber/internal/logging"
1414 "github.com/bnema/dumber/internal/ui/theme"
15- "github.com/jwijenbergh/puregotk/v4/gdk"
15+ "github.com/bnema/puregotk/v4/gdk"
1616 "github.com/rs/zerolog"
1717 )
1818
@@ -12,7 +12,7 @@ import (
12 "github.com/bnema/dumber/internal/infrastructure/filtering"12 "github.com/bnema/dumber/internal/infrastructure/filtering"
13 "github.com/bnema/dumber/internal/logging"13 "github.com/bnema/dumber/internal/logging"
14 "github.com/bnema/dumber/internal/ui/theme"14 "github.com/bnema/dumber/internal/ui/theme"
15- "github.com/jwijenbergh/puregotk/v4/gdk"15+ "github.com/bnema/puregotk/v4/gdk"
16 "github.com/rs/zerolog"16 "github.com/rs/zerolog"
17 )17 )
18 18
M internal/infrastructure/webkit/gobject_type_helpers.go +3 -3
@@ -5,9 +5,9 @@ import (
55 "sync"
66
77 "github.com/bnema/dumber/internal/logging"
8- "github.com/jwijenbergh/purego"
9- "github.com/jwijenbergh/puregotk/pkg/core"
10- gtypes "github.com/jwijenbergh/puregotk/v4/gobject/types"
8+ "github.com/bnema/puregotk/pkg/core"
9+ gtypes "github.com/bnema/puregotk/v4/gobject/types"
10+ "github.com/ebitengine/purego"
1111 )
1212
1313 var (
@@ -5,9 +5,9 @@ import (
5 "sync"5 "sync"
6 6
7 "github.com/bnema/dumber/internal/logging"7 "github.com/bnema/dumber/internal/logging"
8- "github.com/jwijenbergh/purego"8+ "github.com/bnema/puregotk/pkg/core"
9- "github.com/jwijenbergh/puregotk/pkg/core"9+ gtypes "github.com/bnema/puregotk/v4/gobject/types"
10- gtypes "github.com/jwijenbergh/puregotk/v4/gobject/types"10+ "github.com/ebitengine/purego"
11 )11 )
12 12
13 var (13 var (
M internal/infrastructure/webkit/pool.go +1 -1
@@ -11,7 +11,7 @@ import (
1111
1212 "github.com/bnema/dumber/internal/logging"
1313 "github.com/bnema/puregotk-webkit/webkit"
14- "github.com/jwijenbergh/puregotk/v4/glib"
14+ "github.com/bnema/puregotk/v4/glib"
1515 )
1616
1717 // ErrPoolClosed is returned when operations are attempted on a closed pool.
@@ -11,7 +11,7 @@ import (
11 11
12 "github.com/bnema/dumber/internal/logging"12 "github.com/bnema/dumber/internal/logging"
13 "github.com/bnema/puregotk-webkit/webkit"13 "github.com/bnema/puregotk-webkit/webkit"
14- "github.com/jwijenbergh/puregotk/v4/glib"14+ "github.com/bnema/puregotk/v4/glib"
15 )15 )
16 16
17 // ErrPoolClosed is returned when operations are attempted on a closed pool.17 // ErrPoolClosed is returned when operations are attempted on a closed pool.
M internal/infrastructure/webkit/scheme_handler.go +1 -1
@@ -20,7 +20,7 @@ import (
2020 "github.com/bnema/dumber/internal/logging"
2121 "github.com/bnema/puregotk-webkit/soup"
2222 "github.com/bnema/puregotk-webkit/webkit"
23- "github.com/jwijenbergh/puregotk/v4/gio"
23+ "github.com/bnema/puregotk/v4/gio"
2424 "github.com/rs/zerolog"
2525 )
2626
@@ -20,7 +20,7 @@ import (
20 "github.com/bnema/dumber/internal/logging"20 "github.com/bnema/dumber/internal/logging"
21 "github.com/bnema/puregotk-webkit/soup"21 "github.com/bnema/puregotk-webkit/soup"
22 "github.com/bnema/puregotk-webkit/webkit"22 "github.com/bnema/puregotk-webkit/webkit"
23- "github.com/jwijenbergh/puregotk/v4/gio"23+ "github.com/bnema/puregotk/v4/gio"
24 "github.com/rs/zerolog"24 "github.com/rs/zerolog"
25 )25 )
26 26
M internal/infrastructure/webkit/webview.go +27 -34
@@ -11,16 +11,17 @@ import (
1111 "time"
1212
1313 "github.com/bnema/dumber/internal/application/port"
14+ "github.com/bnema/dumber/internal/domain/entity"
1415 urlutil "github.com/bnema/dumber/internal/domain/url"
1516 "github.com/bnema/dumber/internal/infrastructure/desktop"
1617 "github.com/bnema/dumber/internal/logging"
1718 "github.com/bnema/puregotk-webkit/webkit"
18- "github.com/jwijenbergh/puregotk/v4/gdk"
19- "github.com/jwijenbergh/puregotk/v4/gio"
20- "github.com/jwijenbergh/puregotk/v4/glib"
21- "github.com/jwijenbergh/puregotk/v4/gobject"
22- gtypes "github.com/jwijenbergh/puregotk/v4/gobject/types"
23- "github.com/jwijenbergh/puregotk/v4/gtk"
19+ "github.com/bnema/puregotk/v4/gdk"
20+ "github.com/bnema/puregotk/v4/gio"
21+ "github.com/bnema/puregotk/v4/glib"
22+ "github.com/bnema/puregotk/v4/gobject"
23+ gtypes "github.com/bnema/puregotk/v4/gobject/types"
24+ "github.com/bnema/puregotk/v4/gtk"
2425 "github.com/rs/zerolog"
2526 )
2627
@@ -162,7 +163,9 @@ type WebView struct {
162163 // PermissionRequest is called when a site requests permission (mic, camera, screen sharing).
163164 // Return true to indicate the request was handled. Call allow()/deny() to respond.
164165 // The permission types are determined from the request object.
165- OnPermissionRequest func(origin string, permTypes []string, allow, deny func()) bool
166+ // The metadata map carries permission-type-specific context; for website_data_access both
167+ // entity.PermissionMetadataKeyRequestingDomain and entity.PermissionMetadataKeyCurrentDomain are populated.
168+ OnPermissionRequest func(origin string, permTypes []string, metadata map[string]string, allow, deny func()) bool
166169
167170 logger zerolog.Logger
168171 mu sync.RWMutex
@@ -978,15 +981,17 @@ func (wv *WebView) connectWebProcessTerminatedSignal() {
978981 // connectPermissionRequestSignal sets up the permission-request signal handler.
979982 // This is emitted when a site calls getUserMedia() or getDisplayMedia().
980983 func (wv *WebView) connectPermissionRequestSignal() {
981- permissionCb := func(_ webkit.WebView, requestPtr uintptr) bool {
984+ permissionCb := func(inner webkit.WebView, requestPtr uintptr) bool {
982985 ctx := logging.WithContext(context.Background(), wv.logger)
983986
984987 if wv.OnPermissionRequest == nil {
985988 return false // Not handled, WebKit will deny by default
986989 }
987990
988- // Extract and normalize origin from current URI
989- uri := wv.URI()
991+ // Use the live URI from the signal's WebView rather than the cached wv.URI(),
992+ // so permission lookups use the current page origin even if navigation has
993+ // occurred since the last load-changed signal.
994+ uri := inner.GetUri()
990995 if uri == "" {
991996 wv.logger.Debug().Msg("permission request with empty origin, denying")
992997 return false
@@ -998,14 +1003,11 @@ func (wv *WebView) connectPermissionRequestSignal() {
9981003 }
9991004
10001005 // Determine permission types from the request
1001- permTypes, originOverride := wv.determinePermissionTypes(ctx, requestPtr)
1006+ permTypes, metadata := wv.determinePermissionTypes(ctx, requestPtr)
10021007 if len(permTypes) == 0 {
10031008 wv.logger.Warn().Msg("permission request with unknown type, denying")
10041009 return false
10051010 }
1006- if originOverride != "" {
1007- origin = originOverride
1008- }
10091011
10101012 // Ref the request object to prevent use-after-free
10111013 // The request may outlive the signal handler if we show a dialog
@@ -1039,7 +1041,7 @@ func (wv *WebView) connectPermissionRequestSignal() {
10391041 }
10401042
10411043 // Call the handler
1042- return wv.OnPermissionRequest(origin, permTypes, allow, deny)
1044+ return wv.OnPermissionRequest(origin, permTypes, metadata, allow, deny)
10431045 }
10441046
10451047 sigID := wv.inner.ConnectPermissionRequest(&permissionCb)
@@ -1051,13 +1053,13 @@ func (wv *WebView) connectPermissionRequestSignal() {
10511053 // We use GObject property accessors for audio/video request flags. Display detection
10521054 // uses the dedicated WebKit API because this WebKit build does not expose an
10531055 // "is-for-display-device" GObject property.
1054-func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uintptr) (types []string, originOverride string) {
1056+func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uintptr) (types []string, metadata map[string]string) {
10551057 requestKind := detectPermissionRequestKind(ctx, requestPtr)
10561058 switch requestKind {
10571059 case permissionRequestKindUserMedia:
10581060 userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr)
10591061 if userMediaReq == nil {
1060- return nil, ""
1062+ return nil, nil
10611063 }
10621064
10631065 // Use GObject property accessors — more reliable than the C function wrappers
@@ -1072,13 +1074,13 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint
10721074 Bool("is_display", isDisplay).
10731075 Msg("permission request type detection")
10741076
1075- return classifyPermissionRequestTypes(ctx, requestKind, isAudio, isVideo, isDisplay), ""
1077+ return classifyPermissionRequestTypes(ctx, requestKind, isAudio, isVideo, isDisplay), nil
10761078 case permissionRequestKindDeviceInfo:
1077- return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), ""
1079+ return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), nil
10781080 case permissionRequestKindWebsiteDataAccess:
10791081 wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr)
10801082 if wdaReq == nil {
1081- return nil, ""
1083+ return nil, nil
10821084 }
10831085 currentDomain := wdaReq.GetCurrentDomain()
10841086 requestingDomain := wdaReq.GetRequestingDomain()
@@ -1086,20 +1088,11 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint
10861088 Str("current_domain", currentDomain).
10871089 Str("requesting_domain", requestingDomain).
10881090 Msg("website data access permission request")
1089- override := ""
1090- if requestingDomain != "" {
1091- candidate := "https://" + requestingDomain
1092- if normalized, err := urlutil.ExtractOrigin(candidate); err == nil {
1093- override = normalized
1094- } else {
1095- wv.logger.Warn().
1096- Str("requesting_domain", requestingDomain).
1097- Err(err).
1098- Msg("website data access: failed to normalize requesting domain, using raw")
1099- override = candidate
1100- }
1091+ meta := map[string]string{
1092+ entity.PermissionMetadataKeyRequestingDomain: requestingDomain,
1093+ entity.PermissionMetadataKeyCurrentDomain: currentDomain,
11011094 }
1102- return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), override
1095+ return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), meta
11031096 default:
11041097 if requestPtr != 0 {
11051098 typeName := permissionRequestTypeName(ctx, requestPtr)
@@ -1111,7 +1104,7 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint
11111104 }
11121105 // Unknown permission type - could be clipboard, notifications, geolocation, etc.
11131106 // For now, return empty to trigger denial. Future phases will add these types.
1114- return nil, ""
1107+ return nil, nil
11151108 }
11161109 }
11171110
@@ -11,16 +11,17 @@ import (
11 "time"11 "time"
12 12
13 "github.com/bnema/dumber/internal/application/port"13 "github.com/bnema/dumber/internal/application/port"
14+ "github.com/bnema/dumber/internal/domain/entity"
14 urlutil "github.com/bnema/dumber/internal/domain/url"15 urlutil "github.com/bnema/dumber/internal/domain/url"
15 "github.com/bnema/dumber/internal/infrastructure/desktop"16 "github.com/bnema/dumber/internal/infrastructure/desktop"
16 "github.com/bnema/dumber/internal/logging"17 "github.com/bnema/dumber/internal/logging"
17 "github.com/bnema/puregotk-webkit/webkit"18 "github.com/bnema/puregotk-webkit/webkit"
18- "github.com/jwijenbergh/puregotk/v4/gdk"19+ "github.com/bnema/puregotk/v4/gdk"
19- "github.com/jwijenbergh/puregotk/v4/gio"20+ "github.com/bnema/puregotk/v4/gio"
20- "github.com/jwijenbergh/puregotk/v4/glib"21+ "github.com/bnema/puregotk/v4/glib"
21- "github.com/jwijenbergh/puregotk/v4/gobject"22+ "github.com/bnema/puregotk/v4/gobject"
22- gtypes "github.com/jwijenbergh/puregotk/v4/gobject/types"23+ gtypes "github.com/bnema/puregotk/v4/gobject/types"
23- "github.com/jwijenbergh/puregotk/v4/gtk"24+ "github.com/bnema/puregotk/v4/gtk"
24 "github.com/rs/zerolog"25 "github.com/rs/zerolog"
25 )26 )
26 27
@@ -162,7 +163,9 @@ type WebView struct {
162 // PermissionRequest is called when a site requests permission (mic, camera, screen sharing).163 // PermissionRequest is called when a site requests permission (mic, camera, screen sharing).
163 // Return true to indicate the request was handled. Call allow()/deny() to respond.164 // Return true to indicate the request was handled. Call allow()/deny() to respond.
164 // The permission types are determined from the request object.165 // The permission types are determined from the request object.
165- OnPermissionRequest func(origin string, permTypes []string, allow, deny func()) bool166+ // The metadata map carries permission-type-specific context; for website_data_access both
167+ // entity.PermissionMetadataKeyRequestingDomain and entity.PermissionMetadataKeyCurrentDomain are populated.
168+ OnPermissionRequest func(origin string, permTypes []string, metadata map[string]string, allow, deny func()) bool
166 169
167 logger zerolog.Logger170 logger zerolog.Logger
168 mu sync.RWMutex171 mu sync.RWMutex
@@ -978,15 +981,17 @@ func (wv *WebView) connectWebProcessTerminatedSignal() {
978 // connectPermissionRequestSignal sets up the permission-request signal handler.981 // connectPermissionRequestSignal sets up the permission-request signal handler.
979 // This is emitted when a site calls getUserMedia() or getDisplayMedia().982 // This is emitted when a site calls getUserMedia() or getDisplayMedia().
980 func (wv *WebView) connectPermissionRequestSignal() {983 func (wv *WebView) connectPermissionRequestSignal() {
981- permissionCb := func(_ webkit.WebView, requestPtr uintptr) bool {984+ permissionCb := func(inner webkit.WebView, requestPtr uintptr) bool {
982 ctx := logging.WithContext(context.Background(), wv.logger)985 ctx := logging.WithContext(context.Background(), wv.logger)
983 986
984 if wv.OnPermissionRequest == nil {987 if wv.OnPermissionRequest == nil {
985 return false // Not handled, WebKit will deny by default988 return false // Not handled, WebKit will deny by default
986 }989 }
987 990
988- // Extract and normalize origin from current URI991+ // Use the live URI from the signal's WebView rather than the cached wv.URI(),
989- uri := wv.URI()992+ // so permission lookups use the current page origin even if navigation has
993+ // occurred since the last load-changed signal.
994+ uri := inner.GetUri()
990 if uri == "" {995 if uri == "" {
991 wv.logger.Debug().Msg("permission request with empty origin, denying")996 wv.logger.Debug().Msg("permission request with empty origin, denying")
992 return false997 return false
@@ -998,14 +1003,11 @@ func (wv *WebView) connectPermissionRequestSignal() {
998 }1003 }
999 1004
1000 // Determine permission types from the request1005 // Determine permission types from the request
1001- permTypes, originOverride := wv.determinePermissionTypes(ctx, requestPtr)1006+ permTypes, metadata := wv.determinePermissionTypes(ctx, requestPtr)
1002 if len(permTypes) == 0 {1007 if len(permTypes) == 0 {
1003 wv.logger.Warn().Msg("permission request with unknown type, denying")1008 wv.logger.Warn().Msg("permission request with unknown type, denying")
1004 return false1009 return false
1005 }1010 }
1006- if originOverride != "" {
1007- origin = originOverride
1008- }
1009 1011
1010 // Ref the request object to prevent use-after-free1012 // Ref the request object to prevent use-after-free
1011 // The request may outlive the signal handler if we show a dialog1013 // The request may outlive the signal handler if we show a dialog
@@ -1039,7 +1041,7 @@ func (wv *WebView) connectPermissionRequestSignal() {
1039 }1041 }
1040 1042
1041 // Call the handler1043 // Call the handler
1042- return wv.OnPermissionRequest(origin, permTypes, allow, deny)1044+ return wv.OnPermissionRequest(origin, permTypes, metadata, allow, deny)
1043 }1045 }
1044 1046
1045 sigID := wv.inner.ConnectPermissionRequest(&permissionCb)1047 sigID := wv.inner.ConnectPermissionRequest(&permissionCb)
@@ -1051,13 +1053,13 @@ func (wv *WebView) connectPermissionRequestSignal() {
1051 // We use GObject property accessors for audio/video request flags. Display detection1053 // We use GObject property accessors for audio/video request flags. Display detection
1052 // uses the dedicated WebKit API because this WebKit build does not expose an1054 // uses the dedicated WebKit API because this WebKit build does not expose an
1053 // "is-for-display-device" GObject property.1055 // "is-for-display-device" GObject property.
1054-func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uintptr) (types []string, originOverride string) {1056+func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uintptr) (types []string, metadata map[string]string) {
1055 requestKind := detectPermissionRequestKind(ctx, requestPtr)1057 requestKind := detectPermissionRequestKind(ctx, requestPtr)
1056 switch requestKind {1058 switch requestKind {
1057 case permissionRequestKindUserMedia:1059 case permissionRequestKindUserMedia:
1058 userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr)1060 userMediaReq := webkit.UserMediaPermissionRequestNewFromInternalPtr(requestPtr)
1059 if userMediaReq == nil {1061 if userMediaReq == nil {
1060- return nil, ""1062+ return nil, nil
1061 }1063 }
1062 1064
1063 // Use GObject property accessors — more reliable than the C function wrappers1065 // Use GObject property accessors — more reliable than the C function wrappers
@@ -1072,13 +1074,13 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint
1072 Bool("is_display", isDisplay).1074 Bool("is_display", isDisplay).
1073 Msg("permission request type detection")1075 Msg("permission request type detection")
1074 1076
1075- return classifyPermissionRequestTypes(ctx, requestKind, isAudio, isVideo, isDisplay), ""1077+ return classifyPermissionRequestTypes(ctx, requestKind, isAudio, isVideo, isDisplay), nil
1076 case permissionRequestKindDeviceInfo:1078 case permissionRequestKindDeviceInfo:
1077- return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), ""1079+ return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), nil
1078 case permissionRequestKindWebsiteDataAccess:1080 case permissionRequestKindWebsiteDataAccess:
1079 wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr)1081 wdaReq := webkit.WebsiteDataAccessPermissionRequestNewFromInternalPtr(requestPtr)
1080 if wdaReq == nil {1082 if wdaReq == nil {
1081- return nil, ""1083+ return nil, nil
1082 }1084 }
1083 currentDomain := wdaReq.GetCurrentDomain()1085 currentDomain := wdaReq.GetCurrentDomain()
1084 requestingDomain := wdaReq.GetRequestingDomain()1086 requestingDomain := wdaReq.GetRequestingDomain()
@@ -1086,20 +1088,11 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint
1086 Str("current_domain", currentDomain).1088 Str("current_domain", currentDomain).
1087 Str("requesting_domain", requestingDomain).1089 Str("requesting_domain", requestingDomain).
1088 Msg("website data access permission request")1090 Msg("website data access permission request")
1089- override := ""1091+ meta := map[string]string{
1090- if requestingDomain != "" {1092+ entity.PermissionMetadataKeyRequestingDomain: requestingDomain,
1091- candidate := "https://" + requestingDomain1093+ entity.PermissionMetadataKeyCurrentDomain: currentDomain,
1092- if normalized, err := urlutil.ExtractOrigin(candidate); err == nil {
1093- override = normalized
1094- } else {
1095- wv.logger.Warn().
1096- Str("requesting_domain", requestingDomain).
1097- Err(err).
1098- Msg("website data access: failed to normalize requesting domain, using raw")
1099- override = candidate
1100- }
1101 }1094 }
1102- return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), override1095+ return classifyPermissionRequestTypes(ctx, requestKind, false, false, false), meta
1103 default:1096 default:
1104 if requestPtr != 0 {1097 if requestPtr != 0 {
1105 typeName := permissionRequestTypeName(ctx, requestPtr)1098 typeName := permissionRequestTypeName(ctx, requestPtr)
@@ -1111,7 +1104,7 @@ func (wv *WebView) determinePermissionTypes(ctx context.Context, requestPtr uint
1111 }1104 }
1112 // Unknown permission type - could be clipboard, notifications, geolocation, etc.1105 // Unknown permission type - could be clipboard, notifications, geolocation, etc.
1113 // For now, return empty to trigger denial. Future phases will add these types.1106 // For now, return empty to trigger denial. Future phases will add these types.
1114- return nil, ""1107+ return nil, nil
1115 }1108 }
1116 }1109 }
1117 1110
M internal/logging/glib.go +1 -1
@@ -4,7 +4,7 @@ import (
44 "context"
55 "sync"
66
7- "github.com/jwijenbergh/puregotk/v4/glib"
7+ "github.com/bnema/puregotk/v4/glib"
88 "github.com/rs/zerolog"
99 )
1010
@@ -4,7 +4,7 @@ import (
4 "context"4 "context"
5 "sync"5 "sync"
6 6
7- "github.com/jwijenbergh/puregotk/v4/glib"7+ "github.com/bnema/puregotk/v4/glib"
8 "github.com/rs/zerolog"8 "github.com/rs/zerolog"
9 )9 )
10 10
M internal/ui/adapter/favicon.go +2 -2
@@ -8,8 +8,8 @@ import (
88 "github.com/bnema/dumber/internal/application/port"
99 domainurl "github.com/bnema/dumber/internal/domain/url"
1010 "github.com/bnema/dumber/internal/logging"
11- "github.com/jwijenbergh/puregotk/v4/gdk"
12- "github.com/jwijenbergh/puregotk/v4/glib"
11+ "github.com/bnema/puregotk/v4/gdk"
12+ "github.com/bnema/puregotk/v4/glib"
1313 "github.com/rs/zerolog"
1414 )
1515
@@ -8,8 +8,8 @@ import (
8 "github.com/bnema/dumber/internal/application/port"8 "github.com/bnema/dumber/internal/application/port"
9 domainurl "github.com/bnema/dumber/internal/domain/url"9 domainurl "github.com/bnema/dumber/internal/domain/url"
10 "github.com/bnema/dumber/internal/logging"10 "github.com/bnema/dumber/internal/logging"
11- "github.com/jwijenbergh/puregotk/v4/gdk"11+ "github.com/bnema/puregotk/v4/gdk"
12- "github.com/jwijenbergh/puregotk/v4/glib"12+ "github.com/bnema/puregotk/v4/glib"
13 "github.com/rs/zerolog"13 "github.com/rs/zerolog"
14 )14 )
15 15
M internal/ui/app.go +5 -5
@@ -27,11 +27,11 @@ import (
2727 "github.com/bnema/dumber/internal/ui/layout"
2828 "github.com/bnema/dumber/internal/ui/theme"
2929 "github.com/bnema/dumber/internal/ui/window"
30- "github.com/jwijenbergh/puregotk/v4/adw"
31- "github.com/jwijenbergh/puregotk/v4/gdk"
32- "github.com/jwijenbergh/puregotk/v4/gio"
33- "github.com/jwijenbergh/puregotk/v4/glib"
34- "github.com/jwijenbergh/puregotk/v4/gtk"
30+ "github.com/bnema/puregotk/v4/adw"
31+ "github.com/bnema/puregotk/v4/gdk"
32+ "github.com/bnema/puregotk/v4/gio"
33+ "github.com/bnema/puregotk/v4/glib"
34+ "github.com/bnema/puregotk/v4/gtk"
3535 )
3636
3737 const (
@@ -27,11 +27,11 @@ import (
27 "github.com/bnema/dumber/internal/ui/layout"27 "github.com/bnema/dumber/internal/ui/layout"
28 "github.com/bnema/dumber/internal/ui/theme"28 "github.com/bnema/dumber/internal/ui/theme"
29 "github.com/bnema/dumber/internal/ui/window"29 "github.com/bnema/dumber/internal/ui/window"
30- "github.com/jwijenbergh/puregotk/v4/adw"30+ "github.com/bnema/puregotk/v4/adw"
31- "github.com/jwijenbergh/puregotk/v4/gdk"31+ "github.com/bnema/puregotk/v4/gdk"
32- "github.com/jwijenbergh/puregotk/v4/gio"32+ "github.com/bnema/puregotk/v4/gio"
33- "github.com/jwijenbergh/puregotk/v4/glib"33+ "github.com/bnema/puregotk/v4/glib"
34- "github.com/jwijenbergh/puregotk/v4/gtk"34+ "github.com/bnema/puregotk/v4/gtk"
35 )35 )
36 36
37 const (37 const (
M internal/ui/app_flags_test.go +1 -1
@@ -3,7 +3,7 @@ package ui
33 import (
44 "testing"
55
6- "github.com/jwijenbergh/puregotk/v4/gio"
6+ "github.com/bnema/puregotk/v4/gio"
77 "github.com/stretchr/testify/assert"
88 )
99
@@ -3,7 +3,7 @@ package ui
3 import (3 import (
4 "testing"4 "testing"
5 5
6- "github.com/jwijenbergh/puregotk/v4/gio"6+ "github.com/bnema/puregotk/v4/gio"
7 "github.com/stretchr/testify/assert"7 "github.com/stretchr/testify/assert"
8 )8 )
9 9
M internal/ui/component/accent_picker.go +2 -2
@@ -5,8 +5,8 @@ import (
55
66 "github.com/bnema/dumber/internal/application/port"
77 "github.com/bnema/dumber/internal/ui/layout"
8- "github.com/jwijenbergh/puregotk/v4/gdk"
9- "github.com/jwijenbergh/puregotk/v4/gtk"
8+ "github.com/bnema/puregotk/v4/gdk"
9+ "github.com/bnema/puregotk/v4/gtk"
1010 )
1111
1212 // Accent picker layout constants.
@@ -5,8 +5,8 @@ import (
5 5
6 "github.com/bnema/dumber/internal/application/port"6 "github.com/bnema/dumber/internal/application/port"
7 "github.com/bnema/dumber/internal/ui/layout"7 "github.com/bnema/dumber/internal/ui/layout"
8- "github.com/jwijenbergh/puregotk/v4/gdk"8+ "github.com/bnema/puregotk/v4/gdk"
9- "github.com/jwijenbergh/puregotk/v4/gtk"9+ "github.com/bnema/puregotk/v4/gtk"
10 )10 )
11 11
12 // Accent picker layout constants.12 // Accent picker layout constants.
M internal/ui/component/find_bar.go +3 -3
@@ -11,9 +11,9 @@ import (
1111 "github.com/bnema/dumber/internal/domain/entity"
1212 "github.com/bnema/dumber/internal/logging"
1313 "github.com/bnema/dumber/internal/ui/layout"
14- "github.com/jwijenbergh/puregotk/v4/gdk"
15- "github.com/jwijenbergh/puregotk/v4/glib"
16- "github.com/jwijenbergh/puregotk/v4/gtk"
14+ "github.com/bnema/puregotk/v4/gdk"
15+ "github.com/bnema/puregotk/v4/glib"
16+ "github.com/bnema/puregotk/v4/gtk"
1717 )
1818
1919 const (
@@ -11,9 +11,9 @@ import (
11 "github.com/bnema/dumber/internal/domain/entity"11 "github.com/bnema/dumber/internal/domain/entity"
12 "github.com/bnema/dumber/internal/logging"12 "github.com/bnema/dumber/internal/logging"
13 "github.com/bnema/dumber/internal/ui/layout"13 "github.com/bnema/dumber/internal/ui/layout"
14- "github.com/jwijenbergh/puregotk/v4/gdk"14+ "github.com/bnema/puregotk/v4/gdk"
15- "github.com/jwijenbergh/puregotk/v4/glib"15+ "github.com/bnema/puregotk/v4/glib"
16- "github.com/jwijenbergh/puregotk/v4/gtk"16+ "github.com/bnema/puregotk/v4/gtk"
17 )17 )
18 18
19 const (19 const (
M internal/ui/component/link_status.go +2 -2
@@ -5,8 +5,8 @@ import (
55 "sync"
66
77 "github.com/bnema/dumber/internal/ui/mainloop"
8- "github.com/jwijenbergh/puregotk/v4/glib"
9- "github.com/jwijenbergh/puregotk/v4/gtk"
8+ "github.com/bnema/puregotk/v4/glib"
9+ "github.com/bnema/puregotk/v4/gtk"
1010
1111 "github.com/bnema/dumber/internal/ui/layout"
1212 )
@@ -5,8 +5,8 @@ import (
5 "sync"5 "sync"
6 6
7 "github.com/bnema/dumber/internal/ui/mainloop"7 "github.com/bnema/dumber/internal/ui/mainloop"
8- "github.com/jwijenbergh/puregotk/v4/glib"8+ "github.com/bnema/puregotk/v4/glib"
9- "github.com/jwijenbergh/puregotk/v4/gtk"9+ "github.com/bnema/puregotk/v4/gtk"
10 10
11 "github.com/bnema/dumber/internal/ui/layout"11 "github.com/bnema/dumber/internal/ui/layout"
12 )12 )
M internal/ui/component/loading_skeleton.go +1 -1
@@ -4,7 +4,7 @@ import (
44 "sync/atomic"
55
66 "github.com/bnema/dumber/internal/ui/layout"
7- "github.com/jwijenbergh/puregotk/v4/gtk"
7+ "github.com/bnema/puregotk/v4/gtk"
88 )
99
1010 // LoadingSkeleton displays a themed loading placeholder.
@@ -4,7 +4,7 @@ import (
4 "sync/atomic"4 "sync/atomic"
5 5
6 "github.com/bnema/dumber/internal/ui/layout"6 "github.com/bnema/dumber/internal/ui/layout"
7- "github.com/jwijenbergh/puregotk/v4/gtk"7+ "github.com/bnema/puregotk/v4/gtk"
8 )8 )
9 9
10 // LoadingSkeleton displays a themed loading placeholder.10 // LoadingSkeleton displays a themed loading placeholder.
M internal/ui/component/logo.go +2 -2
@@ -5,8 +5,8 @@ import (
55 "sync"
66
77 "github.com/bnema/dumber/assets"
8- "github.com/jwijenbergh/puregotk/v4/gdk"
9- "github.com/jwijenbergh/puregotk/v4/glib"
8+ "github.com/bnema/puregotk/v4/gdk"
9+ "github.com/bnema/puregotk/v4/glib"
1010 )
1111
1212 var (
@@ -5,8 +5,8 @@ import (
5 "sync"5 "sync"
6 6
7 "github.com/bnema/dumber/assets"7 "github.com/bnema/dumber/assets"
8- "github.com/jwijenbergh/puregotk/v4/gdk"8+ "github.com/bnema/puregotk/v4/gdk"
9- "github.com/jwijenbergh/puregotk/v4/glib"9+ "github.com/bnema/puregotk/v4/glib"
10 )10 )
11 11
12 var (12 var (
M internal/ui/component/modal_sizing.go +1 -1
@@ -2,7 +2,7 @@ package component
22
33 import (
44 "github.com/bnema/dumber/internal/ui/layout"
5- "github.com/jwijenbergh/puregotk/v4/gtk"
5+ "github.com/bnema/puregotk/v4/gtk"
66 )
77
88 // ModalSizeConfig holds configuration for modal sizing calculations.
@@ -2,7 +2,7 @@ package component
2 2
3 import (3 import (
4 "github.com/bnema/dumber/internal/ui/layout"4 "github.com/bnema/dumber/internal/ui/layout"
5- "github.com/jwijenbergh/puregotk/v4/gtk"5+ "github.com/bnema/puregotk/v4/gtk"
6 )6 )
7 7
8 // ModalSizeConfig holds configuration for modal sizing calculations.8 // ModalSizeConfig holds configuration for modal sizing calculations.
M internal/ui/component/omnibox.go +4 -4
@@ -18,10 +18,10 @@ import (
1818 "github.com/bnema/dumber/internal/ui/input"
1919 "github.com/bnema/dumber/internal/ui/layout"
2020 "github.com/bnema/dumber/internal/ui/mainloop"
21- "github.com/jwijenbergh/puregotk/v4/gdk"
22- "github.com/jwijenbergh/puregotk/v4/glib"
23- "github.com/jwijenbergh/puregotk/v4/graphene"
24- "github.com/jwijenbergh/puregotk/v4/gtk"
21+ "github.com/bnema/puregotk/v4/gdk"
22+ "github.com/bnema/puregotk/v4/glib"
23+ "github.com/bnema/puregotk/v4/graphene"
24+ "github.com/bnema/puregotk/v4/gtk"
2525 )
2626
2727 const (
@@ -18,10 +18,10 @@ import (
18 "github.com/bnema/dumber/internal/ui/input"18 "github.com/bnema/dumber/internal/ui/input"
19 "github.com/bnema/dumber/internal/ui/layout"19 "github.com/bnema/dumber/internal/ui/layout"
20 "github.com/bnema/dumber/internal/ui/mainloop"20 "github.com/bnema/dumber/internal/ui/mainloop"
21- "github.com/jwijenbergh/puregotk/v4/gdk"21+ "github.com/bnema/puregotk/v4/gdk"
22- "github.com/jwijenbergh/puregotk/v4/glib"22+ "github.com/bnema/puregotk/v4/glib"
23- "github.com/jwijenbergh/puregotk/v4/graphene"23+ "github.com/bnema/puregotk/v4/graphene"
24- "github.com/jwijenbergh/puregotk/v4/gtk"24+ "github.com/bnema/puregotk/v4/gtk"
25 )25 )
26 26
27 const (27 const (
M internal/ui/component/omnibox_navigation_state_test.go +1 -1
@@ -3,7 +3,7 @@ package component
33 import (
44 "testing"
55
6- "github.com/jwijenbergh/puregotk/v4/gdk"
6+ "github.com/bnema/puregotk/v4/gdk"
77 )
88
99 func TestShouldPreferTypedURLNavigation(t *testing.T) {
@@ -3,7 +3,7 @@ package component
3 import (3 import (
4 "testing"4 "testing"
5 5
6- "github.com/jwijenbergh/puregotk/v4/gdk"6+ "github.com/bnema/puregotk/v4/gdk"
7 )7 )
8 8
9 func TestShouldPreferTypedURLNavigation(t *testing.T) {9 func TestShouldPreferTypedURLNavigation(t *testing.T) {
M internal/ui/component/permission_popup.go +2 -2
@@ -6,8 +6,8 @@ import (
66
77 "github.com/bnema/dumber/internal/logging"
88 "github.com/bnema/dumber/internal/ui/layout"
9- "github.com/jwijenbergh/puregotk/v4/gdk"
10- "github.com/jwijenbergh/puregotk/v4/gtk"
9+ "github.com/bnema/puregotk/v4/gdk"
10+ "github.com/bnema/puregotk/v4/gtk"
1111 )
1212
1313 // buttonSpacing is the spacing between buttons in the permission popup.
@@ -6,8 +6,8 @@ import (
6 6
7 "github.com/bnema/dumber/internal/logging"7 "github.com/bnema/dumber/internal/logging"
8 "github.com/bnema/dumber/internal/ui/layout"8 "github.com/bnema/dumber/internal/ui/layout"
9- "github.com/jwijenbergh/puregotk/v4/gdk"9+ "github.com/bnema/puregotk/v4/gdk"
10- "github.com/jwijenbergh/puregotk/v4/gtk"10+ "github.com/bnema/puregotk/v4/gtk"
11 )11 )
12 12
13 // buttonSpacing is the spacing between buttons in the permission popup.13 // buttonSpacing is the spacing between buttons in the permission popup.
M internal/ui/component/progress_bar.go +2 -2
@@ -4,8 +4,8 @@ package component
44 import (
55 "sync"
66
7- "github.com/jwijenbergh/puregotk/v4/glib"
8- "github.com/jwijenbergh/puregotk/v4/gtk"
7+ "github.com/bnema/puregotk/v4/glib"
8+ "github.com/bnema/puregotk/v4/gtk"
99
1010 "github.com/bnema/dumber/internal/ui/layout"
1111 )
@@ -4,8 +4,8 @@ package component
4 import (4 import (
5 "sync"5 "sync"
6 6
7- "github.com/jwijenbergh/puregotk/v4/glib"7+ "github.com/bnema/puregotk/v4/glib"
8- "github.com/jwijenbergh/puregotk/v4/gtk"8+ "github.com/bnema/puregotk/v4/gtk"
9 9
10 "github.com/bnema/dumber/internal/ui/layout"10 "github.com/bnema/dumber/internal/ui/layout"
11 )11 )
M internal/ui/component/session_manager.go +3 -3
@@ -11,9 +11,9 @@ import (
1111 "github.com/bnema/dumber/internal/domain/entity"
1212 "github.com/bnema/dumber/internal/logging"
1313 "github.com/bnema/dumber/internal/ui/layout"
14- "github.com/jwijenbergh/puregotk/v4/gdk"
15- "github.com/jwijenbergh/puregotk/v4/glib"
16- "github.com/jwijenbergh/puregotk/v4/gtk"
14+ "github.com/bnema/puregotk/v4/gdk"
15+ "github.com/bnema/puregotk/v4/glib"
16+ "github.com/bnema/puregotk/v4/gtk"
1717 )
1818
1919 const (
@@ -11,9 +11,9 @@ import (
11 "github.com/bnema/dumber/internal/domain/entity"11 "github.com/bnema/dumber/internal/domain/entity"
12 "github.com/bnema/dumber/internal/logging"12 "github.com/bnema/dumber/internal/logging"
13 "github.com/bnema/dumber/internal/ui/layout"13 "github.com/bnema/dumber/internal/ui/layout"
14- "github.com/jwijenbergh/puregotk/v4/gdk"14+ "github.com/bnema/puregotk/v4/gdk"
15- "github.com/jwijenbergh/puregotk/v4/glib"15+ "github.com/bnema/puregotk/v4/glib"
16- "github.com/jwijenbergh/puregotk/v4/gtk"16+ "github.com/bnema/puregotk/v4/gtk"
17 )17 )
18 18
19 const (19 const (
M internal/ui/component/tab_bar.go +1 -1
@@ -4,7 +4,7 @@ import (
44 "sync"
55
66 "github.com/bnema/dumber/internal/domain/entity"
7- "github.com/jwijenbergh/puregotk/v4/gtk"
7+ "github.com/bnema/puregotk/v4/gtk"
88 )
99
1010 // TabBar represents the horizontal tab bar widget.
@@ -4,7 +4,7 @@ import (
4 "sync"4 "sync"
5 5
6 "github.com/bnema/dumber/internal/domain/entity"6 "github.com/bnema/dumber/internal/domain/entity"
7- "github.com/jwijenbergh/puregotk/v4/gtk"7+ "github.com/bnema/puregotk/v4/gtk"
8 )8 )
9 9
10 // TabBar represents the horizontal tab bar widget.10 // TabBar represents the horizontal tab bar widget.
M internal/ui/component/tab_button.go +2 -2
@@ -3,8 +3,8 @@ package component
33
44 import (
55 "github.com/bnema/dumber/internal/domain/entity"
6- "github.com/jwijenbergh/puregotk/v4/gtk"
7- "github.com/jwijenbergh/puregotk/v4/pango"
6+ "github.com/bnema/puregotk/v4/gtk"
7+ "github.com/bnema/puregotk/v4/pango"
88 )
99
1010 // TabButton represents a single tab button in the tab bar.
@@ -3,8 +3,8 @@ package component
3 3
4 import (4 import (
5 "github.com/bnema/dumber/internal/domain/entity"5 "github.com/bnema/dumber/internal/domain/entity"
6- "github.com/jwijenbergh/puregotk/v4/gtk"6+ "github.com/bnema/puregotk/v4/gtk"
7- "github.com/jwijenbergh/puregotk/v4/pango"7+ "github.com/bnema/puregotk/v4/pango"
8 )8 )
9 9
10 // TabButton represents a single tab button in the tab bar.10 // TabButton represents a single tab button in the tab bar.
M internal/ui/component/tab_picker.go +3 -3
@@ -6,9 +6,9 @@ import (
66
77 "github.com/bnema/dumber/internal/domain/entity"
88 "github.com/bnema/dumber/internal/ui/layout"
9- "github.com/jwijenbergh/puregotk/v4/gdk"
10- "github.com/jwijenbergh/puregotk/v4/glib"
11- "github.com/jwijenbergh/puregotk/v4/gtk"
9+ "github.com/bnema/puregotk/v4/gdk"
10+ "github.com/bnema/puregotk/v4/glib"
11+ "github.com/bnema/puregotk/v4/gtk"
1212 )
1313
1414 type TabPickerItem struct {
@@ -6,9 +6,9 @@ import (
6 6
7 "github.com/bnema/dumber/internal/domain/entity"7 "github.com/bnema/dumber/internal/domain/entity"
8 "github.com/bnema/dumber/internal/ui/layout"8 "github.com/bnema/dumber/internal/ui/layout"
9- "github.com/jwijenbergh/puregotk/v4/gdk"9+ "github.com/bnema/puregotk/v4/gdk"
10- "github.com/jwijenbergh/puregotk/v4/glib"10+ "github.com/bnema/puregotk/v4/glib"
11- "github.com/jwijenbergh/puregotk/v4/gtk"11+ "github.com/bnema/puregotk/v4/gtk"
12 )12 )
13 13
14 type TabPickerItem struct {14 type TabPickerItem struct {
M internal/ui/component/toaster.go +2 -2
@@ -6,8 +6,8 @@ import (
66 "fmt"
77 "sync"
88
9- "github.com/jwijenbergh/puregotk/v4/glib"
10- "github.com/jwijenbergh/puregotk/v4/gtk"
9+ "github.com/bnema/puregotk/v4/glib"
10+ "github.com/bnema/puregotk/v4/gtk"
1111
1212 "github.com/bnema/dumber/internal/logging"
1313 "github.com/bnema/dumber/internal/ui/layout"
@@ -6,8 +6,8 @@ import (
6 "fmt"6 "fmt"
7 "sync"7 "sync"
8 8
9- "github.com/jwijenbergh/puregotk/v4/glib"9+ "github.com/bnema/puregotk/v4/glib"
10- "github.com/jwijenbergh/puregotk/v4/gtk"10+ "github.com/bnema/puregotk/v4/gtk"
11 11
12 "github.com/bnema/dumber/internal/logging"12 "github.com/bnema/dumber/internal/logging"
13 "github.com/bnema/dumber/internal/ui/layout"13 "github.com/bnema/dumber/internal/ui/layout"
M internal/ui/component/webrtc_permission_indicator.go +1 -1
@@ -5,7 +5,7 @@ import (
55 "sync"
66
77 "github.com/bnema/dumber/internal/domain/entity"
8- "github.com/jwijenbergh/puregotk/v4/gtk"
8+ "github.com/bnema/puregotk/v4/gtk"
99 )
1010
1111 // revealerTransitionDuration is the animation duration in milliseconds
@@ -5,7 +5,7 @@ import (
5 "sync"5 "sync"
6 6
7 "github.com/bnema/dumber/internal/domain/entity"7 "github.com/bnema/dumber/internal/domain/entity"
8- "github.com/jwijenbergh/puregotk/v4/gtk"8+ "github.com/bnema/puregotk/v4/gtk"
9 )9 )
10 10
11 // revealerTransitionDuration is the animation duration in milliseconds11 // revealerTransitionDuration is the animation duration in milliseconds
M internal/ui/component/workspace_view.go +1 -1
@@ -12,7 +12,7 @@ import (
1212 "github.com/bnema/dumber/internal/domain/entity"
1313 "github.com/bnema/dumber/internal/logging"
1414 "github.com/bnema/dumber/internal/ui/layout"
15- "github.com/jwijenbergh/puregotk/v4/glib"
15+ "github.com/bnema/puregotk/v4/glib"
1616 "github.com/rs/zerolog"
1717 )
1818
@@ -12,7 +12,7 @@ import (
12 "github.com/bnema/dumber/internal/domain/entity"12 "github.com/bnema/dumber/internal/domain/entity"
13 "github.com/bnema/dumber/internal/logging"13 "github.com/bnema/dumber/internal/logging"
14 "github.com/bnema/dumber/internal/ui/layout"14 "github.com/bnema/dumber/internal/ui/layout"
15- "github.com/jwijenbergh/puregotk/v4/glib"15+ "github.com/bnema/puregotk/v4/glib"
16 "github.com/rs/zerolog"16 "github.com/rs/zerolog"
17 )17 )
18 18
M internal/ui/component/workspace_view_test.go +1 -1
@@ -4,7 +4,7 @@ import (
44 "context"
55 "testing"
66
7- "github.com/jwijenbergh/puregotk/v4/gtk"
7+ "github.com/bnema/puregotk/v4/gtk"
88 "github.com/stretchr/testify/assert"
99 "github.com/stretchr/testify/mock"
1010 "github.com/stretchr/testify/require"
@@ -4,7 +4,7 @@ import (
4 "context"4 "context"
5 "testing"5 "testing"
6 6
7- "github.com/jwijenbergh/puregotk/v4/gtk"7+ "github.com/bnema/puregotk/v4/gtk"
8 "github.com/stretchr/testify/assert"8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/mock"9 "github.com/stretchr/testify/mock"
10 "github.com/stretchr/testify/require"10 "github.com/stretchr/testify/require"
M internal/ui/coordinator/content/callbacks.go +5 -4
@@ -9,7 +9,7 @@ import (
99 "github.com/bnema/dumber/internal/application/usecase"
1010 "github.com/bnema/dumber/internal/domain/entity"
1111 "github.com/bnema/dumber/internal/logging"
12- "github.com/jwijenbergh/puregotk/v4/gdk"
12+ "github.com/bnema/puregotk/v4/gdk"
1313 )
1414
1515 // internalSchemePath is the path used in dumb:// URIs (replaces webkit.HomePath).
@@ -117,8 +117,8 @@ func (c *Coordinator) setupWebViewCallbacks(ctx context.Context, paneID entity.P
117117 Msg("failed to load crash page after web process termination")
118118 }
119119 },
120- OnPermissionRequest: func(origin string, permTypes []string, allow, deny func()) bool {
121- return c.handlePermissionRequest(ctx, origin, permTypes, allow, deny)
120+ OnPermissionRequest: func(origin string, permTypes []string, metadata map[string]string, allow, deny func()) bool {
121+ return c.handlePermissionRequest(ctx, origin, permTypes, metadata, allow, deny)
122122 },
123123 }
124124
@@ -191,6 +191,7 @@ func (c *Coordinator) handlePermissionRequest(
191191 ctx context.Context,
192192 origin string,
193193 permTypes []string,
194+ metadata map[string]string,
194195 allow, deny func(),
195196 ) bool {
196197 log := logging.FromContext(ctx)
@@ -264,7 +265,7 @@ func (c *Coordinator) handlePermissionRequest(
264265 Deny: wrappedDeny,
265266 }
266267
267- c.permissionUC.HandlePermissionRequest(ctx, origin, entityTypes, callback)
268+ c.permissionUC.HandlePermissionRequest(ctx, origin, entityTypes, entity.PermissionMetadata(metadata), callback)
268269 return true
269270 }
270271
@@ -9,7 +9,7 @@ import (
9 "github.com/bnema/dumber/internal/application/usecase"9 "github.com/bnema/dumber/internal/application/usecase"
10 "github.com/bnema/dumber/internal/domain/entity"10 "github.com/bnema/dumber/internal/domain/entity"
11 "github.com/bnema/dumber/internal/logging"11 "github.com/bnema/dumber/internal/logging"
12- "github.com/jwijenbergh/puregotk/v4/gdk"12+ "github.com/bnema/puregotk/v4/gdk"
13 )13 )
14 14
15 // internalSchemePath is the path used in dumb:// URIs (replaces webkit.HomePath).15 // internalSchemePath is the path used in dumb:// URIs (replaces webkit.HomePath).
@@ -117,8 +117,8 @@ func (c *Coordinator) setupWebViewCallbacks(ctx context.Context, paneID entity.P
117 Msg("failed to load crash page after web process termination")117 Msg("failed to load crash page after web process termination")
118 }118 }
119 },119 },
120- OnPermissionRequest: func(origin string, permTypes []string, allow, deny func()) bool {120+ OnPermissionRequest: func(origin string, permTypes []string, metadata map[string]string, allow, deny func()) bool {
121- return c.handlePermissionRequest(ctx, origin, permTypes, allow, deny)121+ return c.handlePermissionRequest(ctx, origin, permTypes, metadata, allow, deny)
122 },122 },
123 }123 }
124 124
@@ -191,6 +191,7 @@ func (c *Coordinator) handlePermissionRequest(
191 ctx context.Context,191 ctx context.Context,
192 origin string,192 origin string,
193 permTypes []string,193 permTypes []string,
194+ metadata map[string]string,
194 allow, deny func(),195 allow, deny func(),
195 ) bool {196 ) bool {
196 log := logging.FromContext(ctx)197 log := logging.FromContext(ctx)
@@ -264,7 +265,7 @@ func (c *Coordinator) handlePermissionRequest(
264 Deny: wrappedDeny,265 Deny: wrappedDeny,
265 }266 }
266 267
267- c.permissionUC.HandlePermissionRequest(ctx, origin, entityTypes, callback)268+ c.permissionUC.HandlePermissionRequest(ctx, origin, entityTypes, entity.PermissionMetadata(metadata), callback)
268 return true269 return true
269 }270 }
270 271
M internal/ui/coordinator/content/lifecycle.go +1 -1
@@ -10,7 +10,7 @@ import (
1010 "github.com/bnema/dumber/internal/ui/component"
1111 "github.com/bnema/dumber/internal/ui/input"
1212 "github.com/bnema/dumber/internal/ui/layout"
13- "github.com/jwijenbergh/puregotk/v4/gtk"
13+ "github.com/bnema/puregotk/v4/gtk"
1414 )
1515
1616 // EnsureWebView acquires or reuses a WebView for the given pane.
@@ -10,7 +10,7 @@ import (
10 "github.com/bnema/dumber/internal/ui/component"10 "github.com/bnema/dumber/internal/ui/component"
11 "github.com/bnema/dumber/internal/ui/input"11 "github.com/bnema/dumber/internal/ui/input"
12 "github.com/bnema/dumber/internal/ui/layout"12 "github.com/bnema/dumber/internal/ui/layout"
13- "github.com/jwijenbergh/puregotk/v4/gtk"13+ "github.com/bnema/puregotk/v4/gtk"
14 )14 )
15 15
16 // EnsureWebView acquires or reuses a WebView for the given pane.16 // EnsureWebView acquires or reuses a WebView for the given pane.
M internal/ui/coordinator/content/navigation.go +1 -1
@@ -9,7 +9,7 @@ import (
99 "github.com/bnema/dumber/internal/domain/entity"
1010 urlutil "github.com/bnema/dumber/internal/domain/url"
1111 "github.com/bnema/dumber/internal/logging"
12- "github.com/jwijenbergh/puregotk/v4/gtk"
12+ "github.com/bnema/puregotk/v4/gtk"
1313 )
1414
1515 // onLoadCommitted re-applies zoom when page content starts loading and records history.
@@ -9,7 +9,7 @@ import (
9 "github.com/bnema/dumber/internal/domain/entity"9 "github.com/bnema/dumber/internal/domain/entity"
10 urlutil "github.com/bnema/dumber/internal/domain/url"10 urlutil "github.com/bnema/dumber/internal/domain/url"
11 "github.com/bnema/dumber/internal/logging"11 "github.com/bnema/dumber/internal/logging"
12- "github.com/jwijenbergh/puregotk/v4/gtk"12+ "github.com/bnema/puregotk/v4/gtk"
13 )13 )
14 14
15 // onLoadCommitted re-applies zoom when page content starts loading and records history.15 // onLoadCommitted re-applies zoom when page content starts loading and records history.
M internal/ui/coordinator/content/oauth.go +1 -1
@@ -10,7 +10,7 @@ import (
1010 "github.com/bnema/dumber/internal/application/port"
1111 "github.com/bnema/dumber/internal/domain/entity"
1212 "github.com/bnema/dumber/internal/logging"
13- "github.com/jwijenbergh/puregotk/v4/glib"
13+ "github.com/bnema/puregotk/v4/glib"
1414 )
1515
1616 // oauthFlowPatterns are URL patterns that indicate an OAuth flow is in progress.
@@ -10,7 +10,7 @@ import (
10 "github.com/bnema/dumber/internal/application/port"10 "github.com/bnema/dumber/internal/application/port"
11 "github.com/bnema/dumber/internal/domain/entity"11 "github.com/bnema/dumber/internal/domain/entity"
12 "github.com/bnema/dumber/internal/logging"12 "github.com/bnema/dumber/internal/logging"
13- "github.com/jwijenbergh/puregotk/v4/glib"13+ "github.com/bnema/puregotk/v4/glib"
14 )14 )
15 15
16 // oauthFlowPatterns are URL patterns that indicate an OAuth flow is in progress.16 // oauthFlowPatterns are URL patterns that indicate an OAuth flow is in progress.
M internal/ui/coordinator/content/title.go +2 -2
@@ -9,8 +9,8 @@ import (
99 "github.com/bnema/dumber/internal/logging"
1010 "github.com/bnema/dumber/internal/ui/adapter"
1111 "github.com/bnema/dumber/internal/ui/layout"
12- "github.com/jwijenbergh/puregotk/v4/gdk"
13- "github.com/jwijenbergh/puregotk/v4/glib"
12+ "github.com/bnema/puregotk/v4/gdk"
13+ "github.com/bnema/puregotk/v4/glib"
1414 )
1515
1616 // GetTitle returns the current title for a pane.
@@ -9,8 +9,8 @@ import (
9 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
10 "github.com/bnema/dumber/internal/ui/adapter"10 "github.com/bnema/dumber/internal/ui/adapter"
11 "github.com/bnema/dumber/internal/ui/layout"11 "github.com/bnema/dumber/internal/ui/layout"
12- "github.com/jwijenbergh/puregotk/v4/gdk"12+ "github.com/bnema/puregotk/v4/gdk"
13- "github.com/jwijenbergh/puregotk/v4/glib"13+ "github.com/bnema/puregotk/v4/glib"
14 )14 )
15 15
16 // GetTitle returns the current title for a pane.16 // GetTitle returns the current title for a pane.
M internal/ui/coordinator/update.go +1 -1
@@ -5,7 +5,7 @@ import (
55 "fmt"
66 "sync"
77
8- "github.com/jwijenbergh/puregotk/v4/glib"
8+ "github.com/bnema/puregotk/v4/glib"
99
1010 "github.com/bnema/dumber/internal/application/usecase"
1111 "github.com/bnema/dumber/internal/domain/entity"
@@ -5,7 +5,7 @@ import (
5 "fmt"5 "fmt"
6 "sync"6 "sync"
7 7
8- "github.com/jwijenbergh/puregotk/v4/glib"8+ "github.com/bnema/puregotk/v4/glib"
9 9
10 "github.com/bnema/dumber/internal/application/usecase"10 "github.com/bnema/dumber/internal/application/usecase"
11 "github.com/bnema/dumber/internal/domain/entity"11 "github.com/bnema/dumber/internal/domain/entity"
M internal/ui/deps.go +1 -1
@@ -11,7 +11,7 @@ import (
1111 "github.com/bnema/dumber/internal/infrastructure/config"
1212 "github.com/bnema/dumber/internal/ui/adapter"
1313 "github.com/bnema/dumber/internal/ui/theme"
14- "github.com/jwijenbergh/puregotk/v4/gtk"
14+ "github.com/bnema/puregotk/v4/gtk"
1515 )
1616
1717 // Dependencies holds all injected dependencies for the UI layer.
@@ -11,7 +11,7 @@ import (
11 "github.com/bnema/dumber/internal/infrastructure/config"11 "github.com/bnema/dumber/internal/infrastructure/config"
12 "github.com/bnema/dumber/internal/ui/adapter"12 "github.com/bnema/dumber/internal/ui/adapter"
13 "github.com/bnema/dumber/internal/ui/theme"13 "github.com/bnema/dumber/internal/ui/theme"
14- "github.com/jwijenbergh/puregotk/v4/gtk"14+ "github.com/bnema/puregotk/v4/gtk"
15 )15 )
16 16
17 // Dependencies holds all injected dependencies for the UI layer.17 // Dependencies holds all injected dependencies for the UI layer.
M internal/ui/dialog/permission.go +13 -5
@@ -21,6 +21,7 @@ type permissionDialogRequest struct {
2121 ctx context.Context
2222 origin string
2323 permTypes []entity.PermissionType
24+ metadata entity.PermissionMetadata
2425 callback func(result port.PermissionDialogResult)
2526 }
2627
@@ -48,12 +49,14 @@ func (d *PermissionDialog) ShowPermissionDialog(
4849 ctx context.Context,
4950 origin string,
5051 permTypes []entity.PermissionType,
52+ metadata entity.PermissionMetadata,
5153 callback func(result port.PermissionDialogResult),
5254 ) {
5355 req := permissionDialogRequest{
5456 ctx: ctx,
5557 origin: origin,
5658 permTypes: permTypes,
59+ metadata: metadata,
5760 callback: callback,
5861 }
5962
@@ -94,7 +97,7 @@ func (d *PermissionDialog) showRequest(req permissionDialogRequest) {
9497
9598 // Build dialog text
9699 heading := d.buildHeading(permTypes)
97- body := d.buildBody(origin, permTypes)
100+ body := d.buildBody(origin, permTypes, req.metadata)
98101
99102 d.popup.Show(ctx, heading, body, func(allowed, persistent bool) {
100103 log.Debug().
@@ -198,7 +201,7 @@ func (d *PermissionDialog) buildHeading(
198201
199202 // buildBody creates the dialog body text.
200203 func (d *PermissionDialog) buildBody(
201- origin string, permTypes []entity.PermissionType,
204+ origin string, permTypes []entity.PermissionType, metadata entity.PermissionMetadata,
202205 ) string {
203206 f := parsePermFlags(permTypes)
204207 var parts []string
@@ -213,9 +216,14 @@ func (d *PermissionDialog) buildBody(
213216 parts = append(parts, "share your screen")
214217 }
215218 if f.dataAccess {
216- if len(parts) == 0 {
217- parts = append(parts,
218- "access its stored data while you browse this site")
219+ reqDomain := metadata[entity.PermissionMetadataKeyRequestingDomain]
220+ curDomain := metadata[entity.PermissionMetadataKeyCurrentDomain]
221+ if reqDomain != "" && curDomain != "" {
222+ parts = append(parts, fmt.Sprintf(
223+ "allow %s to access its data (including cookies) while you browse %s",
224+ reqDomain, curDomain))
225+ } else if len(parts) == 0 {
226+ parts = append(parts, "access its stored data while you browse this site")
219227 } else {
220228 parts = append(parts, "access its stored data")
221229 }
@@ -21,6 +21,7 @@ type permissionDialogRequest struct {
21 ctx context.Context21 ctx context.Context
22 origin string22 origin string
23 permTypes []entity.PermissionType23 permTypes []entity.PermissionType
24+ metadata entity.PermissionMetadata
24 callback func(result port.PermissionDialogResult)25 callback func(result port.PermissionDialogResult)
25 }26 }
26 27
@@ -48,12 +49,14 @@ func (d *PermissionDialog) ShowPermissionDialog(
48 ctx context.Context,49 ctx context.Context,
49 origin string,50 origin string,
50 permTypes []entity.PermissionType,51 permTypes []entity.PermissionType,
52+ metadata entity.PermissionMetadata,
51 callback func(result port.PermissionDialogResult),53 callback func(result port.PermissionDialogResult),
52 ) {54 ) {
53 req := permissionDialogRequest{55 req := permissionDialogRequest{
54 ctx: ctx,56 ctx: ctx,
55 origin: origin,57 origin: origin,
56 permTypes: permTypes,58 permTypes: permTypes,
59+ metadata: metadata,
57 callback: callback,60 callback: callback,
58 }61 }
59 62
@@ -94,7 +97,7 @@ func (d *PermissionDialog) showRequest(req permissionDialogRequest) {
94 97
95 // Build dialog text98 // Build dialog text
96 heading := d.buildHeading(permTypes)99 heading := d.buildHeading(permTypes)
97- body := d.buildBody(origin, permTypes)100+ body := d.buildBody(origin, permTypes, req.metadata)
98 101
99 d.popup.Show(ctx, heading, body, func(allowed, persistent bool) {102 d.popup.Show(ctx, heading, body, func(allowed, persistent bool) {
100 log.Debug().103 log.Debug().
@@ -198,7 +201,7 @@ func (d *PermissionDialog) buildHeading(
198 201
199 // buildBody creates the dialog body text.202 // buildBody creates the dialog body text.
200 func (d *PermissionDialog) buildBody(203 func (d *PermissionDialog) buildBody(
201- origin string, permTypes []entity.PermissionType,204+ origin string, permTypes []entity.PermissionType, metadata entity.PermissionMetadata,
202 ) string {205 ) string {
203 f := parsePermFlags(permTypes)206 f := parsePermFlags(permTypes)
204 var parts []string207 var parts []string
@@ -213,9 +216,14 @@ func (d *PermissionDialog) buildBody(
213 parts = append(parts, "share your screen")216 parts = append(parts, "share your screen")
214 }217 }
215 if f.dataAccess {218 if f.dataAccess {
216- if len(parts) == 0 {219+ reqDomain := metadata[entity.PermissionMetadataKeyRequestingDomain]
217- parts = append(parts,220+ curDomain := metadata[entity.PermissionMetadataKeyCurrentDomain]
218- "access its stored data while you browse this site")221+ if reqDomain != "" && curDomain != "" {
222+ parts = append(parts, fmt.Sprintf(
223+ "allow %s to access its data (including cookies) while you browse %s",
224+ reqDomain, curDomain))
225+ } else if len(parts) == 0 {
226+ parts = append(parts, "access its stored data while you browse this site")
219 } else {227 } else {
220 parts = append(parts, "access its stored data")228 parts = append(parts, "access its stored data")
221 }229 }
M internal/ui/dialog/permission_test.go +14 -1
@@ -51,6 +51,7 @@ func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) {
5151 context.Background(),
5252 "https://example.com",
5353 []entity.PermissionType{entity.PermissionTypeMicrophone},
54+ nil,
5455 func(result port.PermissionDialogResult) {
5556 firstCalled = true
5657 firstResult = result
@@ -63,6 +64,7 @@ func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) {
6364 context.Background(),
6465 "https://example.com",
6566 []entity.PermissionType{entity.PermissionTypeCamera},
67+ nil,
6668 func(result port.PermissionDialogResult) {
6769 secondCalled = true
6870 secondResult = result
@@ -98,6 +100,7 @@ func TestPermissionDialog_NoPopup_DeniesRequest(t *testing.T) {
98100 context.Background(),
99101 "https://example.com",
100102 []entity.PermissionType{entity.PermissionTypeMicrophone},
103+ nil,
101104 func(r port.PermissionDialogResult) {
102105 called = true
103106 result = r
@@ -153,7 +156,17 @@ func TestPermissionDialog_BuildHeadingAndBody_DisplayCombinations(t *testing.T)
153156 for _, tt := range tests {
154157 t.Run(tt.name, func(t *testing.T) {
155158 assert.Equal(t, tt.expectHeading, d.buildHeading(tt.permTypes))
156- assert.Equal(t, origin+" wants to "+tt.expectedAction+".", d.buildBody(origin, tt.permTypes))
159+ assert.Equal(t, origin+" wants to "+tt.expectedAction+".", d.buildBody(origin, tt.permTypes, nil))
157160 })
158161 }
162+
163+ // Test website_data_access with populated metadata (Epiphany-style rich message)
164+ t.Run("website data access with both domains", func(t *testing.T) {
165+ meta := entity.PermissionMetadata{
166+ entity.PermissionMetadataKeyRequestingDomain: "accounts.google.com",
167+ entity.PermissionMetadataKeyCurrentDomain: "shop.example.com",
168+ }
169+ body := d.buildBody(origin, []entity.PermissionType{entity.PermissionTypeWebsiteDataAccess}, meta)
170+ assert.Equal(t, origin+" wants to allow accounts.google.com to access its data (including cookies) while you browse shop.example.com.", body)
171+ })
159172 }
@@ -51,6 +51,7 @@ func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) {
51 context.Background(),51 context.Background(),
52 "https://example.com",52 "https://example.com",
53 []entity.PermissionType{entity.PermissionTypeMicrophone},53 []entity.PermissionType{entity.PermissionTypeMicrophone},
54+ nil,
54 func(result port.PermissionDialogResult) {55 func(result port.PermissionDialogResult) {
55 firstCalled = true56 firstCalled = true
56 firstResult = result57 firstResult = result
@@ -63,6 +64,7 @@ func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) {
63 context.Background(),64 context.Background(),
64 "https://example.com",65 "https://example.com",
65 []entity.PermissionType{entity.PermissionTypeCamera},66 []entity.PermissionType{entity.PermissionTypeCamera},
67+ nil,
66 func(result port.PermissionDialogResult) {68 func(result port.PermissionDialogResult) {
67 secondCalled = true69 secondCalled = true
68 secondResult = result70 secondResult = result
@@ -98,6 +100,7 @@ func TestPermissionDialog_NoPopup_DeniesRequest(t *testing.T) {
98 context.Background(),100 context.Background(),
99 "https://example.com",101 "https://example.com",
100 []entity.PermissionType{entity.PermissionTypeMicrophone},102 []entity.PermissionType{entity.PermissionTypeMicrophone},
103+ nil,
101 func(r port.PermissionDialogResult) {104 func(r port.PermissionDialogResult) {
102 called = true105 called = true
103 result = r106 result = r
@@ -153,7 +156,17 @@ func TestPermissionDialog_BuildHeadingAndBody_DisplayCombinations(t *testing.T)
153 for _, tt := range tests {156 for _, tt := range tests {
154 t.Run(tt.name, func(t *testing.T) {157 t.Run(tt.name, func(t *testing.T) {
155 assert.Equal(t, tt.expectHeading, d.buildHeading(tt.permTypes))158 assert.Equal(t, tt.expectHeading, d.buildHeading(tt.permTypes))
156- assert.Equal(t, origin+" wants to "+tt.expectedAction+".", d.buildBody(origin, tt.permTypes))159+ assert.Equal(t, origin+" wants to "+tt.expectedAction+".", d.buildBody(origin, tt.permTypes, nil))
157 })160 })
158 }161 }
162+
163+ // Test website_data_access with populated metadata (Epiphany-style rich message)
164+ t.Run("website data access with both domains", func(t *testing.T) {
165+ meta := entity.PermissionMetadata{
166+ entity.PermissionMetadataKeyRequestingDomain: "accounts.google.com",
167+ entity.PermissionMetadataKeyCurrentDomain: "shop.example.com",
168+ }
169+ body := d.buildBody(origin, []entity.PermissionType{entity.PermissionTypeWebsiteDataAccess}, meta)
170+ assert.Equal(t, origin+" wants to allow accounts.google.com to access its data (including cookies) while you browse shop.example.com.", body)
171+ })
159 }172 }
M internal/ui/dispatcher/keyboard.go +1 -1
@@ -11,7 +11,7 @@ import (
1111 "github.com/bnema/dumber/internal/ui/component"
1212 "github.com/bnema/dumber/internal/ui/coordinator"
1313 "github.com/bnema/dumber/internal/ui/input"
14- "github.com/jwijenbergh/puregotk/v4/glib"
14+ "github.com/bnema/puregotk/v4/glib"
1515 )
1616
1717 // KeyboardDispatcher routes keyboard actions to appropriate coordinators.
@@ -11,7 +11,7 @@ import (
11 "github.com/bnema/dumber/internal/ui/component"11 "github.com/bnema/dumber/internal/ui/component"
12 "github.com/bnema/dumber/internal/ui/coordinator"12 "github.com/bnema/dumber/internal/ui/coordinator"
13 "github.com/bnema/dumber/internal/ui/input"13 "github.com/bnema/dumber/internal/ui/input"
14- "github.com/jwijenbergh/puregotk/v4/glib"14+ "github.com/bnema/puregotk/v4/glib"
15 )15 )
16 16
17 // KeyboardDispatcher routes keyboard actions to appropriate coordinators.17 // KeyboardDispatcher routes keyboard actions to appropriate coordinators.
M internal/ui/input/floating_shortcuts_test.go +1 -1
@@ -5,7 +5,7 @@ import (
55 "testing"
66
77 "github.com/bnema/dumber/internal/domain/entity"
8- "github.com/jwijenbergh/puregotk/v4/gdk"
8+ "github.com/bnema/puregotk/v4/gdk"
99 "github.com/stretchr/testify/assert"
1010 "github.com/stretchr/testify/require"
1111 )
@@ -5,7 +5,7 @@ import (
5 "testing"5 "testing"
6 6
7 "github.com/bnema/dumber/internal/domain/entity"7 "github.com/bnema/dumber/internal/domain/entity"
8- "github.com/jwijenbergh/puregotk/v4/gdk"8+ "github.com/bnema/puregotk/v4/gdk"
9 "github.com/stretchr/testify/assert"9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"10 "github.com/stretchr/testify/require"
11 )11 )
M internal/ui/input/gesture.go +1 -1
@@ -5,7 +5,7 @@ import (
55 "sync"
66
77 "github.com/bnema/dumber/internal/logging"
8- "github.com/jwijenbergh/puregotk/v4/gtk"
8+ "github.com/bnema/puregotk/v4/gtk"
99 )
1010
1111 // DirectNavigator allows direct back/forward navigation on a WebView.
@@ -5,7 +5,7 @@ import (
5 "sync"5 "sync"
6 6
7 "github.com/bnema/dumber/internal/logging"7 "github.com/bnema/dumber/internal/logging"
8- "github.com/jwijenbergh/puregotk/v4/gtk"8+ "github.com/bnema/puregotk/v4/gtk"
9 )9 )
10 10
11 // DirectNavigator allows direct back/forward navigation on a WebView.11 // DirectNavigator allows direct back/forward navigation on a WebView.
M internal/ui/input/global_shortcuts.go +3 -3
@@ -8,9 +8,9 @@ import (
88
99 "github.com/bnema/dumber/internal/domain/entity"
1010 "github.com/bnema/dumber/internal/logging"
11- "github.com/jwijenbergh/puregotk/v4/gdk"
12- "github.com/jwijenbergh/puregotk/v4/glib"
13- "github.com/jwijenbergh/puregotk/v4/gtk"
11+ "github.com/bnema/puregotk/v4/gdk"
12+ "github.com/bnema/puregotk/v4/glib"
13+ "github.com/bnema/puregotk/v4/gtk"
1414 )
1515
1616 // GlobalShortcutHandler manages keyboard shortcuts that must work globally,
@@ -8,9 +8,9 @@ import (
8 8
9 "github.com/bnema/dumber/internal/domain/entity"9 "github.com/bnema/dumber/internal/domain/entity"
10 "github.com/bnema/dumber/internal/logging"10 "github.com/bnema/dumber/internal/logging"
11- "github.com/jwijenbergh/puregotk/v4/gdk"11+ "github.com/bnema/puregotk/v4/gdk"
12- "github.com/jwijenbergh/puregotk/v4/glib"12+ "github.com/bnema/puregotk/v4/glib"
13- "github.com/jwijenbergh/puregotk/v4/gtk"13+ "github.com/bnema/puregotk/v4/gtk"
14 )14 )
15 15
16 // GlobalShortcutHandler manages keyboard shortcuts that must work globally,16 // GlobalShortcutHandler manages keyboard shortcuts that must work globally,
M internal/ui/input/hover.go +1 -1
@@ -7,7 +7,7 @@ import (
77
88 "github.com/bnema/dumber/internal/domain/entity"
99 "github.com/bnema/dumber/internal/logging"
10- "github.com/jwijenbergh/puregotk/v4/gtk"
10+ "github.com/bnema/puregotk/v4/gtk"
1111 )
1212
1313 const (
@@ -7,7 +7,7 @@ import (
7 7
8 "github.com/bnema/dumber/internal/domain/entity"8 "github.com/bnema/dumber/internal/domain/entity"
9 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
10- "github.com/jwijenbergh/puregotk/v4/gtk"10+ "github.com/bnema/puregotk/v4/gtk"
11 )11 )
12 12
13 const (13 const (
M internal/ui/input/keyboard.go +3 -3
@@ -25,9 +25,9 @@ import (
2525
2626 "github.com/bnema/dumber/internal/domain/entity"
2727 "github.com/bnema/dumber/internal/logging"
28- "github.com/jwijenbergh/puregotk/v4/gdk"
29- "github.com/jwijenbergh/puregotk/v4/glib"
30- "github.com/jwijenbergh/puregotk/v4/gtk"
28+ "github.com/bnema/puregotk/v4/gdk"
29+ "github.com/bnema/puregotk/v4/glib"
30+ "github.com/bnema/puregotk/v4/gtk"
3131 "github.com/rs/zerolog"
3232 )
3333
@@ -25,9 +25,9 @@ import (
25 25
26 "github.com/bnema/dumber/internal/domain/entity"26 "github.com/bnema/dumber/internal/domain/entity"
27 "github.com/bnema/dumber/internal/logging"27 "github.com/bnema/dumber/internal/logging"
28- "github.com/jwijenbergh/puregotk/v4/gdk"28+ "github.com/bnema/puregotk/v4/gdk"
29- "github.com/jwijenbergh/puregotk/v4/glib"29+ "github.com/bnema/puregotk/v4/glib"
30- "github.com/jwijenbergh/puregotk/v4/gtk"30+ "github.com/bnema/puregotk/v4/gtk"
31 "github.com/rs/zerolog"31 "github.com/rs/zerolog"
32 )32 )
33 33
M internal/ui/input/keyboard_test.go +1 -1
@@ -5,7 +5,7 @@ import (
55 "testing"
66
77 "github.com/bnema/dumber/internal/domain/entity"
8- "github.com/jwijenbergh/puregotk/v4/gdk"
8+ "github.com/bnema/puregotk/v4/gdk"
99 "github.com/stretchr/testify/assert"
1010 )
1111
@@ -5,7 +5,7 @@ import (
5 "testing"5 "testing"
6 6
7 "github.com/bnema/dumber/internal/domain/entity"7 "github.com/bnema/dumber/internal/domain/entity"
8- "github.com/jwijenbergh/puregotk/v4/gdk"8+ "github.com/bnema/puregotk/v4/gdk"
9 "github.com/stretchr/testify/assert"9 "github.com/stretchr/testify/assert"
10 )10 )
11 11
M internal/ui/input/shortcuts.go +1 -1
@@ -8,7 +8,7 @@ import (
88
99 "github.com/bnema/dumber/internal/domain/entity"
1010 "github.com/bnema/dumber/internal/logging"
11- "github.com/jwijenbergh/puregotk/v4/gdk"
11+ "github.com/bnema/puregotk/v4/gdk"
1212 )
1313
1414 // Modifier represents keyboard modifier flags.
@@ -8,7 +8,7 @@ import (
8 8
9 "github.com/bnema/dumber/internal/domain/entity"9 "github.com/bnema/dumber/internal/domain/entity"
10 "github.com/bnema/dumber/internal/logging"10 "github.com/bnema/dumber/internal/logging"
11- "github.com/jwijenbergh/puregotk/v4/gdk"11+ "github.com/bnema/puregotk/v4/gdk"
12 )12 )
13 13
14 // Modifier represents keyboard modifier flags.14 // Modifier represents keyboard modifier flags.
M internal/ui/input/shortcuts_test.go +1 -1
@@ -3,7 +3,7 @@ package input
33 import (
44 "testing"
55
6- "github.com/jwijenbergh/puregotk/v4/gdk"
6+ "github.com/bnema/puregotk/v4/gdk"
77 )
88
99 func TestParseKeyString_SingleKeys(t *testing.T) {
@@ -3,7 +3,7 @@ package input
3 import (3 import (
4 "testing"4 "testing"
5 5
6- "github.com/jwijenbergh/puregotk/v4/gdk"6+ "github.com/bnema/puregotk/v4/gdk"
7 )7 )
8 8
9 func TestParseKeyString_SingleKeys(t *testing.T) {9 func TestParseKeyString_SingleKeys(t *testing.T) {
M internal/ui/layout/gtk_widgets.go +5 -5
@@ -1,11 +1,11 @@
11 package layout
22
33 import (
4- "github.com/jwijenbergh/puregotk/v4/gdk"
5- "github.com/jwijenbergh/puregotk/v4/gobject"
6- "github.com/jwijenbergh/puregotk/v4/graphene"
7- "github.com/jwijenbergh/puregotk/v4/gtk"
8- "github.com/jwijenbergh/puregotk/v4/pango"
4+ "github.com/bnema/puregotk/v4/gdk"
5+ "github.com/bnema/puregotk/v4/gobject"
6+ "github.com/bnema/puregotk/v4/graphene"
7+ "github.com/bnema/puregotk/v4/gtk"
8+ "github.com/bnema/puregotk/v4/pango"
99 )
1010
1111 // Ensure implementations satisfy interfaces at compile time.
@@ -1,11 +1,11 @@
1 package layout1 package layout
2 2
3 import (3 import (
4- "github.com/jwijenbergh/puregotk/v4/gdk"4+ "github.com/bnema/puregotk/v4/gdk"
5- "github.com/jwijenbergh/puregotk/v4/gobject"5+ "github.com/bnema/puregotk/v4/gobject"
6- "github.com/jwijenbergh/puregotk/v4/graphene"6+ "github.com/bnema/puregotk/v4/graphene"
7- "github.com/jwijenbergh/puregotk/v4/gtk"7+ "github.com/bnema/puregotk/v4/gtk"
8- "github.com/jwijenbergh/puregotk/v4/pango"8+ "github.com/bnema/puregotk/v4/pango"
9 )9 )
10 10
11 // Ensure implementations satisfy interfaces at compile time.11 // Ensure implementations satisfy interfaces at compile time.
M internal/ui/layout/mocks/mocks.go +1 -1
@@ -7,7 +7,7 @@ package mocks
77 import (
88 "github.com/bnema/dumber/internal/domain/entity"
99 "github.com/bnema/dumber/internal/ui/layout"
10- "github.com/jwijenbergh/puregotk/v4/gtk"
10+ "github.com/bnema/puregotk/v4/gtk"
1111 mock "github.com/stretchr/testify/mock"
1212 )
1313
@@ -7,7 +7,7 @@ package mocks
7 import (7 import (
8 "github.com/bnema/dumber/internal/domain/entity"8 "github.com/bnema/dumber/internal/domain/entity"
9 "github.com/bnema/dumber/internal/ui/layout"9 "github.com/bnema/dumber/internal/ui/layout"
10- "github.com/jwijenbergh/puregotk/v4/gtk"10+ "github.com/bnema/puregotk/v4/gtk"
11 mock "github.com/stretchr/testify/mock"11 mock "github.com/stretchr/testify/mock"
12 )12 )
13 13
M internal/ui/layout/split.go +1 -1
@@ -6,7 +6,7 @@ import (
66 "time"
77
88 "github.com/bnema/dumber/internal/logging"
9- "github.com/jwijenbergh/puregotk/v4/glib"
9+ "github.com/bnema/puregotk/v4/glib"
1010 "github.com/rs/zerolog"
1111 )
1212
@@ -6,7 +6,7 @@ import (
6 "time"6 "time"
7 7
8 "github.com/bnema/dumber/internal/logging"8 "github.com/bnema/dumber/internal/logging"
9- "github.com/jwijenbergh/puregotk/v4/glib"9+ "github.com/bnema/puregotk/v4/glib"
10 "github.com/rs/zerolog"10 "github.com/rs/zerolog"
11 )11 )
12 12
M internal/ui/layout/stacked.go +2 -2
@@ -6,8 +6,8 @@ import (
66 "sync"
77
88 "github.com/bnema/dumber/internal/logging"
9- "github.com/jwijenbergh/puregotk/v4/gobject"
10- "github.com/jwijenbergh/puregotk/v4/gtk"
9+ "github.com/bnema/puregotk/v4/gobject"
10+ "github.com/bnema/puregotk/v4/gtk"
1111 )
1212
1313 // ErrStackEmpty is returned when operating on an empty stack.
@@ -6,8 +6,8 @@ import (
6 "sync"6 "sync"
7 7
8 "github.com/bnema/dumber/internal/logging"8 "github.com/bnema/dumber/internal/logging"
9- "github.com/jwijenbergh/puregotk/v4/gobject"9+ "github.com/bnema/puregotk/v4/gobject"
10- "github.com/jwijenbergh/puregotk/v4/gtk"10+ "github.com/bnema/puregotk/v4/gtk"
11 )11 )
12 12
13 // ErrStackEmpty is returned when operating on an empty stack.13 // ErrStackEmpty is returned when operating on an empty stack.
M internal/ui/layout/widgets.go +1 -1
@@ -3,7 +3,7 @@
33 package layout
44
55 import (
6- "github.com/jwijenbergh/puregotk/v4/gtk"
6+ "github.com/bnema/puregotk/v4/gtk"
77 )
88
99 // Orientation represents the orientation for layout widgets.
@@ -3,7 +3,7 @@
3 package layout3 package layout
4 4
5 import (5 import (
6- "github.com/jwijenbergh/puregotk/v4/gtk"6+ "github.com/bnema/puregotk/v4/gtk"
7 )7 )
8 8
9 // Orientation represents the orientation for layout widgets.9 // Orientation represents the orientation for layout widgets.
M internal/ui/theme/manager.go +2 -2
@@ -7,8 +7,8 @@ import (
77 "github.com/bnema/dumber/internal/application/port"
88 "github.com/bnema/dumber/internal/domain/entity"
99 "github.com/bnema/dumber/internal/logging"
10- "github.com/jwijenbergh/puregotk/v4/gdk"
11- "github.com/jwijenbergh/puregotk/v4/gtk"
10+ "github.com/bnema/puregotk/v4/gdk"
11+ "github.com/bnema/puregotk/v4/gtk"
1212 )
1313
1414 // Manager handles theme state and CSS application.
@@ -7,8 +7,8 @@ import (
7 "github.com/bnema/dumber/internal/application/port"7 "github.com/bnema/dumber/internal/application/port"
8 "github.com/bnema/dumber/internal/domain/entity"8 "github.com/bnema/dumber/internal/domain/entity"
9 "github.com/bnema/dumber/internal/logging"9 "github.com/bnema/dumber/internal/logging"
10- "github.com/jwijenbergh/puregotk/v4/gdk"10+ "github.com/bnema/puregotk/v4/gdk"
11- "github.com/jwijenbergh/puregotk/v4/gtk"11+ "github.com/bnema/puregotk/v4/gtk"
12 )12 )
13 13
14 // Manager handles theme state and CSS application.14 // Manager handles theme state and CSS application.
M internal/ui/window/main_window.go +1 -1
@@ -6,7 +6,7 @@ import (
66
77 "github.com/bnema/dumber/internal/logging"
88 "github.com/bnema/dumber/internal/ui/component"
9- "github.com/jwijenbergh/puregotk/v4/gtk"
9+ "github.com/bnema/puregotk/v4/gtk"
1010 "github.com/rs/zerolog"
1111 )
1212
@@ -6,7 +6,7 @@ import (
6 6
7 "github.com/bnema/dumber/internal/logging"7 "github.com/bnema/dumber/internal/logging"
8 "github.com/bnema/dumber/internal/ui/component"8 "github.com/bnema/dumber/internal/ui/component"
9- "github.com/jwijenbergh/puregotk/v4/gtk"9+ "github.com/bnema/puregotk/v4/gtk"
10 "github.com/rs/zerolog"10 "github.com/rs/zerolog"
11 )11 )
12 12

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