v0.12.0 d035193

content_blocking.go RawHistoryFolder

1package webkit
2
3import (
4	"context"
5	"fmt"
6	"log"
7	"path/filepath"
8
9	webkit "github.com/diamondburned/gotk4-webkitgtk/pkg/webkit/v6"
10	"github.com/diamondburned/gotk4/pkg/gio/v2"
11	"github.com/diamondburned/gotk4/pkg/glib/v2"
12)
13
14// ContentBlockingManager handles WebKit content filtering
15type ContentBlockingManager struct {
16	filterStore *webkit.UserContentFilterStore
17	storagePath string
18}
19
20// NewContentBlockingManager creates a new content blocking manager
21func NewContentBlockingManager(storagePath string) (*ContentBlockingManager, error) {
22	// Create the filter store
23	filterStore := webkit.NewUserContentFilterStore(storagePath)
24	if filterStore == nil {
25		return nil, fmt.Errorf("failed to create UserContentFilterStore")
26	}
27
28	log.Printf("[webkit] Created UserContentFilterStore at: %s", storagePath)
29
30	return &ContentBlockingManager{
31		filterStore: filterStore,
32		storagePath: storagePath,
33	}, nil
34}
35
36// ApplyFiltersFromJSON compiles and applies WebKit JSON filter rules to a UserContentManager
37// The jsonRules parameter should be a JSON array of WebKit content blocker rules
38func (cbm *ContentBlockingManager) ApplyFiltersFromJSON(ucm *webkit.UserContentManager, identifier string, jsonRules []byte) error {
39	if ucm == nil {
40		return fmt.Errorf("UserContentManager is nil")
41	}
42
43	if len(jsonRules) == 0 {
44		return fmt.Errorf("empty filter rules")
45	}
46
47	log.Printf("[webkit] Applying content filters (identifier: %s, rules size: %d bytes)", identifier, len(jsonRules))
48
49	// Convert []byte to *glib.Bytes for WebKit API
50	// NewBytesWithGo keeps the Go slice alive for the GBytes lifetime
51	gBytes := glib.NewBytesWithGo(jsonRules)
52
53	// Save filters to store asynchronously
54	// This compiles the JSON rules into WebKit's internal format
55	ctx := context.Background()
56
57	// Use a channel to wait for the async operation
58	done := make(chan error, 1)
59
60	cbm.filterStore.Save(ctx, identifier, gBytes, func(result gio.AsyncResulter) {
61		// This callback runs when compilation is complete
62		filter, err := cbm.filterStore.SaveFinish(result)
63		if err != nil {
64			log.Printf("[webkit] Failed to compile content filter: %v", err)
65			done <- fmt.Errorf("failed to compile filter: %w", err)
66			return
67		}
68
69		if filter == nil {
70			log.Printf("[webkit] Filter compilation returned nil")
71			done <- fmt.Errorf("filter compilation returned nil")
72			return
73		}
74
75		// Add the compiled filter to the UserContentManager
76		// This must be done on the main GTK thread
77		RunOnMainThread(func() {
78			ucm.AddFilter(filter)
79			log.Printf("[webkit] Successfully added content filter: %s", identifier)
80			done <- nil
81		})
82	})
83
84	// Wait for completion
85	return <-done
86}
87
88// RemoveAllFilters removes all content filters from a UserContentManager
89func (cbm *ContentBlockingManager) RemoveAllFilters(ucm *webkit.UserContentManager) {
90	if ucm != nil {
91		ucm.RemoveAllFilters()
92		log.Printf("[webkit] Removed all content filters")
93	}
94}
95
96// GetFilterStore returns the underlying UserContentFilterStore
97func (cbm *ContentBlockingManager) GetFilterStore() *webkit.UserContentFilterStore {
98	return cbm.filterStore
99}
100
101// ApplyFiltersToWebView applies content blocking filters to a WebView
102func ApplyFiltersToWebView(view *WebView, filterJSON []byte) error {
103	if view == nil || view.view == nil {
104		return fmt.Errorf("invalid WebView")
105	}
106
107	// Get the UserContentManager
108	ucm := view.view.UserContentManager()
109	if ucm == nil {
110		return fmt.Errorf("UserContentManager is nil")
111	}
112
113	// Create content blocking manager
114	// Use a subdirectory of the cache dir for filter storage
115	dataDir := view.config.DataDir
116	if dataDir == "" {
117		return fmt.Errorf("WebView has no data directory configured")
118	}
119
120	filterStorePath := filepath.Join(dataDir, "content-filters")
121	cbm, err := NewContentBlockingManager(filterStorePath)
122	if err != nil {
123		return fmt.Errorf("failed to create content blocking manager: %w", err)
124	}
125
126	// Apply the filters
127	return cbm.ApplyFiltersFromJSON(ucm, "adblock-filters", filterJSON)
128}

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