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}