refactor(internal): update browser controls and filtering for svelte integration
- commit
- d70b2b0be66e5ecc5d42c9f36507c8c55669a350
- author
- bamen · · 2025-09-15 05:21 UTC
- parent
- 5e9cbe6
- signature
- unsigned
5 files · +83 -29
M internal/app/browser/shortcuts.go +12 -5
| @@ -29,15 +29,22 @@ func (s *ShortcutHandler) RegisterShortcuts() { | |||
| 29 | 29 | _ = s.webView.ShowDevTools() | |
| 30 | 30 | }) | |
| 31 | 31 | ||
| 32 | - | // Omnibox (Ctrl+L): rely on injected script listener | |
| 32 | + | // Omnibox (Ctrl+L): use new keyboard service bridge | |
| 33 | 33 | _ = s.webView.RegisterKeyboardShortcut("cmdorctrl+l", func() { | |
| 34 | - | log.Printf("Shortcut: Omnibox toggle") | |
| 35 | - | _ = s.webView.InjectScript("window.__dumber_toggle && window.__dumber_toggle()") | |
| 34 | + | log.Printf("Shortcut: Omnibox toggle via keyboard service") | |
| 35 | + | _ = s.webView.InjectScript("window.__dumber_keyboard && window.__dumber_keyboard.handleNativeShortcut('cmdorctrl+l')") | |
| 36 | 36 | }) | |
| 37 | 37 | ||
| 38 | - | // Copy URL (Ctrl+Shift+C) | |
| 38 | + | // Find in page (Ctrl+F): use keyboard service bridge | |
| 39 | + | _ = s.webView.RegisterKeyboardShortcut("cmdorctrl+f", func() { | |
| 40 | + | log.Printf("Shortcut: Find in page via keyboard service") | |
| 41 | + | _ = s.webView.InjectScript("window.__dumber_keyboard && window.__dumber_keyboard.handleNativeShortcut('cmdorctrl+f')") | |
| 42 | + | }) | |
| 43 | + | ||
| 44 | + | // Copy URL (Ctrl+Shift+C): use keyboard service with native fallback | |
| 39 | 45 | _ = s.webView.RegisterKeyboardShortcut("cmdorctrl+shift+c", func() { | |
| 40 | - | s.clipboardController.CopyCurrentURL() | |
| 46 | + | log.Printf("Shortcut: Copy URL via keyboard service") | |
| 47 | + | _ = s.webView.InjectScript("window.__dumber_keyboard && window.__dumber_keyboard.handleNativeShortcut('cmdorctrl+shift+c')") | |
| 41 | 48 | }) | |
| 42 | 49 | ||
| 43 | 50 | // Page refresh shortcuts | |
| @@ -29,15 +29,22 @@ func (s *ShortcutHandler) RegisterShortcuts() { | |||||
| 29 | _ = s.webView.ShowDevTools() | 29 | _ = s.webView.ShowDevTools() | ||
| 30 | }) | 30 | }) | ||
| 31 | 31 | ||||
| 32 | - | // Omnibox (Ctrl+L): rely on injected script listener | 32 | + | // Omnibox (Ctrl+L): use new keyboard service bridge |
| 33 | _ = s.webView.RegisterKeyboardShortcut("cmdorctrl+l", func() { | 33 | _ = s.webView.RegisterKeyboardShortcut("cmdorctrl+l", func() { | ||
| 34 | - | log.Printf("Shortcut: Omnibox toggle") | 34 | + | log.Printf("Shortcut: Omnibox toggle via keyboard service") |
| 35 | - | _ = s.webView.InjectScript("window.__dumber_toggle && window.__dumber_toggle()") | 35 | + | _ = s.webView.InjectScript("window.__dumber_keyboard && window.__dumber_keyboard.handleNativeShortcut('cmdorctrl+l')") |
| 36 | }) | 36 | }) | ||
| 37 | 37 | ||||
| 38 | - | // Copy URL (Ctrl+Shift+C) | 38 | + | // Find in page (Ctrl+F): use keyboard service bridge |
| 39 | + | _ = s.webView.RegisterKeyboardShortcut("cmdorctrl+f", func() { | |||
| 40 | + | log.Printf("Shortcut: Find in page via keyboard service") | |||
| 41 | + | _ = s.webView.InjectScript("window.__dumber_keyboard && window.__dumber_keyboard.handleNativeShortcut('cmdorctrl+f')") | |||
| 42 | + | }) | |||
| 43 | + | ||||
| 44 | + | // Copy URL (Ctrl+Shift+C): use keyboard service with native fallback | |||
| 39 | _ = s.webView.RegisterKeyboardShortcut("cmdorctrl+shift+c", func() { | 45 | _ = s.webView.RegisterKeyboardShortcut("cmdorctrl+shift+c", func() { | ||
| 40 | - | s.clipboardController.CopyCurrentURL() | 46 | + | log.Printf("Shortcut: Copy URL via keyboard service") |
| 47 | + | _ = s.webView.InjectScript("window.__dumber_keyboard && window.__dumber_keyboard.handleNativeShortcut('cmdorctrl+shift+c')") | |||
| 41 | }) | 48 | }) | ||
| 42 | 49 | ||||
| 43 | // Page refresh shortcuts | 50 | // Page refresh shortcuts | ||
M internal/app/browser/webview.go +12 -1
| @@ -54,6 +54,7 @@ func (app *BrowserApp) createWebView() error { | |||
| 54 | 54 | _ = os.MkdirAll(webkitCache, constants.DirPerm) | |
| 55 | 55 | ||
| 56 | 56 | view, err := webkit.NewWebView(&webkit.Config{ | |
| 57 | + | Assets: app.assets, | |
| 57 | 58 | InitialURL: "dumb://homepage", | |
| 58 | 59 | ZoomDefault: 1.0, | |
| 59 | 60 | EnableDeveloperExtras: true, | |
| @@ -107,6 +108,10 @@ func (app *BrowserApp) createWebView() error { | |||
| 107 | 108 | // setupWebViewIntegration connects the WebView to browser services | |
| 108 | 109 | func (app *BrowserApp) setupWebViewIntegration() { | |
| 109 | 110 | app.browserService.AttachWebView(app.webView) | |
| 111 | + | ||
| 112 | + | // GUI bundle is loaded via WebKit user scripts in enableUserContentManager | |
| 113 | + | // No need to load separately in browser service | |
| 114 | + | ||
| 110 | 115 | // Use native window as title updater | |
| 111 | 116 | if win := app.webView.Window(); win != nil { | |
| 112 | 117 | app.browserService.SetWindowTitleUpdater(win) | |
| @@ -129,6 +134,7 @@ func (app *BrowserApp) setupWebViewHandlers() { | |||
| 129 | 134 | // Set WebView reference for message handler and register script messages | |
| 130 | 135 | app.messageHandler.SetWebView(app.webView) | |
| 131 | 136 | app.webView.RegisterScriptMessageHandler(app.messageHandler.Handle) | |
| 137 | + | ||
| 132 | 138 | } | |
| 133 | 139 | ||
| 134 | 140 | // setupControllers initializes and configures controller objects | |
| @@ -212,15 +218,20 @@ func (app *BrowserApp) setupContentBlocking() error { | |||
| 212 | 218 | // Continue without content blocking rather than failing | |
| 213 | 219 | } | |
| 214 | 220 | ||
| 215 | - | // Register navigation handler for domain-specific filtering | |
| 221 | + | // Register navigation handler for domain-specific filtering and GUI injection | |
| 216 | 222 | // Only register after content blocking is initialized | |
| 217 | 223 | app.webView.RegisterURIChangedHandler(func(uri string) { | |
| 218 | 224 | // Add small delay to avoid conflicts with page load | |
| 219 | 225 | go func() { | |
| 220 | 226 | time.Sleep(200 * time.Millisecond) // Slightly longer delay for stability | |
| 227 | + | ||
| 228 | + | // Apply content filtering if available | |
| 221 | 229 | if app.filterManager != nil { | |
| 222 | 230 | app.webView.OnNavigate(uri, app.filterManager) | |
| 223 | 231 | } | |
| 232 | + | ||
| 233 | + | // Note: GUI bundle (controls and toast) is now injected as User Script | |
| 234 | + | // in WebKit's enableUserContentManager, so it persists across all navigations | |
| 224 | 235 | }() | |
| 225 | 236 | }) | |
| 226 | 237 | }() | |
| @@ -54,6 +54,7 @@ func (app *BrowserApp) createWebView() error { | |||||
| 54 | _ = os.MkdirAll(webkitCache, constants.DirPerm) | 54 | _ = os.MkdirAll(webkitCache, constants.DirPerm) | ||
| 55 | 55 | ||||
| 56 | view, err := webkit.NewWebView(&webkit.Config{ | 56 | view, err := webkit.NewWebView(&webkit.Config{ | ||
| 57 | + | Assets: app.assets, | |||
| 57 | InitialURL: "dumb://homepage", | 58 | InitialURL: "dumb://homepage", | ||
| 58 | ZoomDefault: 1.0, | 59 | ZoomDefault: 1.0, | ||
| 59 | EnableDeveloperExtras: true, | 60 | EnableDeveloperExtras: true, | ||
| @@ -107,6 +108,10 @@ func (app *BrowserApp) createWebView() error { | |||||
| 107 | // setupWebViewIntegration connects the WebView to browser services | 108 | // setupWebViewIntegration connects the WebView to browser services | ||
| 108 | func (app *BrowserApp) setupWebViewIntegration() { | 109 | func (app *BrowserApp) setupWebViewIntegration() { | ||
| 109 | app.browserService.AttachWebView(app.webView) | 110 | app.browserService.AttachWebView(app.webView) | ||
| 111 | + | ||||
| 112 | + | // GUI bundle is loaded via WebKit user scripts in enableUserContentManager | |||
| 113 | + | // No need to load separately in browser service | |||
| 114 | + | ||||
| 110 | // Use native window as title updater | 115 | // Use native window as title updater | ||
| 111 | if win := app.webView.Window(); win != nil { | 116 | if win := app.webView.Window(); win != nil { | ||
| 112 | app.browserService.SetWindowTitleUpdater(win) | 117 | app.browserService.SetWindowTitleUpdater(win) | ||
| @@ -129,6 +134,7 @@ func (app *BrowserApp) setupWebViewHandlers() { | |||||
| 129 | // Set WebView reference for message handler and register script messages | 134 | // Set WebView reference for message handler and register script messages | ||
| 130 | app.messageHandler.SetWebView(app.webView) | 135 | app.messageHandler.SetWebView(app.webView) | ||
| 131 | app.webView.RegisterScriptMessageHandler(app.messageHandler.Handle) | 136 | app.webView.RegisterScriptMessageHandler(app.messageHandler.Handle) | ||
| 137 | + | ||||
| 132 | } | 138 | } | ||
| 133 | 139 | ||||
| 134 | // setupControllers initializes and configures controller objects | 140 | // setupControllers initializes and configures controller objects | ||
| @@ -212,15 +218,20 @@ func (app *BrowserApp) setupContentBlocking() error { | |||||
| 212 | // Continue without content blocking rather than failing | 218 | // Continue without content blocking rather than failing | ||
| 213 | } | 219 | } | ||
| 214 | 220 | ||||
| 215 | - | // Register navigation handler for domain-specific filtering | 221 | + | // Register navigation handler for domain-specific filtering and GUI injection |
| 216 | // Only register after content blocking is initialized | 222 | // Only register after content blocking is initialized | ||
| 217 | app.webView.RegisterURIChangedHandler(func(uri string) { | 223 | app.webView.RegisterURIChangedHandler(func(uri string) { | ||
| 218 | // Add small delay to avoid conflicts with page load | 224 | // Add small delay to avoid conflicts with page load | ||
| 219 | go func() { | 225 | go func() { | ||
| 220 | time.Sleep(200 * time.Millisecond) // Slightly longer delay for stability | 226 | time.Sleep(200 * time.Millisecond) // Slightly longer delay for stability | ||
| 227 | + | ||||
| 228 | + | // Apply content filtering if available | |||
| 221 | if app.filterManager != nil { | 229 | if app.filterManager != nil { | ||
| 222 | app.webView.OnNavigate(uri, app.filterManager) | 230 | app.webView.OnNavigate(uri, app.filterManager) | ||
| 223 | } | 231 | } | ||
| 232 | + | ||||
| 233 | + | // Note: GUI bundle (controls and toast) is now injected as User Script | |||
| 234 | + | // in WebKit's enableUserContentManager, so it persists across all navigations | |||
| 224 | }() | 235 | }() | ||
| 225 | }) | 236 | }) | ||
| 226 | }() | 237 | }() | ||
M internal/app/control/zoom.go +35 -9
| @@ -86,19 +86,45 @@ func (z *ZoomController) handleZoomChange(level float64) { | |||
| 86 | 86 | } | |
| 87 | 87 | } | |
| 88 | 88 | ||
| 89 | - | // showZoomToast displays a zoom level notification | |
| 89 | + | // showZoomToast displays a zoom level notification using TypeScript toast system | |
| 90 | 90 | func (z *ZoomController) showZoomToast(level float64) { | |
| 91 | - | js := fmt.Sprintf(`try { | |
| 92 | - | if (typeof window.__dumber_showZoomToast === 'function') { | |
| 93 | - | window.__dumber_showZoomToast(%f); | |
| 94 | - | } else { | |
| 95 | - | console.warn('[dumber] Zoom toast function not available yet'); | |
| 91 | + | log.Printf("[zoom] Attempting to show zoom toast for level %.2f", level) | |
| 92 | + | ||
| 93 | + | percentage := int(level * 100) | |
| 94 | + | ||
| 95 | + | // Enhanced approach that ensures toast always appears | |
| 96 | + | js := fmt.Sprintf(`(function() { | |
| 97 | + | try { | |
| 98 | + | console.log('[dumber] Attempting to show zoom toast...'); | |
| 99 | + | const zoomLevel = %f; | |
| 100 | + | const percentage = %d; | |
| 101 | + | let toastShown = false; | |
| 102 | + | ||
| 103 | + | // Strategy 1: Try the Svelte toast system first | |
| 104 | + | if (typeof window.__dumber_showZoomToast === 'function') { | |
| 105 | + | console.log('[dumber] Using Svelte zoom toast system'); | |
| 106 | + | window.__dumber_showZoomToast(zoomLevel); | |
| 107 | + | toastShown = true; | |
| 108 | + | } else if (typeof window.__dumber_showToast === 'function') { | |
| 109 | + | console.log('[dumber] Using Svelte general toast system'); | |
| 110 | + | window.__dumber_showToast('Zoom: ' + percentage + '%%', 1500, 'info'); | |
| 111 | + | toastShown = true; | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | ||
| 115 | + | if (!toastShown) { | |
| 116 | + | console.warn('[dumber] Failed to show zoom toast - no DOM body available'); | |
| 117 | + | } | |
| 118 | + | ||
| 119 | + | } catch(e) { | |
| 120 | + | console.error('[dumber] Zoom toast error:', e); | |
| 96 | 121 | } | |
| 97 | - | } catch(e) { | |
| 98 | - | console.error('[dumber] Zoom toast error:', e); | |
| 99 | - | }`, level) | |
| 122 | + | })();`, level, percentage) | |
| 123 | + | ||
| 100 | 124 | if err := z.webView.InjectScript(js); err != nil { | |
| 101 | 125 | log.Printf("[zoom] failed to show toast: %v", err) | |
| 126 | + | } else { | |
| 127 | + | log.Printf("[zoom] Toast script injected successfully") | |
| 102 | 128 | } | |
| 103 | 129 | } | |
| 104 | 130 | ||
| @@ -86,19 +86,45 @@ func (z *ZoomController) handleZoomChange(level float64) { | |||||
| 86 | } | 86 | } | ||
| 87 | } | 87 | } | ||
| 88 | 88 | ||||
| 89 | - | // showZoomToast displays a zoom level notification | 89 | + | // showZoomToast displays a zoom level notification using TypeScript toast system |
| 90 | func (z *ZoomController) showZoomToast(level float64) { | 90 | func (z *ZoomController) showZoomToast(level float64) { | ||
| 91 | - | js := fmt.Sprintf(`try { | 91 | + | log.Printf("[zoom] Attempting to show zoom toast for level %.2f", level) |
| 92 | - | if (typeof window.__dumber_showZoomToast === 'function') { | 92 | + | |
| 93 | - | window.__dumber_showZoomToast(%f); | 93 | + | percentage := int(level * 100) |
| 94 | - | } else { | 94 | + | |
| 95 | - | console.warn('[dumber] Zoom toast function not available yet'); | 95 | + | // Enhanced approach that ensures toast always appears |
| 96 | + | js := fmt.Sprintf(`(function() { | |||
| 97 | + | try { | |||
| 98 | + | console.log('[dumber] Attempting to show zoom toast...'); | |||
| 99 | + | const zoomLevel = %f; | |||
| 100 | + | const percentage = %d; | |||
| 101 | + | let toastShown = false; | |||
| 102 | + | ||||
| 103 | + | // Strategy 1: Try the Svelte toast system first | |||
| 104 | + | if (typeof window.__dumber_showZoomToast === 'function') { | |||
| 105 | + | console.log('[dumber] Using Svelte zoom toast system'); | |||
| 106 | + | window.__dumber_showZoomToast(zoomLevel); | |||
| 107 | + | toastShown = true; | |||
| 108 | + | } else if (typeof window.__dumber_showToast === 'function') { | |||
| 109 | + | console.log('[dumber] Using Svelte general toast system'); | |||
| 110 | + | window.__dumber_showToast('Zoom: ' + percentage + '%%', 1500, 'info'); | |||
| 111 | + | toastShown = true; | |||
| 112 | + | } | |||
| 113 | + | ||||
| 114 | + | ||||
| 115 | + | if (!toastShown) { | |||
| 116 | + | console.warn('[dumber] Failed to show zoom toast - no DOM body available'); | |||
| 117 | + | } | |||
| 118 | + | ||||
| 119 | + | } catch(e) { | |||
| 120 | + | console.error('[dumber] Zoom toast error:', e); | |||
| 96 | } | 121 | } | ||
| 97 | - | } catch(e) { | 122 | + | })();`, level, percentage) |
| 98 | - | console.error('[dumber] Zoom toast error:', e); | 123 | + | |
| 99 | - | }`, level) | |||
| 100 | if err := z.webView.InjectScript(js); err != nil { | 124 | if err := z.webView.InjectScript(js); err != nil { | ||
| 101 | log.Printf("[zoom] failed to show toast: %v", err) | 125 | log.Printf("[zoom] failed to show toast: %v", err) | ||
| 126 | + | } else { | |||
| 127 | + | log.Printf("[zoom] Toast script injected successfully") | |||
| 102 | } | 128 | } | ||
| 103 | } | 129 | } | ||
| 104 | 130 | ||||
M internal/filtering/cosmetic/injector.go +12 -4
| @@ -100,9 +100,13 @@ func (ci *CosmeticInjector) GetScriptForDomain(domain string) string { | |||
| 100 | 100 | (function() { | |
| 101 | 101 | if (typeof window.__dumber_cosmetic_init === 'function') { | |
| 102 | 102 | window.__dumber_cosmetic_init(%s); | |
| 103 | - | console.log('[dumber] Cosmetic filtering initialized with %d rules'); | |
| 103 | + | if (window.self === window.top) { | |
| 104 | + | console.log('[dumber] Cosmetic filtering initialized with %d rules'); | |
| 105 | + | } | |
| 104 | 106 | } else { | |
| 105 | - | console.warn('[dumber] Cosmetic filtering not available'); | |
| 107 | + | if (window.self === window.top) { | |
| 108 | + | console.warn('[dumber] Cosmetic filtering not available'); | |
| 109 | + | } | |
| 106 | 110 | } | |
| 107 | 111 | })();`, domain, string(rulesJSON), len(rules)) | |
| 108 | 112 | ||
| @@ -149,7 +153,9 @@ func (ci *CosmeticInjector) GetUpdateScript(newSelectors []string) string { | |||
| 149 | 153 | (function() { | |
| 150 | 154 | if (typeof window.__dumber_cosmetic_update === 'function') { | |
| 151 | 155 | window.__dumber_cosmetic_update(%s); | |
| 152 | - | console.log('[dumber] Updated cosmetic rules with %d new selectors'); | |
| 156 | + | if (window.self === window.top) { | |
| 157 | + | console.log('[dumber] Updated cosmetic rules with %d new selectors'); | |
| 158 | + | } | |
| 153 | 159 | } | |
| 154 | 160 | })();`, string(selectorsJSON), len(newSelectors)) | |
| 155 | 161 | } | |
| @@ -160,7 +166,9 @@ func (ci *CosmeticInjector) GetCleanupScript() string { | |||
| 160 | 166 | (function() { | |
| 161 | 167 | if (typeof window.__dumber_cosmetic_cleanup === 'function') { | |
| 162 | 168 | window.__dumber_cosmetic_cleanup(); | |
| 163 | - | console.log('[dumber] Cosmetic filtering cleaned up'); | |
| 169 | + | if (window.self === window.top) { | |
| 170 | + | console.log('[dumber] Cosmetic filtering cleaned up'); | |
| 171 | + | } | |
| 164 | 172 | } | |
| 165 | 173 | })();` | |
| 166 | 174 | } | |
| @@ -100,9 +100,13 @@ func (ci *CosmeticInjector) GetScriptForDomain(domain string) string { | |||||
| 100 | (function() { | 100 | (function() { | ||
| 101 | if (typeof window.__dumber_cosmetic_init === 'function') { | 101 | if (typeof window.__dumber_cosmetic_init === 'function') { | ||
| 102 | window.__dumber_cosmetic_init(%s); | 102 | window.__dumber_cosmetic_init(%s); | ||
| 103 | - | console.log('[dumber] Cosmetic filtering initialized with %d rules'); | 103 | + | if (window.self === window.top) { |
| 104 | + | console.log('[dumber] Cosmetic filtering initialized with %d rules'); | |||
| 105 | + | } | |||
| 104 | } else { | 106 | } else { | ||
| 105 | - | console.warn('[dumber] Cosmetic filtering not available'); | 107 | + | if (window.self === window.top) { |
| 108 | + | console.warn('[dumber] Cosmetic filtering not available'); | |||
| 109 | + | } | |||
| 106 | } | 110 | } | ||
| 107 | })();`, domain, string(rulesJSON), len(rules)) | 111 | })();`, domain, string(rulesJSON), len(rules)) | ||
| 108 | 112 | ||||
| @@ -149,7 +153,9 @@ func (ci *CosmeticInjector) GetUpdateScript(newSelectors []string) string { | |||||
| 149 | (function() { | 153 | (function() { | ||
| 150 | if (typeof window.__dumber_cosmetic_update === 'function') { | 154 | if (typeof window.__dumber_cosmetic_update === 'function') { | ||
| 151 | window.__dumber_cosmetic_update(%s); | 155 | window.__dumber_cosmetic_update(%s); | ||
| 152 | - | console.log('[dumber] Updated cosmetic rules with %d new selectors'); | 156 | + | if (window.self === window.top) { |
| 157 | + | console.log('[dumber] Updated cosmetic rules with %d new selectors'); | |||
| 158 | + | } | |||
| 153 | } | 159 | } | ||
| 154 | })();`, string(selectorsJSON), len(newSelectors)) | 160 | })();`, string(selectorsJSON), len(newSelectors)) | ||
| 155 | } | 161 | } | ||
| @@ -160,7 +166,9 @@ func (ci *CosmeticInjector) GetCleanupScript() string { | |||||
| 160 | (function() { | 166 | (function() { | ||
| 161 | if (typeof window.__dumber_cosmetic_cleanup === 'function') { | 167 | if (typeof window.__dumber_cosmetic_cleanup === 'function') { | ||
| 162 | window.__dumber_cosmetic_cleanup(); | 168 | window.__dumber_cosmetic_cleanup(); | ||
| 163 | - | console.log('[dumber] Cosmetic filtering cleaned up'); | 169 | + | if (window.self === window.top) { |
| 170 | + | console.log('[dumber] Cosmetic filtering cleaned up'); | |||
| 171 | + | } | |||
| 164 | } | 172 | } | ||
| 165 | })();` | 173 | })();` | ||
| 166 | } | 174 | } | ||
M internal/filtering/cosmetic/injector.js +12 -10
| @@ -182,16 +182,18 @@ const CosmeticFilter = (() => { | |||
| 182 | 182 | antiBreakage.neutralizeAdPromises(); | |
| 183 | 183 | antiBreakage.cleanupLoadingIndicators(); | |
| 184 | 184 | ||
| 185 | - | // Parse and categorize rules | |
| 186 | - | rules.forEach(rule => { | |
| 187 | - | if (rule.includes(':has(') || rule.includes(':not(')) { | |
| 188 | - | selectors.procedural.push(rule); | |
| 189 | - | } else if (rule.domain === window.location.hostname) { | |
| 190 | - | selectors.specific.push(rule.selector); | |
| 191 | - | } else { | |
| 192 | - | selectors.generic.push(rule.selector); | |
| 193 | - | } | |
| 194 | - | }); | |
| 185 | + | // Parse and categorize rules (with null safety) | |
| 186 | + | if (rules && Array.isArray(rules)) { | |
| 187 | + | rules.forEach(rule => { | |
| 188 | + | if (rule.includes(':has(') || rule.includes(':not(')) { | |
| 189 | + | selectors.procedural.push(rule); | |
| 190 | + | } else if (rule.domain === window.location.hostname) { | |
| 191 | + | selectors.specific.push(rule.selector); | |
| 192 | + | } else { | |
| 193 | + | selectors.generic.push(rule.selector); | |
| 194 | + | } | |
| 195 | + | }); | |
| 196 | + | } | |
| 195 | 197 | ||
| 196 | 198 | // Apply initial hiding | |
| 197 | 199 | hideElements([...selectors.generic, ...selectors.specific]); | |
| @@ -182,16 +182,18 @@ const CosmeticFilter = (() => { | |||||
| 182 | antiBreakage.neutralizeAdPromises(); | 182 | antiBreakage.neutralizeAdPromises(); | ||
| 183 | antiBreakage.cleanupLoadingIndicators(); | 183 | antiBreakage.cleanupLoadingIndicators(); | ||
| 184 | 184 | ||||
| 185 | - | // Parse and categorize rules | 185 | + | // Parse and categorize rules (with null safety) |
| 186 | - | rules.forEach(rule => { | 186 | + | if (rules && Array.isArray(rules)) { |
| 187 | - | if (rule.includes(':has(') || rule.includes(':not(')) { | 187 | + | rules.forEach(rule => { |
| 188 | - | selectors.procedural.push(rule); | 188 | + | if (rule.includes(':has(') || rule.includes(':not(')) { |
| 189 | - | } else if (rule.domain === window.location.hostname) { | 189 | + | selectors.procedural.push(rule); |
| 190 | - | selectors.specific.push(rule.selector); | 190 | + | } else if (rule.domain === window.location.hostname) { |
| 191 | - | } else { | 191 | + | selectors.specific.push(rule.selector); |
| 192 | - | selectors.generic.push(rule.selector); | 192 | + | } else { |
| 193 | - | } | 193 | + | selectors.generic.push(rule.selector); |
| 194 | - | }); | 194 | + | } |
| 195 | + | }); | |||
| 196 | + | } | |||
| 195 | 197 | ||||
| 196 | // Apply initial hiding | 198 | // Apply initial hiding | ||
| 197 | hideElements([...selectors.generic, ...selectors.specific]); | 199 | hideElements([...selectors.generic, ...selectors.specific]); | ||