refactor(config): consolidate engine migration metadata
- commit
- 89e934d3c61261570396e663d08e6a3f273baa7d
- author
- brice · · 2026-06-25 12:02 UTC
- parent
- f4ac668
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +416 -228
M docs/config/index.md +101 -101
| @@ -248,38 +248,67 @@ When Noctalia rewrites the watched file, Dumber reapplies the same resolved them | |||
| 248 | 248 | ||
| 249 | 249 | ## Rendering, UI Scale & Zoom | |
| 250 | 250 | ||
| 251 | + | CEF is the default browser engine. WebKitGTK remains available as a fallback via `engine.type = "webkit"`; `engine.webkit.*` settings only affect that fallback engine. | |
| 252 | + | ||
| 251 | 253 | | Key | Type | Default | Valid Values | Description | | |
| 252 | 254 | |-----|------|---------|--------------|-------------| | |
| 253 | - | | `rendering.mode` | string | `"gpu"` | `auto`, `gpu`, `cpu` | WebKit hardware acceleration policy | | |
| 254 | - | | `rendering.disable_dmabuf_renderer` | bool | `false` | - | Disable WebKit DMA-BUF renderer (may fix flicker on Wayland; slower) | | |
| 255 | - | | `rendering.force_compositing_mode` | bool | `false` | - | Force WebKit compositing mode (`WEBKIT_FORCE_COMPOSITING_MODE`) | | |
| 256 | - | | `rendering.disable_compositing_mode` | bool | `false` | - | Disable WebKit compositing mode (`WEBKIT_DISABLE_COMPOSITING_MODE`) | | |
| 257 | - | | `rendering.gsk_renderer` | string | `"auto"` | `auto`, `opengl`, `vulkan`, `cairo` | GTK renderer selection (`GSK_RENDERER`) | | |
| 258 | - | | `rendering.disable_mipmaps` | bool | `false` | - | Disable GTK mipmaps (`GSK_GPU_DISABLE=mipmap`) | | |
| 259 | - | | `rendering.prefer_gl` | bool | `false` | - | Prefer OpenGL over GLES (`GDK_DEBUG=gl-prefer-gl`) | | |
| 260 | - | | `rendering.draw_compositing_indicators` | bool | `false` | - | Draw WebKit compositing indicators (debug) | | |
| 261 | - | | `rendering.show_fps` | bool | `false` | - | Show WebKit FPS counter (`WEBKIT_SHOW_FPS`) | | |
| 262 | - | | `rendering.sample_memory` | bool | `false` | - | Enable WebKit memory sampling (`WEBKIT_SAMPLE_MEMORY`) | | |
| 263 | - | | `rendering.debug_frames` | bool | `false` | - | Enable GTK frame timing debug (`GDK_DEBUG=frames`) | | |
| 264 | - | | `engine.type` | string | `"cef"` | `cef`, `webkit` | Browser engine. WebKit remains available as a fallback. | | |
| 255 | + | | `engine.type` | string | `"cef"` | `cef`, `webkit` | Browser engine selection; WebKitGTK is the fallback option | | |
| 265 | 256 | | `engine.cef.render_stack` | string | `"vulkan"` | `vulkan`, `egl` | CEF GPU render stack | | |
| 266 | - | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Enable adaptive CEF OSR FPS polling (only applies when `windowless_frame_rate = 0`) | | |
| 267 | - | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default | | |
| 268 | - | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | Hard cap for adaptive CEF OSR FPS; 0 falls back to the built-in default cap selected by Dumber/CEF | | |
| 269 | - | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | > 0 | Mouse wheel scroll sensitivity multiplier | | |
| 270 | - | | `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | > 0 | Precise/surface scroll sensitivity multiplier for touchpads and high-resolution wheels | | |
| 271 | - | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | > 0 | Horizontal scroll sensitivity multiplier | | |
| 272 | - | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | > 0 | Vertical scroll sensitivity multiplier; touchpad vertical speed uses this together with `scroll_precise_multiplier` | | |
| 273 | - | | `engine.cef.input.scroll_max_delta` | int32 | `0` | >= 0 | Maximum absolute scroll delta after scaling; 0 disables clamping | | |
| 257 | + | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Enable adaptive CEF OSR FPS polling when `windowless_frame_rate = 0` | | |
| 258 | + | | `engine.cef.windowless_frame_rate` | int32 | `0` | `>= 0` | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled | | |
| 259 | + | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | `>= 0` | Hard cap for adaptive CEF OSR FPS; 0 uses the built-in cap | | |
| 260 | + | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | `> 0` | Mouse wheel scroll sensitivity multiplier | | |
| 261 | + | | `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | `> 0` | Precise/surface scroll sensitivity multiplier for touchpads and high-resolution wheels | | |
| 262 | + | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | `> 0` | Horizontal scroll sensitivity multiplier | | |
| 263 | + | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | `> 0` | Vertical scroll sensitivity multiplier; combines with `scroll_precise_multiplier` for touchpads | | |
| 264 | + | | `engine.cef.input.scroll_max_delta` | int32 | `0` | `>= 0` | Maximum absolute scroll delta after scaling; 0 disables clamping | | |
| 274 | 265 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | - | Enable two-finger touchpad swipe back/forward navigation | | |
| 275 | - | | `engine.cef.input.touchpad_navigation_min_delta` | float | `200.0` | > 0 | Minimum accumulated horizontal swipe delta required for navigation | | |
| 276 | - | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | Maximum vertical-to-horizontal delta ratio allowed for navigation swipes; does not affect vertical scroll speed | | |
| 277 | - | | `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) | | |
| 266 | + | | `engine.cef.input.touchpad_navigation_min_delta` | float | `200.0` | `> 0` | Minimum accumulated horizontal swipe delta required for navigation | | |
| 267 | + | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | `> 0` | Maximum vertical-to-horizontal delta ratio allowed for navigation swipes | | |
| 268 | + | | `engine.webkit.gsk_renderer` | string | `"auto"` | `auto`, `opengl`, `vulkan`, `cairo` | WebKit fallback GTK renderer selection (`GSK_RENDERER`) | | |
| 269 | + | | `engine.webkit.disable_dmabuf_renderer` | bool | `false` | - | Disable WebKit fallback DMA-BUF renderer | | |
| 270 | + | | `engine.webkit.force_compositing_mode` | bool | `false` | - | Force WebKit fallback compositing mode (`WEBKIT_FORCE_COMPOSITING_MODE`) | | |
| 271 | + | | `engine.webkit.disable_compositing_mode` | bool | `false` | - | Disable WebKit fallback compositing mode (`WEBKIT_DISABLE_COMPOSITING_MODE`) | | |
| 272 | + | | `engine.webkit.disable_mipmaps` | bool | `false` | - | Disable GTK mipmaps for the WebKit fallback (`GSK_GPU_DISABLE=mipmap`) | | |
| 273 | + | | `engine.webkit.prefer_gl` | bool | `false` | - | Prefer OpenGL over GLES for the WebKit fallback (`GDK_DEBUG=gl-prefer-gl`) | | |
| 274 | + | | `engine.webkit.draw_compositing_indicators` | bool | `false` | - | Draw WebKit fallback compositing indicators (debug) | | |
| 275 | + | | `engine.webkit.show_fps` | bool | `false` | - | Show WebKit fallback FPS counter (`WEBKIT_SHOW_FPS`) | | |
| 276 | + | | `engine.webkit.sample_memory` | bool | `false` | - | Enable WebKit fallback memory sampling (`WEBKIT_SAMPLE_MEMORY`) | | |
| 277 | + | | `engine.webkit.debug_frames` | bool | `false` | - | Enable GTK frame timing debug for the WebKit fallback (`GDK_DEBUG=frames`) | | |
| 278 | + | | `engine.webkit.force_vsync` | bool | `false` | - | Force VSync for WebKit fallback video playback | | |
| 279 | + | | `engine.webkit.gl_rendering_mode` | string | `"auto"` | `auto`, `gles2`, `gl3`, `none` | WebKit fallback GStreamer/OpenGL API selection | | |
| 280 | + | | `engine.webkit.gstreamer_debug_level` | int | `0` | `0-5` | WebKit fallback GStreamer debug verbosity | | |
| 281 | + | | `default_ui_scale` | float | `1.0` | `> 0` | GTK widget UI scale (1.0=100%, 2.0=200%) | | |
| 282 | + | | `default_webpage_zoom` | float | `1.2` | `> 0` | Default page zoom (1.0=100%, 1.2=120%) | | |
| 278 | 283 | ||
| 279 | 284 | `engine.cef.input.scroll_precise_multiplier` controls touchpad/high-resolution wheel scroll speed, and `engine.cef.input.scroll_vertical_multiplier` applies an additional vertical-only scale. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | |
| 280 | 285 | ||
| 281 | 286 | `engine.cef.input.touchpad_navigation_min_delta` uses raw GTK touchpad surface units for back/forward gestures. The default `200.0` matches WebKit-style commit distance to reduce accidental navigation; raise or lower it in `config.toml` to tune gesture sensitivity. | |
| 282 | - | | `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) | | |
| 287 | + | ||
| 288 | + | ### Legacy key migration | |
| 289 | + | ||
| 290 | + | Existing configs using older keys are migrated to the current engine shape. New configs should use the canonical keys above. | |
| 291 | + | ||
| 292 | + | | Legacy key | Current key | | |
| 293 | + | |------------|-------------| | |
| 294 | + | | `privacy.cookie_policy` | `engine.cookie_policy` | | |
| 295 | + | | `privacy.itp_enabled` | `engine.webkit.itp_enabled` | | |
| 296 | + | | `rendering.disable_dmabuf_renderer` | `engine.webkit.disable_dmabuf_renderer` | | |
| 297 | + | | `rendering.force_compositing_mode` | `engine.webkit.force_compositing_mode` | | |
| 298 | + | | `rendering.disable_compositing_mode` | `engine.webkit.disable_compositing_mode` | | |
| 299 | + | | `rendering.gsk_renderer` | `engine.webkit.gsk_renderer` | | |
| 300 | + | | `rendering.disable_mipmaps` | `engine.webkit.disable_mipmaps` | | |
| 301 | + | | `rendering.prefer_gl` | `engine.webkit.prefer_gl` | | |
| 302 | + | | `rendering.draw_compositing_indicators` | `engine.webkit.draw_compositing_indicators` | | |
| 303 | + | | `rendering.show_fps` | `engine.webkit.show_fps` | | |
| 304 | + | | `rendering.sample_memory` | `engine.webkit.sample_memory` | | |
| 305 | + | | `rendering.debug_frames` | `engine.webkit.debug_frames` | | |
| 306 | + | | `media.force_vsync` | `engine.webkit.force_vsync` | | |
| 307 | + | | `media.gl_rendering_mode` | `engine.webkit.gl_rendering_mode` | | |
| 308 | + | | `media.gstreamer_debug_level` | `engine.webkit.gstreamer_debug_level` | | |
| 309 | + | | `runtime.prefix` | `engine.webkit.prefix` | | |
| 310 | + | | `performance.*` | `engine.profile`, `engine.pool_prewarm_count`, `engine.zoom_cache_size`, or matching `engine.webkit.*` custom tuning keys | | |
| 311 | + | | `rendering.mode` | Dropped; no current setting | | |
| 283 | 312 | ||
| 284 | 313 | ## Workspace Configuration | |
| 285 | 314 | ||
| @@ -548,21 +577,14 @@ max_exited_session_age_days = 7 # Delete sessions older than 7 days on startup | |||
| 548 | 577 | | `media.hardware_decoding` | string | `"auto"` | `auto`, `force`, `disable` | Hardware video decoding mode | | |
| 549 | 578 | | `media.prefer_av1` | bool | `false` | - | Prefer AV1 codec when available | | |
| 550 | 579 | | `media.show_diagnostics` | bool | `false` | - | Show media diagnostics warnings at startup | | |
| 551 | - | | `media.force_vsync` | bool | `false` | - | Force VSync for video playback (may help with tearing) | | |
| 552 | - | | `media.gl_rendering_mode` | string | `"auto"` | `auto`, `gles2`, `gl3`, `none` | OpenGL API selection for video rendering | | |
| 553 | - | | `media.gstreamer_debug_level` | int | `0` | `0-5` | GStreamer debug verbosity (0=off) | | |
| 580 | + | ||
| 581 | + | WebKit fallback GStreamer tuning is configured under `engine.webkit.force_vsync`, `engine.webkit.gl_rendering_mode`, and `engine.webkit.gstreamer_debug_level`. | |
| 554 | 582 | ||
| 555 | 583 | **Hardware decoding modes:** | |
| 556 | 584 | - `auto` (recommended): Hardware preferred with software fallback - fixes Twitch Error #4000 | |
| 557 | 585 | - `force`: Hardware only - fails if unavailable | |
| 558 | 586 | - `disable`: Software only - higher CPU usage | |
| 559 | 587 | ||
| 560 | - | **GL rendering modes:** | |
| 561 | - | - `auto` (default): Let GStreamer choose the best OpenGL API | |
| 562 | - | - `gles2`: Force GLES2 (better for some mobile/embedded GPUs) | |
| 563 | - | - `gl3`: Force OpenGL 3.x desktop | |
| 564 | - | - `none`: Disable GL-based rendering | |
| 565 | - | ||
| 566 | 588 | **GPU auto-detection:** | |
| 567 | 589 | Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal VA-API driver settings: | |
| 568 | 590 | - **AMD**: Uses `radeonsi` driver | |
| @@ -575,21 +597,15 @@ Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal | |||
| 575 | 597 | hardware_decoding = "auto" # HW preferred, SW fallback | |
| 576 | 598 | prefer_av1 = false # Let site choose codec | |
| 577 | 599 | show_diagnostics = false # Keep off for daily use | |
| 578 | - | force_vsync = false # Let compositor handle VSync | |
| 579 | - | gl_rendering_mode = "auto" # GStreamer picks best GL API | |
| 580 | - | gstreamer_debug_level = 0 # Increase to 3-5 for debugging | |
| 581 | 600 | ``` | |
| 582 | 601 | ||
| 583 | - | **Diagnostic mode toggle path:** | |
| 584 | - | ||
| 602 | + | **WebKit fallback GStreamer diagnostics:** | |
| 585 | 603 | ```toml | |
| 586 | - | # ~/.config/dumber/config.toml | |
| 587 | - | [media] | |
| 588 | - | show_diagnostics = true | |
| 604 | + | [engine.webkit] | |
| 589 | 605 | gstreamer_debug_level = 3 | |
| 590 | 606 | ``` | |
| 591 | 607 | ||
| 592 | - | Set `show_diagnostics = false` and `gstreamer_debug_level = 0` to return to normal mode. | |
| 608 | + | Set `media.show_diagnostics = false` and `engine.webkit.gstreamer_debug_level = 0` to return to normal mode. | |
| 593 | 609 | ||
| 594 | 610 | **Diagnostics CLI:** | |
| 595 | 611 | ```bash | |
| @@ -601,9 +617,10 @@ dumber doctor --media | |||
| 601 | 617 | ||
| 602 | 618 | | Key | Type | Default | Description | | |
| 603 | 619 | |-----|------|---------|-------------| | |
| 604 | - | | `runtime.prefix` | string | `""` | Optional runtime prefix (e.g., `/opt/webkitgtk`) used to locate newer GTK/WebKitGTK installs | | |
| 620 | + | | `engine.cef.cef_dir` | string | `""` | Optional CEF runtime directory containing `libcef.so` and resources | | |
| 621 | + | | `engine.webkit.prefix` | string | `""` | Optional WebKitGTK fallback runtime prefix (e.g., `/opt/webkitgtk`) | | |
| 605 | 622 | ||
| 606 | - | If set, dumber prepends prefix-derived paths to: | |
| 623 | + | `engine.cef.cef_dir` overrides CEF runtime discovery. For the WebKitGTK fallback, `engine.webkit.prefix` prepends prefix-derived paths to: | |
| 607 | 624 | - `PKG_CONFIG_PATH` (for version checks) | |
| 608 | 625 | - `LD_LIBRARY_PATH` (for runtime library loading) | |
| 609 | 626 | - `GI_TYPELIB_PATH` (GObject introspection) | |
| @@ -611,7 +628,10 @@ If set, dumber prepends prefix-derived paths to: | |||
| 611 | 628 | ||
| 612 | 629 | **Example:** | |
| 613 | 630 | ```toml | |
| 614 | - | [runtime] | |
| 631 | + | [engine.cef] | |
| 632 | + | cef_dir = "/opt/cef" | |
| 633 | + | ||
| 634 | + | [engine.webkit] | |
| 615 | 635 | prefix = "/opt/webkitgtk" | |
| 616 | 636 | ``` | |
| 617 | 637 | ||
| @@ -670,89 +690,69 @@ dumber config migrate --yes | |||
| 670 | 690 | ||
| 671 | 691 | ## Performance Profiles | |
| 672 | 692 | ||
| 673 | - | Performance profiles provide preset configurations for WebKitGTK tuning. These settings affect Skia rendering threads, memory pressure handling, and WebView pool behavior. | |
| 693 | + | Performance profiles tune engine resource behavior. CEF is the default engine; the `engine.webkit.*` custom fields below apply only when using the WebKitGTK fallback. | |
| 674 | 694 | ||
| 675 | 695 | > **Note:** Performance settings are applied at browser startup. Changes require a restart to take effect. | |
| 676 | 696 | ||
| 677 | 697 | | Key | Type | Default | Valid Values | Description | | |
| 678 | 698 | |-----|------|---------|--------------|-------------| | |
| 679 | - | | `performance.profile` | string | `"default"` | `default`, `lite`, `max`, `custom` | Performance profile selection | | |
| 699 | + | | `engine.profile` | string | `"default"` | `default`, `lite`, `balanced`, `max`, `custom` | Performance profile selection | | |
| 700 | + | | `engine.pool_prewarm_count` | int | `4` | `>= 0` | WebViews to pre-create at startup | | |
| 701 | + | | `engine.zoom_cache_size` | int | `256` | `>= 0` | Domain zoom levels to cache | | |
| 680 | 702 | ||
| 681 | 703 | ### Profiles | |
| 682 | 704 | ||
| 683 | 705 | | Profile | Description | Use Case | | |
| 684 | 706 | |---------|-------------|----------| | |
| 685 | - | | `default` | No tuning, uses WebKit defaults | Compatibility baseline, troubleshooting | | |
| 707 | + | | `default` | No tuning, uses engine defaults | Compatibility baseline, troubleshooting | | |
| 686 | 708 | | `lite` | Reduced resource usage | Low-RAM systems (< 4GB), battery saving | | |
| 709 | + | | `balanced` | Moderate tuning | General use | | |
| 687 | 710 | | `max` | Maximum responsiveness | Heavy pages (GitHub PRs, complex SPAs), high-end systems | | |
| 688 | 711 | | `custom` | Manual control over all settings | Advanced users who want fine-grained control | | |
| 689 | 712 | ||
| 690 | - | ### Profile Settings Matrix | |
| 691 | - | ||
| 692 | - | | Setting | default | lite | max | | |
| 693 | - | |---------|---------|------|-----| | |
| 694 | - | | Skia CPU threads | unset | 2 | `NumCPU()/2` (min 4) | | |
| 695 | - | | Skia GPU threads | unset | unset | scales with VRAM | | |
| 696 | - | | Web process memory (MB) | unset | 768 | unset | | |
| 697 | - | | Network process memory (MB) | unset | 384 | unset | | |
| 698 | - | | Conservative threshold | unset | 0.25 | unset | | |
| 699 | - | | Strict threshold | unset | 0.4 | unset | | |
| 700 | - | | WebView pool prewarm | 4 | 2 | scales with RAM | | |
| 701 | - | ||
| 702 | 713 | **Example:** | |
| 703 | 714 | ```toml | |
| 704 | - | [performance] | |
| 705 | - | profile = "default" # WebKit baseline default | |
| 706 | - | ||
| 707 | - | # For WebKit baseline behavior: | |
| 708 | - | # profile = "default" | |
| 709 | - | ||
| 710 | - | # For heavy pages (GitHub PRs, complex web apps): | |
| 711 | - | # profile = "max" | |
| 712 | - | ||
| 713 | - | # For manual control: | |
| 714 | - | # profile = "custom" | |
| 715 | + | [engine] | |
| 716 | + | profile = "default" | |
| 717 | + | pool_prewarm_count = 4 | |
| 718 | + | zoom_cache_size = 256 | |
| 715 | 719 | ``` | |
| 716 | 720 | ||
| 717 | - | ### Custom Profile Settings | |
| 721 | + | ### WebKit fallback custom profile settings | |
| 718 | 722 | ||
| 719 | - | When `profile = "custom"`, you can configure individual tuning options: | |
| 723 | + | When `engine.profile = "custom"` and `engine.type = "webkit"`, you can configure individual WebKit tuning options: | |
| 720 | 724 | ||
| 721 | 725 | | Key | Type | Default | Description | | |
| 722 | 726 | |-----|------|---------|-------------| | |
| 723 | - | | `performance.skia_cpu_painting_threads` | int | `0` | Skia CPU rendering threads (0=unset) | | |
| 724 | - | | `performance.skia_gpu_painting_threads` | int | `-1` | Skia GPU rendering threads (-1=unset, 0=disable) | | |
| 725 | - | | `performance.skia_enable_cpu_rendering` | bool | `false` | Force CPU rendering | | |
| 726 | - | | `performance.web_process_memory_limit_mb` | int | `0` | Web process memory limit in MB (0=unset) | | |
| 727 | - | | `performance.web_process_memory_poll_interval_sec` | float | `0` | Memory check interval (0=WebKit default: 30s) | | |
| 728 | - | | `performance.web_process_memory_conservative_threshold` | float | `0` | Conservative cleanup threshold (0=unset) | | |
| 729 | - | | `performance.web_process_memory_strict_threshold` | float | `0` | Strict cleanup threshold (0=unset) | | |
| 730 | - | | `performance.network_process_memory_limit_mb` | int | `0` | Network process memory limit in MB | | |
| 731 | - | | `performance.network_process_memory_poll_interval_sec` | float | `0` | Network memory check interval | | |
| 732 | - | | `performance.network_process_memory_conservative_threshold` | float | `0` | Network conservative threshold | | |
| 733 | - | | `performance.network_process_memory_strict_threshold` | float | `0` | Network strict threshold | | |
| 734 | - | | `performance.webview_pool_prewarm_count` | int | `4` | WebViews to pre-create at startup | | |
| 735 | - | | `performance.zoom_cache_size` | int | `256` | Domain zoom levels to cache | | |
| 736 | - | ||
| 737 | - | **Custom profile example:** | |
| 727 | + | | `engine.webkit.skia_cpu_painting_threads` | int | `0` | Skia CPU rendering threads (0=unset) | | |
| 728 | + | | `engine.webkit.skia_gpu_painting_threads` | int | `-1` | Skia GPU rendering threads (-1=unset, 0=disable) | | |
| 729 | + | | `engine.webkit.skia_enable_cpu_rendering` | bool | `false` | Force CPU rendering | | |
| 730 | + | | `engine.webkit.web_process_memory_limit_mb` | int | `0` | Web process memory limit in MB (0=unset) | | |
| 731 | + | | `engine.webkit.web_process_memory_poll_interval_sec` | float | `0` | Memory check interval (0=WebKit default: 30s) | | |
| 732 | + | | `engine.webkit.web_process_memory_conservative_threshold` | float | `0` | Conservative cleanup threshold (0=unset) | | |
| 733 | + | | `engine.webkit.web_process_memory_strict_threshold` | float | `0` | Strict cleanup threshold (0=unset) | | |
| 734 | + | | `engine.webkit.network_process_memory_limit_mb` | int | `0` | Network process memory limit in MB | | |
| 735 | + | | `engine.webkit.network_process_memory_poll_interval_sec` | float | `0` | Network memory check interval | | |
| 736 | + | | `engine.webkit.network_process_memory_conservative_threshold` | float | `0` | Network conservative threshold | | |
| 737 | + | | `engine.webkit.network_process_memory_strict_threshold` | float | `0` | Network strict threshold | | |
| 738 | + | ||
| 739 | + | **Custom WebKit fallback example:** | |
| 738 | 740 | ```toml | |
| 739 | - | [performance] | |
| 741 | + | [engine] | |
| 742 | + | type = "webkit" | |
| 740 | 743 | profile = "custom" | |
| 744 | + | pool_prewarm_count = 6 | |
| 745 | + | zoom_cache_size = 256 | |
| 741 | 746 | ||
| 742 | - | # Skia threading - tune for your CPU | |
| 747 | + | [engine.webkit] | |
| 743 | 748 | skia_cpu_painting_threads = 4 | |
| 744 | 749 | skia_gpu_painting_threads = 2 | |
| 745 | - | ||
| 746 | - | # Web process memory pressure | |
| 747 | 750 | web_process_memory_limit_mb = 1024 | |
| 748 | 751 | web_process_memory_conservative_threshold = 0.4 | |
| 749 | 752 | web_process_memory_strict_threshold = 0.6 | |
| 750 | - | ||
| 751 | - | # WebView pool | |
| 752 | - | webview_pool_prewarm_count = 6 | |
| 753 | 753 | ``` | |
| 754 | 754 | ||
| 755 | - | > **Important:** Individual tuning fields are ignored unless `profile = "custom"`. Setting individual fields with any other profile will produce a validation warning. | |
| 755 | + | > **Important:** Individual `engine.webkit.*` tuning fields are ignored unless `engine.profile = "custom"`. | |
| 756 | 756 | ||
| 757 | 757 | ## Downloads | |
| 758 | 758 | ||
| @@ -779,14 +779,14 @@ All config values can be overridden via environment variables with the prefix `D | |||
| 779 | 779 | # Database | |
| 780 | 780 | DUMBER_DATABASE_PATH=/custom/path/db.sqlite | |
| 781 | 781 | ||
| 782 | - | # Rendering (examples) | |
| 782 | + | # Engine/rendering (examples) | |
| 783 | + | DUMBER_ENGINE_TYPE=cef | |
| 783 | 784 | # CEF defaults to the GPU-first Vulkan DMABUF stack. | |
| 784 | 785 | # Use EGL/OpenGL for driver compatibility: | |
| 785 | 786 | # DUMBER_ENGINE_CEF_RENDER_STACK=egl | |
| 786 | - | DUMBER_RENDERING_MODE=cpu | |
| 787 | - | # WebKit-specific rendering overrides: | |
| 788 | - | # DUMBER_RENDERING_DISABLE_DMABUF_RENDERER=true | |
| 789 | - | # DUMBER_RENDERING_GSK_RENDERER=opengl | |
| 787 | + | # WebKit fallback rendering overrides: | |
| 788 | + | # DUMBER_ENGINE_WEBKIT_DISABLE_DMABUF_RENDERER=true | |
| 789 | + | # DUMBER_ENGINE_WEBKIT_GSK_RENDERER=opengl | |
| 790 | 790 | DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5 | |
| 791 | 791 | ||
| 792 | 792 | # Logging | |
| @@ -248,38 +248,67 @@ When Noctalia rewrites the watched file, Dumber reapplies the same resolved them | |||||
| 248 | 248 | ||||
| 249 | ## Rendering, UI Scale & Zoom | 249 | ## Rendering, UI Scale & Zoom | ||
| 250 | 250 | ||||
| 251 | + | CEF is the default browser engine. WebKitGTK remains available as a fallback via `engine.type = "webkit"`; `engine.webkit.*` settings only affect that fallback engine. | |||
| 252 | + | ||||
| 251 | | Key | Type | Default | Valid Values | Description | | 253 | | Key | Type | Default | Valid Values | Description | | ||
| 252 | |-----|------|---------|--------------|-------------| | 254 | |-----|------|---------|--------------|-------------| | ||
| 253 | - | | `rendering.mode` | string | `"gpu"` | `auto`, `gpu`, `cpu` | WebKit hardware acceleration policy | | 255 | + | | `engine.type` | string | `"cef"` | `cef`, `webkit` | Browser engine selection; WebKitGTK is the fallback option | |
| 254 | - | | `rendering.disable_dmabuf_renderer` | bool | `false` | - | Disable WebKit DMA-BUF renderer (may fix flicker on Wayland; slower) | | |||
| 255 | - | | `rendering.force_compositing_mode` | bool | `false` | - | Force WebKit compositing mode (`WEBKIT_FORCE_COMPOSITING_MODE`) | | |||
| 256 | - | | `rendering.disable_compositing_mode` | bool | `false` | - | Disable WebKit compositing mode (`WEBKIT_DISABLE_COMPOSITING_MODE`) | | |||
| 257 | - | | `rendering.gsk_renderer` | string | `"auto"` | `auto`, `opengl`, `vulkan`, `cairo` | GTK renderer selection (`GSK_RENDERER`) | | |||
| 258 | - | | `rendering.disable_mipmaps` | bool | `false` | - | Disable GTK mipmaps (`GSK_GPU_DISABLE=mipmap`) | | |||
| 259 | - | | `rendering.prefer_gl` | bool | `false` | - | Prefer OpenGL over GLES (`GDK_DEBUG=gl-prefer-gl`) | | |||
| 260 | - | | `rendering.draw_compositing_indicators` | bool | `false` | - | Draw WebKit compositing indicators (debug) | | |||
| 261 | - | | `rendering.show_fps` | bool | `false` | - | Show WebKit FPS counter (`WEBKIT_SHOW_FPS`) | | |||
| 262 | - | | `rendering.sample_memory` | bool | `false` | - | Enable WebKit memory sampling (`WEBKIT_SAMPLE_MEMORY`) | | |||
| 263 | - | | `rendering.debug_frames` | bool | `false` | - | Enable GTK frame timing debug (`GDK_DEBUG=frames`) | | |||
| 264 | - | | `engine.type` | string | `"cef"` | `cef`, `webkit` | Browser engine. WebKit remains available as a fallback. | | |||
| 265 | | `engine.cef.render_stack` | string | `"vulkan"` | `vulkan`, `egl` | CEF GPU render stack | | 256 | | `engine.cef.render_stack` | string | `"vulkan"` | `vulkan`, `egl` | CEF GPU render stack | | ||
| 266 | - | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Enable adaptive CEF OSR FPS polling (only applies when `windowless_frame_rate = 0`) | | 257 | + | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | - | Enable adaptive CEF OSR FPS polling when `windowless_frame_rate = 0` | |
| 267 | - | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled, otherwise CEF default | | 258 | + | | `engine.cef.windowless_frame_rate` | int32 | `0` | `>= 0` | Explicit CEF OSR FPS cap; 0 uses adaptive mode if enabled | |
| 268 | - | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | >= 0 | Hard cap for adaptive CEF OSR FPS; 0 falls back to the built-in default cap selected by Dumber/CEF | | 259 | + | | `engine.cef.windowless_frame_rate_max` | int32 | `240` | `>= 0` | Hard cap for adaptive CEF OSR FPS; 0 uses the built-in cap | |
| 269 | - | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | > 0 | Mouse wheel scroll sensitivity multiplier | | 260 | + | | `engine.cef.input.scroll_wheel_multiplier` | float | `1.0` | `> 0` | Mouse wheel scroll sensitivity multiplier | |
| 270 | - | | `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | > 0 | Precise/surface scroll sensitivity multiplier for touchpads and high-resolution wheels | | 261 | + | | `engine.cef.input.scroll_precise_multiplier` | float | `2.5` | `> 0` | Precise/surface scroll sensitivity multiplier for touchpads and high-resolution wheels | |
| 271 | - | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | > 0 | Horizontal scroll sensitivity multiplier | | 262 | + | | `engine.cef.input.scroll_horizontal_multiplier` | float | `1.0` | `> 0` | Horizontal scroll sensitivity multiplier | |
| 272 | - | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | > 0 | Vertical scroll sensitivity multiplier; touchpad vertical speed uses this together with `scroll_precise_multiplier` | | 263 | + | | `engine.cef.input.scroll_vertical_multiplier` | float | `1.0` | `> 0` | Vertical scroll sensitivity multiplier; combines with `scroll_precise_multiplier` for touchpads | |
| 273 | - | | `engine.cef.input.scroll_max_delta` | int32 | `0` | >= 0 | Maximum absolute scroll delta after scaling; 0 disables clamping | | 264 | + | | `engine.cef.input.scroll_max_delta` | int32 | `0` | `>= 0` | Maximum absolute scroll delta after scaling; 0 disables clamping | |
| 274 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | - | Enable two-finger touchpad swipe back/forward navigation | | 265 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | - | Enable two-finger touchpad swipe back/forward navigation | | ||
| 275 | - | | `engine.cef.input.touchpad_navigation_min_delta` | float | `200.0` | > 0 | Minimum accumulated horizontal swipe delta required for navigation | | 266 | + | | `engine.cef.input.touchpad_navigation_min_delta` | float | `200.0` | `> 0` | Minimum accumulated horizontal swipe delta required for navigation | |
| 276 | - | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | Maximum vertical-to-horizontal delta ratio allowed for navigation swipes; does not affect vertical scroll speed | | 267 | + | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | `> 0` | Maximum vertical-to-horizontal delta ratio allowed for navigation swipes | |
| 277 | - | | `default_ui_scale` | float | `1.0` | > 0 | GTK widget UI scale (1.0=100%, 2.0=200%) | | 268 | + | | `engine.webkit.gsk_renderer` | string | `"auto"` | `auto`, `opengl`, `vulkan`, `cairo` | WebKit fallback GTK renderer selection (`GSK_RENDERER`) | |
| 269 | + | | `engine.webkit.disable_dmabuf_renderer` | bool | `false` | - | Disable WebKit fallback DMA-BUF renderer | | |||
| 270 | + | | `engine.webkit.force_compositing_mode` | bool | `false` | - | Force WebKit fallback compositing mode (`WEBKIT_FORCE_COMPOSITING_MODE`) | | |||
| 271 | + | | `engine.webkit.disable_compositing_mode` | bool | `false` | - | Disable WebKit fallback compositing mode (`WEBKIT_DISABLE_COMPOSITING_MODE`) | | |||
| 272 | + | | `engine.webkit.disable_mipmaps` | bool | `false` | - | Disable GTK mipmaps for the WebKit fallback (`GSK_GPU_DISABLE=mipmap`) | | |||
| 273 | + | | `engine.webkit.prefer_gl` | bool | `false` | - | Prefer OpenGL over GLES for the WebKit fallback (`GDK_DEBUG=gl-prefer-gl`) | | |||
| 274 | + | | `engine.webkit.draw_compositing_indicators` | bool | `false` | - | Draw WebKit fallback compositing indicators (debug) | | |||
| 275 | + | | `engine.webkit.show_fps` | bool | `false` | - | Show WebKit fallback FPS counter (`WEBKIT_SHOW_FPS`) | | |||
| 276 | + | | `engine.webkit.sample_memory` | bool | `false` | - | Enable WebKit fallback memory sampling (`WEBKIT_SAMPLE_MEMORY`) | | |||
| 277 | + | | `engine.webkit.debug_frames` | bool | `false` | - | Enable GTK frame timing debug for the WebKit fallback (`GDK_DEBUG=frames`) | | |||
| 278 | + | | `engine.webkit.force_vsync` | bool | `false` | - | Force VSync for WebKit fallback video playback | | |||
| 279 | + | | `engine.webkit.gl_rendering_mode` | string | `"auto"` | `auto`, `gles2`, `gl3`, `none` | WebKit fallback GStreamer/OpenGL API selection | | |||
| 280 | + | | `engine.webkit.gstreamer_debug_level` | int | `0` | `0-5` | WebKit fallback GStreamer debug verbosity | | |||
| 281 | + | | `default_ui_scale` | float | `1.0` | `> 0` | GTK widget UI scale (1.0=100%, 2.0=200%) | | |||
| 282 | + | | `default_webpage_zoom` | float | `1.2` | `> 0` | Default page zoom (1.0=100%, 1.2=120%) | | |||
| 278 | 283 | ||||
| 279 | `engine.cef.input.scroll_precise_multiplier` controls touchpad/high-resolution wheel scroll speed, and `engine.cef.input.scroll_vertical_multiplier` applies an additional vertical-only scale. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | 284 | `engine.cef.input.scroll_precise_multiplier` controls touchpad/high-resolution wheel scroll speed, and `engine.cef.input.scroll_vertical_multiplier` applies an additional vertical-only scale. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | ||
| 280 | 285 | ||||
| 281 | `engine.cef.input.touchpad_navigation_min_delta` uses raw GTK touchpad surface units for back/forward gestures. The default `200.0` matches WebKit-style commit distance to reduce accidental navigation; raise or lower it in `config.toml` to tune gesture sensitivity. | 286 | `engine.cef.input.touchpad_navigation_min_delta` uses raw GTK touchpad surface units for back/forward gestures. The default `200.0` matches WebKit-style commit distance to reduce accidental navigation; raise or lower it in `config.toml` to tune gesture sensitivity. | ||
| 282 | - | | `default_webpage_zoom` | float | `1.2` | > 0 | Default page zoom (1.0=100%, 1.2=120%) | | 287 | + | |
| 288 | + | ### Legacy key migration | |||
| 289 | + | ||||
| 290 | + | Existing configs using older keys are migrated to the current engine shape. New configs should use the canonical keys above. | |||
| 291 | + | ||||
| 292 | + | | Legacy key | Current key | | |||
| 293 | + | |------------|-------------| | |||
| 294 | + | | `privacy.cookie_policy` | `engine.cookie_policy` | | |||
| 295 | + | | `privacy.itp_enabled` | `engine.webkit.itp_enabled` | | |||
| 296 | + | | `rendering.disable_dmabuf_renderer` | `engine.webkit.disable_dmabuf_renderer` | | |||
| 297 | + | | `rendering.force_compositing_mode` | `engine.webkit.force_compositing_mode` | | |||
| 298 | + | | `rendering.disable_compositing_mode` | `engine.webkit.disable_compositing_mode` | | |||
| 299 | + | | `rendering.gsk_renderer` | `engine.webkit.gsk_renderer` | | |||
| 300 | + | | `rendering.disable_mipmaps` | `engine.webkit.disable_mipmaps` | | |||
| 301 | + | | `rendering.prefer_gl` | `engine.webkit.prefer_gl` | | |||
| 302 | + | | `rendering.draw_compositing_indicators` | `engine.webkit.draw_compositing_indicators` | | |||
| 303 | + | | `rendering.show_fps` | `engine.webkit.show_fps` | | |||
| 304 | + | | `rendering.sample_memory` | `engine.webkit.sample_memory` | | |||
| 305 | + | | `rendering.debug_frames` | `engine.webkit.debug_frames` | | |||
| 306 | + | | `media.force_vsync` | `engine.webkit.force_vsync` | | |||
| 307 | + | | `media.gl_rendering_mode` | `engine.webkit.gl_rendering_mode` | | |||
| 308 | + | | `media.gstreamer_debug_level` | `engine.webkit.gstreamer_debug_level` | | |||
| 309 | + | | `runtime.prefix` | `engine.webkit.prefix` | | |||
| 310 | + | | `performance.*` | `engine.profile`, `engine.pool_prewarm_count`, `engine.zoom_cache_size`, or matching `engine.webkit.*` custom tuning keys | | |||
| 311 | + | | `rendering.mode` | Dropped; no current setting | | |||
| 283 | 312 | ||||
| 284 | ## Workspace Configuration | 313 | ## Workspace Configuration | ||
| 285 | 314 | ||||
| @@ -548,21 +577,14 @@ max_exited_session_age_days = 7 # Delete sessions older than 7 days on startup | |||||
| 548 | | `media.hardware_decoding` | string | `"auto"` | `auto`, `force`, `disable` | Hardware video decoding mode | | 577 | | `media.hardware_decoding` | string | `"auto"` | `auto`, `force`, `disable` | Hardware video decoding mode | | ||
| 549 | | `media.prefer_av1` | bool | `false` | - | Prefer AV1 codec when available | | 578 | | `media.prefer_av1` | bool | `false` | - | Prefer AV1 codec when available | | ||
| 550 | | `media.show_diagnostics` | bool | `false` | - | Show media diagnostics warnings at startup | | 579 | | `media.show_diagnostics` | bool | `false` | - | Show media diagnostics warnings at startup | | ||
| 551 | - | | `media.force_vsync` | bool | `false` | - | Force VSync for video playback (may help with tearing) | | 580 | + | |
| 552 | - | | `media.gl_rendering_mode` | string | `"auto"` | `auto`, `gles2`, `gl3`, `none` | OpenGL API selection for video rendering | | 581 | + | WebKit fallback GStreamer tuning is configured under `engine.webkit.force_vsync`, `engine.webkit.gl_rendering_mode`, and `engine.webkit.gstreamer_debug_level`. |
| 553 | - | | `media.gstreamer_debug_level` | int | `0` | `0-5` | GStreamer debug verbosity (0=off) | | |||
| 554 | 582 | ||||
| 555 | **Hardware decoding modes:** | 583 | **Hardware decoding modes:** | ||
| 556 | - `auto` (recommended): Hardware preferred with software fallback - fixes Twitch Error #4000 | 584 | - `auto` (recommended): Hardware preferred with software fallback - fixes Twitch Error #4000 | ||
| 557 | - `force`: Hardware only - fails if unavailable | 585 | - `force`: Hardware only - fails if unavailable | ||
| 558 | - `disable`: Software only - higher CPU usage | 586 | - `disable`: Software only - higher CPU usage | ||
| 559 | 587 | ||||
| 560 | - | **GL rendering modes:** | |||
| 561 | - | - `auto` (default): Let GStreamer choose the best OpenGL API | |||
| 562 | - | - `gles2`: Force GLES2 (better for some mobile/embedded GPUs) | |||
| 563 | - | - `gl3`: Force OpenGL 3.x desktop | |||
| 564 | - | - `none`: Disable GL-based rendering | |||
| 565 | - | ||||
| 566 | **GPU auto-detection:** | 588 | **GPU auto-detection:** | ||
| 567 | Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal VA-API driver settings: | 589 | Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal VA-API driver settings: | ||
| 568 | - **AMD**: Uses `radeonsi` driver | 590 | - **AMD**: Uses `radeonsi` driver | ||
| @@ -575,21 +597,15 @@ Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal | |||||
| 575 | hardware_decoding = "auto" # HW preferred, SW fallback | 597 | hardware_decoding = "auto" # HW preferred, SW fallback | ||
| 576 | prefer_av1 = false # Let site choose codec | 598 | prefer_av1 = false # Let site choose codec | ||
| 577 | show_diagnostics = false # Keep off for daily use | 599 | show_diagnostics = false # Keep off for daily use | ||
| 578 | - | force_vsync = false # Let compositor handle VSync | |||
| 579 | - | gl_rendering_mode = "auto" # GStreamer picks best GL API | |||
| 580 | - | gstreamer_debug_level = 0 # Increase to 3-5 for debugging | |||
| 581 | ``` | 600 | ``` | ||
| 582 | 601 | ||||
| 583 | - | **Diagnostic mode toggle path:** | 602 | + | **WebKit fallback GStreamer diagnostics:** |
| 584 | - | ||||
| 585 | ```toml | 603 | ```toml | ||
| 586 | - | # ~/.config/dumber/config.toml | 604 | + | [engine.webkit] |
| 587 | - | [media] | |||
| 588 | - | show_diagnostics = true | |||
| 589 | gstreamer_debug_level = 3 | 605 | gstreamer_debug_level = 3 | ||
| 590 | ``` | 606 | ``` | ||
| 591 | 607 | ||||
| 592 | - | Set `show_diagnostics = false` and `gstreamer_debug_level = 0` to return to normal mode. | 608 | + | Set `media.show_diagnostics = false` and `engine.webkit.gstreamer_debug_level = 0` to return to normal mode. |
| 593 | 609 | ||||
| 594 | **Diagnostics CLI:** | 610 | **Diagnostics CLI:** | ||
| 595 | ```bash | 611 | ```bash | ||
| @@ -601,9 +617,10 @@ dumber doctor --media | |||||
| 601 | 617 | ||||
| 602 | | Key | Type | Default | Description | | 618 | | Key | Type | Default | Description | | ||
| 603 | |-----|------|---------|-------------| | 619 | |-----|------|---------|-------------| | ||
| 604 | - | | `runtime.prefix` | string | `""` | Optional runtime prefix (e.g., `/opt/webkitgtk`) used to locate newer GTK/WebKitGTK installs | | 620 | + | | `engine.cef.cef_dir` | string | `""` | Optional CEF runtime directory containing `libcef.so` and resources | |
| 621 | + | | `engine.webkit.prefix` | string | `""` | Optional WebKitGTK fallback runtime prefix (e.g., `/opt/webkitgtk`) | | |||
| 605 | 622 | ||||
| 606 | - | If set, dumber prepends prefix-derived paths to: | 623 | + | `engine.cef.cef_dir` overrides CEF runtime discovery. For the WebKitGTK fallback, `engine.webkit.prefix` prepends prefix-derived paths to: |
| 607 | - `PKG_CONFIG_PATH` (for version checks) | 624 | - `PKG_CONFIG_PATH` (for version checks) | ||
| 608 | - `LD_LIBRARY_PATH` (for runtime library loading) | 625 | - `LD_LIBRARY_PATH` (for runtime library loading) | ||
| 609 | - `GI_TYPELIB_PATH` (GObject introspection) | 626 | - `GI_TYPELIB_PATH` (GObject introspection) | ||
| @@ -611,7 +628,10 @@ If set, dumber prepends prefix-derived paths to: | |||||
| 611 | 628 | ||||
| 612 | **Example:** | 629 | **Example:** | ||
| 613 | ```toml | 630 | ```toml | ||
| 614 | - | [runtime] | 631 | + | [engine.cef] |
| 632 | + | cef_dir = "/opt/cef" | |||
| 633 | + | ||||
| 634 | + | [engine.webkit] | |||
| 615 | prefix = "/opt/webkitgtk" | 635 | prefix = "/opt/webkitgtk" | ||
| 616 | ``` | 636 | ``` | ||
| 617 | 637 | ||||
| @@ -670,89 +690,69 @@ dumber config migrate --yes | |||||
| 670 | 690 | ||||
| 671 | ## Performance Profiles | 691 | ## Performance Profiles | ||
| 672 | 692 | ||||
| 673 | - | Performance profiles provide preset configurations for WebKitGTK tuning. These settings affect Skia rendering threads, memory pressure handling, and WebView pool behavior. | 693 | + | Performance profiles tune engine resource behavior. CEF is the default engine; the `engine.webkit.*` custom fields below apply only when using the WebKitGTK fallback. |
| 674 | 694 | ||||
| 675 | > **Note:** Performance settings are applied at browser startup. Changes require a restart to take effect. | 695 | > **Note:** Performance settings are applied at browser startup. Changes require a restart to take effect. | ||
| 676 | 696 | ||||
| 677 | | Key | Type | Default | Valid Values | Description | | 697 | | Key | Type | Default | Valid Values | Description | | ||
| 678 | |-----|------|---------|--------------|-------------| | 698 | |-----|------|---------|--------------|-------------| | ||
| 679 | - | | `performance.profile` | string | `"default"` | `default`, `lite`, `max`, `custom` | Performance profile selection | | 699 | + | | `engine.profile` | string | `"default"` | `default`, `lite`, `balanced`, `max`, `custom` | Performance profile selection | |
| 700 | + | | `engine.pool_prewarm_count` | int | `4` | `>= 0` | WebViews to pre-create at startup | | |||
| 701 | + | | `engine.zoom_cache_size` | int | `256` | `>= 0` | Domain zoom levels to cache | | |||
| 680 | 702 | ||||
| 681 | ### Profiles | 703 | ### Profiles | ||
| 682 | 704 | ||||
| 683 | | Profile | Description | Use Case | | 705 | | Profile | Description | Use Case | | ||
| 684 | |---------|-------------|----------| | 706 | |---------|-------------|----------| | ||
| 685 | - | | `default` | No tuning, uses WebKit defaults | Compatibility baseline, troubleshooting | | 707 | + | | `default` | No tuning, uses engine defaults | Compatibility baseline, troubleshooting | |
| 686 | | `lite` | Reduced resource usage | Low-RAM systems (< 4GB), battery saving | | 708 | | `lite` | Reduced resource usage | Low-RAM systems (< 4GB), battery saving | | ||
| 709 | + | | `balanced` | Moderate tuning | General use | | |||
| 687 | | `max` | Maximum responsiveness | Heavy pages (GitHub PRs, complex SPAs), high-end systems | | 710 | | `max` | Maximum responsiveness | Heavy pages (GitHub PRs, complex SPAs), high-end systems | | ||
| 688 | | `custom` | Manual control over all settings | Advanced users who want fine-grained control | | 711 | | `custom` | Manual control over all settings | Advanced users who want fine-grained control | | ||
| 689 | 712 | ||||
| 690 | - | ### Profile Settings Matrix | |||
| 691 | - | ||||
| 692 | - | | Setting | default | lite | max | | |||
| 693 | - | |---------|---------|------|-----| | |||
| 694 | - | | Skia CPU threads | unset | 2 | `NumCPU()/2` (min 4) | | |||
| 695 | - | | Skia GPU threads | unset | unset | scales with VRAM | | |||
| 696 | - | | Web process memory (MB) | unset | 768 | unset | | |||
| 697 | - | | Network process memory (MB) | unset | 384 | unset | | |||
| 698 | - | | Conservative threshold | unset | 0.25 | unset | | |||
| 699 | - | | Strict threshold | unset | 0.4 | unset | | |||
| 700 | - | | WebView pool prewarm | 4 | 2 | scales with RAM | | |||
| 701 | - | ||||
| 702 | **Example:** | 713 | **Example:** | ||
| 703 | ```toml | 714 | ```toml | ||
| 704 | - | [performance] | 715 | + | [engine] |
| 705 | - | profile = "default" # WebKit baseline default | 716 | + | profile = "default" |
| 706 | - | 717 | + | pool_prewarm_count = 4 | |
| 707 | - | # For WebKit baseline behavior: | 718 | + | zoom_cache_size = 256 |
| 708 | - | # profile = "default" | |||
| 709 | - | ||||
| 710 | - | # For heavy pages (GitHub PRs, complex web apps): | |||
| 711 | - | # profile = "max" | |||
| 712 | - | ||||
| 713 | - | # For manual control: | |||
| 714 | - | # profile = "custom" | |||
| 715 | ``` | 719 | ``` | ||
| 716 | 720 | ||||
| 717 | - | ### Custom Profile Settings | 721 | + | ### WebKit fallback custom profile settings |
| 718 | 722 | ||||
| 719 | - | When `profile = "custom"`, you can configure individual tuning options: | 723 | + | When `engine.profile = "custom"` and `engine.type = "webkit"`, you can configure individual WebKit tuning options: |
| 720 | 724 | ||||
| 721 | | Key | Type | Default | Description | | 725 | | Key | Type | Default | Description | | ||
| 722 | |-----|------|---------|-------------| | 726 | |-----|------|---------|-------------| | ||
| 723 | - | | `performance.skia_cpu_painting_threads` | int | `0` | Skia CPU rendering threads (0=unset) | | 727 | + | | `engine.webkit.skia_cpu_painting_threads` | int | `0` | Skia CPU rendering threads (0=unset) | |
| 724 | - | | `performance.skia_gpu_painting_threads` | int | `-1` | Skia GPU rendering threads (-1=unset, 0=disable) | | 728 | + | | `engine.webkit.skia_gpu_painting_threads` | int | `-1` | Skia GPU rendering threads (-1=unset, 0=disable) | |
| 725 | - | | `performance.skia_enable_cpu_rendering` | bool | `false` | Force CPU rendering | | 729 | + | | `engine.webkit.skia_enable_cpu_rendering` | bool | `false` | Force CPU rendering | |
| 726 | - | | `performance.web_process_memory_limit_mb` | int | `0` | Web process memory limit in MB (0=unset) | | 730 | + | | `engine.webkit.web_process_memory_limit_mb` | int | `0` | Web process memory limit in MB (0=unset) | |
| 727 | - | | `performance.web_process_memory_poll_interval_sec` | float | `0` | Memory check interval (0=WebKit default: 30s) | | 731 | + | | `engine.webkit.web_process_memory_poll_interval_sec` | float | `0` | Memory check interval (0=WebKit default: 30s) | |
| 728 | - | | `performance.web_process_memory_conservative_threshold` | float | `0` | Conservative cleanup threshold (0=unset) | | 732 | + | | `engine.webkit.web_process_memory_conservative_threshold` | float | `0` | Conservative cleanup threshold (0=unset) | |
| 729 | - | | `performance.web_process_memory_strict_threshold` | float | `0` | Strict cleanup threshold (0=unset) | | 733 | + | | `engine.webkit.web_process_memory_strict_threshold` | float | `0` | Strict cleanup threshold (0=unset) | |
| 730 | - | | `performance.network_process_memory_limit_mb` | int | `0` | Network process memory limit in MB | | 734 | + | | `engine.webkit.network_process_memory_limit_mb` | int | `0` | Network process memory limit in MB | |
| 731 | - | | `performance.network_process_memory_poll_interval_sec` | float | `0` | Network memory check interval | | 735 | + | | `engine.webkit.network_process_memory_poll_interval_sec` | float | `0` | Network memory check interval | |
| 732 | - | | `performance.network_process_memory_conservative_threshold` | float | `0` | Network conservative threshold | | 736 | + | | `engine.webkit.network_process_memory_conservative_threshold` | float | `0` | Network conservative threshold | |
| 733 | - | | `performance.network_process_memory_strict_threshold` | float | `0` | Network strict threshold | | 737 | + | | `engine.webkit.network_process_memory_strict_threshold` | float | `0` | Network strict threshold | |
| 734 | - | | `performance.webview_pool_prewarm_count` | int | `4` | WebViews to pre-create at startup | | 738 | + | |
| 735 | - | | `performance.zoom_cache_size` | int | `256` | Domain zoom levels to cache | | 739 | + | **Custom WebKit fallback example:** |
| 736 | - | ||||
| 737 | - | **Custom profile example:** | |||
| 738 | ```toml | 740 | ```toml | ||
| 739 | - | [performance] | 741 | + | [engine] |
| 742 | + | type = "webkit" | |||
| 740 | profile = "custom" | 743 | profile = "custom" | ||
| 744 | + | pool_prewarm_count = 6 | |||
| 745 | + | zoom_cache_size = 256 | |||
| 741 | 746 | ||||
| 742 | - | # Skia threading - tune for your CPU | 747 | + | [engine.webkit] |
| 743 | skia_cpu_painting_threads = 4 | 748 | skia_cpu_painting_threads = 4 | ||
| 744 | skia_gpu_painting_threads = 2 | 749 | skia_gpu_painting_threads = 2 | ||
| 745 | - | ||||
| 746 | - | # Web process memory pressure | |||
| 747 | web_process_memory_limit_mb = 1024 | 750 | web_process_memory_limit_mb = 1024 | ||
| 748 | web_process_memory_conservative_threshold = 0.4 | 751 | web_process_memory_conservative_threshold = 0.4 | ||
| 749 | web_process_memory_strict_threshold = 0.6 | 752 | web_process_memory_strict_threshold = 0.6 | ||
| 750 | - | ||||
| 751 | - | # WebView pool | |||
| 752 | - | webview_pool_prewarm_count = 6 | |||
| 753 | ``` | 753 | ``` | ||
| 754 | 754 | ||||
| 755 | - | > **Important:** Individual tuning fields are ignored unless `profile = "custom"`. Setting individual fields with any other profile will produce a validation warning. | 755 | + | > **Important:** Individual `engine.webkit.*` tuning fields are ignored unless `engine.profile = "custom"`. |
| 756 | 756 | ||||
| 757 | ## Downloads | 757 | ## Downloads | ||
| 758 | 758 | ||||
| @@ -779,14 +779,14 @@ All config values can be overridden via environment variables with the prefix `D | |||||
| 779 | # Database | 779 | # Database | ||
| 780 | DUMBER_DATABASE_PATH=/custom/path/db.sqlite | 780 | DUMBER_DATABASE_PATH=/custom/path/db.sqlite | ||
| 781 | 781 | ||||
| 782 | - | # Rendering (examples) | 782 | + | # Engine/rendering (examples) |
| 783 | + | DUMBER_ENGINE_TYPE=cef | |||
| 783 | # CEF defaults to the GPU-first Vulkan DMABUF stack. | 784 | # CEF defaults to the GPU-first Vulkan DMABUF stack. | ||
| 784 | # Use EGL/OpenGL for driver compatibility: | 785 | # Use EGL/OpenGL for driver compatibility: | ||
| 785 | # DUMBER_ENGINE_CEF_RENDER_STACK=egl | 786 | # DUMBER_ENGINE_CEF_RENDER_STACK=egl | ||
| 786 | - | DUMBER_RENDERING_MODE=cpu | 787 | + | # WebKit fallback rendering overrides: |
| 787 | - | # WebKit-specific rendering overrides: | 788 | + | # DUMBER_ENGINE_WEBKIT_DISABLE_DMABUF_RENDERER=true |
| 788 | - | # DUMBER_RENDERING_DISABLE_DMABUF_RENDERER=true | 789 | + | # DUMBER_ENGINE_WEBKIT_GSK_RENDERER=opengl |
| 789 | - | # DUMBER_RENDERING_GSK_RENDERER=opengl | |||
| 790 | DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5 | 790 | DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5 | ||
| 791 | 791 | ||||
| 792 | # Logging | 792 | # Logging | ||
M docs/reference/configuration.md +55 -30
| @@ -52,18 +52,7 @@ | |||
| 52 | 52 | | `appearance.dark_palette.accent` | string | `#a8a8a8` | | | |
| 53 | 53 | | `appearance.dark_palette.border` | string | `#363636` | | | |
| 54 | 54 | | `debug.enable_devtools` | bool | `true` | | | |
| 55 | - | | `rendering.mode` | string | `gpu` | `auto`, `gpu`, `cpu` | | |
| 56 | - | | `rendering.disable_dmabuf_renderer` | bool | `false` | | | |
| 57 | - | | `rendering.force_compositing_mode` | bool | `false` | | | |
| 58 | - | | `rendering.disable_compositing_mode` | bool | `false` | | | |
| 59 | - | | `rendering.gsk_renderer` | string | `auto` | `auto`, `opengl`, `vulkan`, `cairo` | | |
| 60 | - | | `rendering.disable_mipmaps` | bool | `false` | | | |
| 61 | - | | `rendering.prefer_gl` | bool | `false` | | | |
| 62 | - | | `rendering.draw_compositing_indicators` | bool | `false` | | | |
| 63 | - | | `rendering.show_fps` | bool | `false` | | | |
| 64 | - | | `rendering.sample_memory` | bool | `false` | | | |
| 65 | - | | `rendering.debug_frames` | bool | `false` | | | |
| 66 | - | | `engine.type` | string | `cef` | `cef`, `webkit` | | |
| 55 | + | | `engine.type` | string | `cef` | `cef`, `webkit` (CEF default; WebKitGTK fallback) | | |
| 67 | 56 | | `engine.cef.render_stack` | string | `vulkan` | `vulkan`, `egl` | | |
| 68 | 57 | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | | | |
| 69 | 58 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | |
| @@ -76,6 +65,19 @@ | |||
| 76 | 65 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | | | |
| 77 | 66 | | `engine.cef.input.touchpad_navigation_min_delta` | float | `200.0` | > 0 | | |
| 78 | 67 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | | |
| 68 | + | | `engine.webkit.gsk_renderer` | string | `auto` | `auto`, `opengl`, `vulkan`, `cairo` (WebKit fallback only) | | |
| 69 | + | | `engine.webkit.disable_dmabuf_renderer` | bool | `false` | WebKit fallback only | | |
| 70 | + | | `engine.webkit.force_compositing_mode` | bool | `false` | WebKit fallback only | | |
| 71 | + | | `engine.webkit.disable_compositing_mode` | bool | `false` | WebKit fallback only | | |
| 72 | + | | `engine.webkit.disable_mipmaps` | bool | `false` | WebKit fallback only | | |
| 73 | + | | `engine.webkit.prefer_gl` | bool | `false` | WebKit fallback only | | |
| 74 | + | | `engine.webkit.draw_compositing_indicators` | bool | `false` | WebKit fallback only | | |
| 75 | + | | `engine.webkit.show_fps` | bool | `false` | WebKit fallback only | | |
| 76 | + | | `engine.webkit.sample_memory` | bool | `false` | WebKit fallback only | | |
| 77 | + | | `engine.webkit.debug_frames` | bool | `false` | WebKit fallback only | | |
| 78 | + | | `engine.webkit.force_vsync` | bool | `false` | WebKit fallback GStreamer only | | |
| 79 | + | | `engine.webkit.gl_rendering_mode` | string | `auto` | `auto`, `gles2`, `gl3`, `none` (WebKit fallback only) | | |
| 80 | + | | `engine.webkit.gstreamer_debug_level` | int | `0` | `0-5` (WebKit fallback only) | | |
| 79 | 81 | | `default_ui_scale` | float | `1.0` | > 0 | | |
| 80 | 82 | ||
| 81 | 83 | Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise_multiplier` and the additional axis-specific `engine.cef.input.scroll_vertical_multiplier`. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | |
| @@ -124,28 +126,51 @@ Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise | |||
| 124 | 126 | | `media.hardware_decoding` | string | `auto` | `auto`, `force`, `disable` | | |
| 125 | 127 | | `media.prefer_av1` | bool | `false` | | | |
| 126 | 128 | | `media.show_diagnostics` | bool | `false` | | | |
| 127 | - | | `media.force_vsync` | bool | `false` | | | |
| 128 | - | | `media.gl_rendering_mode` | string | `auto` | `auto`, `gles2`, `gl3`, `none` | | |
| 129 | - | | `media.gstreamer_debug_level` | int | `0` | 0-5 | | |
| 130 | - | | `runtime.prefix` | string | `` | | | |
| 129 | + | | `engine.cef.cef_dir` | string | `` | CEF runtime directory | | |
| 130 | + | | `engine.webkit.prefix` | string | `` | WebKitGTK fallback runtime prefix | | |
| 131 | 131 | | `clipboard.auto_copy_on_selection` | bool | `true` | | | |
| 132 | 132 | | `content_filtering.enabled` | bool | `true` | | | |
| 133 | 133 | | `content_filtering.auto_update` | bool | `true` | | | |
| 134 | 134 | | `update.enable_on_startup` | bool | `true` | | | |
| 135 | 135 | | `update.auto_download` | bool | `false` | | | |
| 136 | 136 | | `update.notify_on_new_settings` | bool | `true` | | | |
| 137 | - | | `performance.profile` | string | `default` | `default`, `lite`, `max`, `custom` | | |
| 138 | - | | `performance.skia_cpu_painting_threads` | int | `0` | | | |
| 139 | - | | `performance.skia_gpu_painting_threads` | int | `-1` | | | |
| 140 | - | | `performance.skia_enable_cpu_rendering` | bool | `false` | | | |
| 141 | - | | `performance.web_process_memory_limit_mb` | int | `0` | | | |
| 142 | - | | `performance.web_process_memory_poll_interval_sec` | float | `0` | | | |
| 143 | - | | `performance.web_process_memory_conservative_threshold` | float | `0` | | | |
| 144 | - | | `performance.web_process_memory_strict_threshold` | float | `0` | | | |
| 145 | - | | `performance.network_process_memory_limit_mb` | int | `0` | | | |
| 146 | - | | `performance.network_process_memory_poll_interval_sec` | float | `0` | | | |
| 147 | - | | `performance.network_process_memory_conservative_threshold` | float | `0` | | | |
| 148 | - | | `performance.network_process_memory_strict_threshold` | float | `0` | | | |
| 149 | - | | `performance.webview_pool_prewarm_count` | int | `4` | | | |
| 150 | - | | `performance.zoom_cache_size` | int | `256` | | | |
| 137 | + | | `engine.profile` | string | `default` | `default`, `lite`, `balanced`, `max`, `custom` | | |
| 138 | + | | `engine.webkit.skia_cpu_painting_threads` | int | `0` | >= 0 (WebKit fallback custom profile only) | | |
| 139 | + | | `engine.webkit.skia_gpu_painting_threads` | int | `-1` | >= -1 (WebKit fallback custom profile only) | | |
| 140 | + | | `engine.webkit.skia_enable_cpu_rendering` | bool | `false` | WebKit fallback custom profile only | | |
| 141 | + | | `engine.webkit.web_process_memory_limit_mb` | int | `0` | >= 0 (WebKit fallback custom profile only) | | |
| 142 | + | | `engine.webkit.web_process_memory_poll_interval_sec` | float | `0` | >= 0 (WebKit fallback custom profile only) | | |
| 143 | + | | `engine.webkit.web_process_memory_conservative_threshold` | float | `0` | 0-1 (WebKit fallback custom profile only) | | |
| 144 | + | | `engine.webkit.web_process_memory_strict_threshold` | float | `0` | 0-1 (WebKit fallback custom profile only) | | |
| 145 | + | | `engine.webkit.network_process_memory_limit_mb` | int | `0` | >= 0 (WebKit fallback custom profile only) | | |
| 146 | + | | `engine.webkit.network_process_memory_poll_interval_sec` | float | `0` | >= 0 (WebKit fallback custom profile only) | | |
| 147 | + | | `engine.webkit.network_process_memory_conservative_threshold` | float | `0` | 0-1 (WebKit fallback custom profile only) | | |
| 148 | + | | `engine.webkit.network_process_memory_strict_threshold` | float | `0` | 0-1 (WebKit fallback custom profile only) | | |
| 149 | + | | `engine.pool_prewarm_count` | int | `4` | >= 0 | | |
| 150 | + | | `engine.zoom_cache_size` | int | `256` | >= 0 | | |
| 151 | 151 | | `downloads.path` | string | `` | | | |
| 152 | + | ||
| 153 | + | CEF is the default engine. WebKitGTK is a fallback selected with `engine.type = "webkit"`; `engine.webkit.*` keys are WebKit-specific. | |
| 154 | + | ||
| 155 | + | Legacy key migration: | |
| 156 | + | ||
| 157 | + | | Legacy key | Current key | | |
| 158 | + | |------------|-------------| | |
| 159 | + | | `privacy.cookie_policy` | `engine.cookie_policy` | | |
| 160 | + | | `privacy.itp_enabled` | `engine.webkit.itp_enabled` | | |
| 161 | + | | `rendering.disable_dmabuf_renderer` | `engine.webkit.disable_dmabuf_renderer` | | |
| 162 | + | | `rendering.force_compositing_mode` | `engine.webkit.force_compositing_mode` | | |
| 163 | + | | `rendering.disable_compositing_mode` | `engine.webkit.disable_compositing_mode` | | |
| 164 | + | | `rendering.gsk_renderer` | `engine.webkit.gsk_renderer` | | |
| 165 | + | | `rendering.disable_mipmaps` | `engine.webkit.disable_mipmaps` | | |
| 166 | + | | `rendering.prefer_gl` | `engine.webkit.prefer_gl` | | |
| 167 | + | | `rendering.draw_compositing_indicators` | `engine.webkit.draw_compositing_indicators` | | |
| 168 | + | | `rendering.show_fps` | `engine.webkit.show_fps` | | |
| 169 | + | | `rendering.sample_memory` | `engine.webkit.sample_memory` | | |
| 170 | + | | `rendering.debug_frames` | `engine.webkit.debug_frames` | | |
| 171 | + | | `media.force_vsync` | `engine.webkit.force_vsync` | | |
| 172 | + | | `media.gl_rendering_mode` | `engine.webkit.gl_rendering_mode` | | |
| 173 | + | | `media.gstreamer_debug_level` | `engine.webkit.gstreamer_debug_level` | | |
| 174 | + | | `runtime.prefix` | `engine.webkit.prefix` | | |
| 175 | + | | `performance.*` | `engine.profile`, `engine.pool_prewarm_count`, `engine.zoom_cache_size`, or matching `engine.webkit.*` custom tuning keys | | |
| 176 | + | | `rendering.mode` | Dropped; no current setting | | |
| @@ -52,18 +52,7 @@ | |||||
| 52 | | `appearance.dark_palette.accent` | string | `#a8a8a8` | | | 52 | | `appearance.dark_palette.accent` | string | `#a8a8a8` | | | ||
| 53 | | `appearance.dark_palette.border` | string | `#363636` | | | 53 | | `appearance.dark_palette.border` | string | `#363636` | | | ||
| 54 | | `debug.enable_devtools` | bool | `true` | | | 54 | | `debug.enable_devtools` | bool | `true` | | | ||
| 55 | - | | `rendering.mode` | string | `gpu` | `auto`, `gpu`, `cpu` | | 55 | + | | `engine.type` | string | `cef` | `cef`, `webkit` (CEF default; WebKitGTK fallback) | |
| 56 | - | | `rendering.disable_dmabuf_renderer` | bool | `false` | | | |||
| 57 | - | | `rendering.force_compositing_mode` | bool | `false` | | | |||
| 58 | - | | `rendering.disable_compositing_mode` | bool | `false` | | | |||
| 59 | - | | `rendering.gsk_renderer` | string | `auto` | `auto`, `opengl`, `vulkan`, `cairo` | | |||
| 60 | - | | `rendering.disable_mipmaps` | bool | `false` | | | |||
| 61 | - | | `rendering.prefer_gl` | bool | `false` | | | |||
| 62 | - | | `rendering.draw_compositing_indicators` | bool | `false` | | | |||
| 63 | - | | `rendering.show_fps` | bool | `false` | | | |||
| 64 | - | | `rendering.sample_memory` | bool | `false` | | | |||
| 65 | - | | `rendering.debug_frames` | bool | `false` | | | |||
| 66 | - | | `engine.type` | string | `cef` | `cef`, `webkit` | | |||
| 67 | | `engine.cef.render_stack` | string | `vulkan` | `vulkan`, `egl` | | 56 | | `engine.cef.render_stack` | string | `vulkan` | `vulkan`, `egl` | | ||
| 68 | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | | | 57 | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | | | ||
| 69 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | 58 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | ||
| @@ -76,6 +65,19 @@ | |||||
| 76 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | | | 65 | | `engine.cef.input.touchpad_navigation_enabled` | bool | `true` | | | ||
| 77 | | `engine.cef.input.touchpad_navigation_min_delta` | float | `200.0` | > 0 | | 66 | | `engine.cef.input.touchpad_navigation_min_delta` | float | `200.0` | > 0 | | ||
| 78 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | | 67 | | `engine.cef.input.touchpad_navigation_max_vertical_ratio` | float | `0.5` | > 0 | | ||
| 68 | + | | `engine.webkit.gsk_renderer` | string | `auto` | `auto`, `opengl`, `vulkan`, `cairo` (WebKit fallback only) | | |||
| 69 | + | | `engine.webkit.disable_dmabuf_renderer` | bool | `false` | WebKit fallback only | | |||
| 70 | + | | `engine.webkit.force_compositing_mode` | bool | `false` | WebKit fallback only | | |||
| 71 | + | | `engine.webkit.disable_compositing_mode` | bool | `false` | WebKit fallback only | | |||
| 72 | + | | `engine.webkit.disable_mipmaps` | bool | `false` | WebKit fallback only | | |||
| 73 | + | | `engine.webkit.prefer_gl` | bool | `false` | WebKit fallback only | | |||
| 74 | + | | `engine.webkit.draw_compositing_indicators` | bool | `false` | WebKit fallback only | | |||
| 75 | + | | `engine.webkit.show_fps` | bool | `false` | WebKit fallback only | | |||
| 76 | + | | `engine.webkit.sample_memory` | bool | `false` | WebKit fallback only | | |||
| 77 | + | | `engine.webkit.debug_frames` | bool | `false` | WebKit fallback only | | |||
| 78 | + | | `engine.webkit.force_vsync` | bool | `false` | WebKit fallback GStreamer only | | |||
| 79 | + | | `engine.webkit.gl_rendering_mode` | string | `auto` | `auto`, `gles2`, `gl3`, `none` (WebKit fallback only) | | |||
| 80 | + | | `engine.webkit.gstreamer_debug_level` | int | `0` | `0-5` (WebKit fallback only) | | |||
| 79 | | `default_ui_scale` | float | `1.0` | > 0 | | 81 | | `default_ui_scale` | float | `1.0` | > 0 | | ||
| 80 | 82 | ||||
| 81 | Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise_multiplier` and the additional axis-specific `engine.cef.input.scroll_vertical_multiplier`. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | 83 | Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise_multiplier` and the additional axis-specific `engine.cef.input.scroll_vertical_multiplier`. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | ||
| @@ -124,28 +126,51 @@ Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise | |||||
| 124 | | `media.hardware_decoding` | string | `auto` | `auto`, `force`, `disable` | | 126 | | `media.hardware_decoding` | string | `auto` | `auto`, `force`, `disable` | | ||
| 125 | | `media.prefer_av1` | bool | `false` | | | 127 | | `media.prefer_av1` | bool | `false` | | | ||
| 126 | | `media.show_diagnostics` | bool | `false` | | | 128 | | `media.show_diagnostics` | bool | `false` | | | ||
| 127 | - | | `media.force_vsync` | bool | `false` | | | 129 | + | | `engine.cef.cef_dir` | string | `` | CEF runtime directory | |
| 128 | - | | `media.gl_rendering_mode` | string | `auto` | `auto`, `gles2`, `gl3`, `none` | | 130 | + | | `engine.webkit.prefix` | string | `` | WebKitGTK fallback runtime prefix | |
| 129 | - | | `media.gstreamer_debug_level` | int | `0` | 0-5 | | |||
| 130 | - | | `runtime.prefix` | string | `` | | | |||
| 131 | | `clipboard.auto_copy_on_selection` | bool | `true` | | | 131 | | `clipboard.auto_copy_on_selection` | bool | `true` | | | ||
| 132 | | `content_filtering.enabled` | bool | `true` | | | 132 | | `content_filtering.enabled` | bool | `true` | | | ||
| 133 | | `content_filtering.auto_update` | bool | `true` | | | 133 | | `content_filtering.auto_update` | bool | `true` | | | ||
| 134 | | `update.enable_on_startup` | bool | `true` | | | 134 | | `update.enable_on_startup` | bool | `true` | | | ||
| 135 | | `update.auto_download` | bool | `false` | | | 135 | | `update.auto_download` | bool | `false` | | | ||
| 136 | | `update.notify_on_new_settings` | bool | `true` | | | 136 | | `update.notify_on_new_settings` | bool | `true` | | | ||
| 137 | - | | `performance.profile` | string | `default` | `default`, `lite`, `max`, `custom` | | 137 | + | | `engine.profile` | string | `default` | `default`, `lite`, `balanced`, `max`, `custom` | |
| 138 | - | | `performance.skia_cpu_painting_threads` | int | `0` | | | 138 | + | | `engine.webkit.skia_cpu_painting_threads` | int | `0` | >= 0 (WebKit fallback custom profile only) | |
| 139 | - | | `performance.skia_gpu_painting_threads` | int | `-1` | | | 139 | + | | `engine.webkit.skia_gpu_painting_threads` | int | `-1` | >= -1 (WebKit fallback custom profile only) | |
| 140 | - | | `performance.skia_enable_cpu_rendering` | bool | `false` | | | 140 | + | | `engine.webkit.skia_enable_cpu_rendering` | bool | `false` | WebKit fallback custom profile only | |
| 141 | - | | `performance.web_process_memory_limit_mb` | int | `0` | | | 141 | + | | `engine.webkit.web_process_memory_limit_mb` | int | `0` | >= 0 (WebKit fallback custom profile only) | |
| 142 | - | | `performance.web_process_memory_poll_interval_sec` | float | `0` | | | 142 | + | | `engine.webkit.web_process_memory_poll_interval_sec` | float | `0` | >= 0 (WebKit fallback custom profile only) | |
| 143 | - | | `performance.web_process_memory_conservative_threshold` | float | `0` | | | 143 | + | | `engine.webkit.web_process_memory_conservative_threshold` | float | `0` | 0-1 (WebKit fallback custom profile only) | |
| 144 | - | | `performance.web_process_memory_strict_threshold` | float | `0` | | | 144 | + | | `engine.webkit.web_process_memory_strict_threshold` | float | `0` | 0-1 (WebKit fallback custom profile only) | |
| 145 | - | | `performance.network_process_memory_limit_mb` | int | `0` | | | 145 | + | | `engine.webkit.network_process_memory_limit_mb` | int | `0` | >= 0 (WebKit fallback custom profile only) | |
| 146 | - | | `performance.network_process_memory_poll_interval_sec` | float | `0` | | | 146 | + | | `engine.webkit.network_process_memory_poll_interval_sec` | float | `0` | >= 0 (WebKit fallback custom profile only) | |
| 147 | - | | `performance.network_process_memory_conservative_threshold` | float | `0` | | | 147 | + | | `engine.webkit.network_process_memory_conservative_threshold` | float | `0` | 0-1 (WebKit fallback custom profile only) | |
| 148 | - | | `performance.network_process_memory_strict_threshold` | float | `0` | | | 148 | + | | `engine.webkit.network_process_memory_strict_threshold` | float | `0` | 0-1 (WebKit fallback custom profile only) | |
| 149 | - | | `performance.webview_pool_prewarm_count` | int | `4` | | | 149 | + | | `engine.pool_prewarm_count` | int | `4` | >= 0 | |
| 150 | - | | `performance.zoom_cache_size` | int | `256` | | | 150 | + | | `engine.zoom_cache_size` | int | `256` | >= 0 | |
| 151 | | `downloads.path` | string | `` | | | 151 | | `downloads.path` | string | `` | | | ||
| 152 | + | ||||
| 153 | + | CEF is the default engine. WebKitGTK is a fallback selected with `engine.type = "webkit"`; `engine.webkit.*` keys are WebKit-specific. | |||
| 154 | + | ||||
| 155 | + | Legacy key migration: | |||
| 156 | + | ||||
| 157 | + | | Legacy key | Current key | | |||
| 158 | + | |------------|-------------| | |||
| 159 | + | | `privacy.cookie_policy` | `engine.cookie_policy` | | |||
| 160 | + | | `privacy.itp_enabled` | `engine.webkit.itp_enabled` | | |||
| 161 | + | | `rendering.disable_dmabuf_renderer` | `engine.webkit.disable_dmabuf_renderer` | | |||
| 162 | + | | `rendering.force_compositing_mode` | `engine.webkit.force_compositing_mode` | | |||
| 163 | + | | `rendering.disable_compositing_mode` | `engine.webkit.disable_compositing_mode` | | |||
| 164 | + | | `rendering.gsk_renderer` | `engine.webkit.gsk_renderer` | | |||
| 165 | + | | `rendering.disable_mipmaps` | `engine.webkit.disable_mipmaps` | | |||
| 166 | + | | `rendering.prefer_gl` | `engine.webkit.prefer_gl` | | |||
| 167 | + | | `rendering.draw_compositing_indicators` | `engine.webkit.draw_compositing_indicators` | | |||
| 168 | + | | `rendering.show_fps` | `engine.webkit.show_fps` | | |||
| 169 | + | | `rendering.sample_memory` | `engine.webkit.sample_memory` | | |||
| 170 | + | | `rendering.debug_frames` | `engine.webkit.debug_frames` | | |||
| 171 | + | | `media.force_vsync` | `engine.webkit.force_vsync` | | |||
| 172 | + | | `media.gl_rendering_mode` | `engine.webkit.gl_rendering_mode` | | |||
| 173 | + | | `media.gstreamer_debug_level` | `engine.webkit.gstreamer_debug_level` | | |||
| 174 | + | | `runtime.prefix` | `engine.webkit.prefix` | | |||
| 175 | + | | `performance.*` | `engine.profile`, `engine.pool_prewarm_count`, `engine.zoom_cache_size`, or matching `engine.webkit.*` custom tuning keys | | |||
| 176 | + | | `rendering.mode` | Dropped; no current setting | | |||
M internal/infrastructure/config/loader.go +11 -9
| @@ -311,22 +311,24 @@ func bindBrowsingContextEnvAliases(v *viper.Viper) error { | |||
| 311 | 311 | ||
| 312 | 312 | // checkLegacyFormat detects old config format and returns an error directing user to migrate. | |
| 313 | 313 | func (m *Manager) checkLegacyFormat() error { | |
| 314 | - | // Check if old sections exist by looking for known keys. | |
| 315 | 314 | // IsSet works for legacy keys because they do not have defaults, while | |
| 316 | 315 | // engine.type has a default and must be checked with InConfig. | |
| 317 | - | hasOldSections := m.viper.IsSet("rendering.mode") || | |
| 318 | - | m.viper.IsSet("rendering.disable_dmabuf_renderer") || | |
| 319 | - | m.viper.IsSet("performance.profile") || | |
| 320 | - | m.viper.IsSet("privacy.cookie_policy") || | |
| 321 | - | m.viper.IsSet("runtime.prefix") | |
| 316 | + | hasOldSections := false | |
| 317 | + | for _, alias := range legacyEngineAliases { | |
| 318 | + | if m.viper.IsSet(alias.legacyKey()) { | |
| 319 | + | hasOldSections = true | |
| 320 | + | break | |
| 321 | + | } | |
| 322 | + | } | |
| 322 | 323 | ||
| 323 | 324 | hasEngineSection := m.viper.InConfig("engine.type") | |
| 324 | 325 | ||
| 325 | 326 | if hasOldSections && !hasEngineSection { | |
| 326 | 327 | return fmt.Errorf( | |
| 327 | - | "config format outdated: [rendering], [performance], [privacy] sections " + | |
| 328 | - | "have moved to [engine]/[engine.webkit] — " + | |
| 329 | - | "run \"dumber config migrate\" to update your config file", | |
| 328 | + | "config format outdated: legacy engine keys in [rendering], [performance], " + | |
| 329 | + | "[privacy], [runtime], or migrated [media] fields have moved to " + | |
| 330 | + | "[engine]/[engine.webkit] — run \"dumber config migrate\" " + | |
| 331 | + | "to update your config file", | |
| 330 | 332 | ) | |
| 331 | 333 | } | |
| 332 | 334 | return nil | |
| @@ -311,22 +311,24 @@ func bindBrowsingContextEnvAliases(v *viper.Viper) error { | |||||
| 311 | 311 | ||||
| 312 | // checkLegacyFormat detects old config format and returns an error directing user to migrate. | 312 | // checkLegacyFormat detects old config format and returns an error directing user to migrate. | ||
| 313 | func (m *Manager) checkLegacyFormat() error { | 313 | func (m *Manager) checkLegacyFormat() error { | ||
| 314 | - | // Check if old sections exist by looking for known keys. | |||
| 315 | // IsSet works for legacy keys because they do not have defaults, while | 314 | // IsSet works for legacy keys because they do not have defaults, while | ||
| 316 | // engine.type has a default and must be checked with InConfig. | 315 | // engine.type has a default and must be checked with InConfig. | ||
| 317 | - | hasOldSections := m.viper.IsSet("rendering.mode") || | 316 | + | hasOldSections := false |
| 318 | - | m.viper.IsSet("rendering.disable_dmabuf_renderer") || | 317 | + | for _, alias := range legacyEngineAliases { |
| 319 | - | m.viper.IsSet("performance.profile") || | 318 | + | if m.viper.IsSet(alias.legacyKey()) { |
| 320 | - | m.viper.IsSet("privacy.cookie_policy") || | 319 | + | hasOldSections = true |
| 321 | - | m.viper.IsSet("runtime.prefix") | 320 | + | break |
| 321 | + | } | |||
| 322 | + | } | |||
| 322 | 323 | ||||
| 323 | hasEngineSection := m.viper.InConfig("engine.type") | 324 | hasEngineSection := m.viper.InConfig("engine.type") | ||
| 324 | 325 | ||||
| 325 | if hasOldSections && !hasEngineSection { | 326 | if hasOldSections && !hasEngineSection { | ||
| 326 | return fmt.Errorf( | 327 | return fmt.Errorf( | ||
| 327 | - | "config format outdated: [rendering], [performance], [privacy] sections " + | 328 | + | "config format outdated: legacy engine keys in [rendering], [performance], " + |
| 328 | - | "have moved to [engine]/[engine.webkit] — " + | 329 | + | "[privacy], [runtime], or migrated [media] fields have moved to " + |
| 329 | - | "run \"dumber config migrate\" to update your config file", | 330 | + | "[engine]/[engine.webkit] — run \"dumber config migrate\" " + |
| 331 | + | "to update your config file", | |||
| 330 | ) | 332 | ) | ||
| 331 | } | 333 | } | ||
| 332 | return nil | 334 | return nil | ||
M internal/infrastructure/config/loader_legacy_test.go +44 -0
| @@ -78,6 +78,50 @@ func TestCheckLegacyFormat_RuntimePrefix_ReturnsError(t *testing.T) { | |||
| 78 | 78 | require.Contains(t, err.Error(), "dumber config migrate") | |
| 79 | 79 | } | |
| 80 | 80 | ||
| 81 | + | func TestCheckLegacyFormat_MetadataBackedAliasesReturnError(t *testing.T) { | |
| 82 | + | for _, alias := range legacyEngineAliases { | |
| 83 | + | key := alias.legacyKey() | |
| 84 | + | t.Run(key, func(t *testing.T) { | |
| 85 | + | m := &Manager{viper: viper.New()} | |
| 86 | + | m.viper.Set(key, sampleLegacyAliasValue(alias)) | |
| 87 | + | ||
| 88 | + | err := m.checkLegacyFormat() | |
| 89 | + | ||
| 90 | + | require.Error(t, err) | |
| 91 | + | require.Contains(t, err.Error(), "dumber config migrate") | |
| 92 | + | }) | |
| 93 | + | } | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | func sampleLegacyAliasValue(alias legacyEngineAlias) any { | |
| 97 | + | switch alias.legacyKey() { | |
| 98 | + | case "performance.profile": | |
| 99 | + | return "balanced" | |
| 100 | + | case "privacy.cookie_policy": | |
| 101 | + | return "always" | |
| 102 | + | case "rendering.mode": | |
| 103 | + | return "gpu" | |
| 104 | + | case "rendering.gsk_renderer", "media.gl_rendering_mode": | |
| 105 | + | return "auto" | |
| 106 | + | case "runtime.prefix": | |
| 107 | + | return "/usr" | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | switch alias.legacyField { | |
| 111 | + | case "zoom_cache_size", "webview_pool_prewarm_count", | |
| 112 | + | "skia_cpu_painting_threads", "skia_gpu_painting_threads", | |
| 113 | + | "web_process_memory_limit_mb", "network_process_memory_limit_mb", | |
| 114 | + | "gstreamer_debug_level": | |
| 115 | + | return 1 | |
| 116 | + | case "web_process_memory_poll_interval_sec", "network_process_memory_poll_interval_sec", | |
| 117 | + | "web_process_memory_conservative_threshold", "web_process_memory_strict_threshold", | |
| 118 | + | "network_process_memory_conservative_threshold", "network_process_memory_strict_threshold": | |
| 119 | + | return 0.5 | |
| 120 | + | default: | |
| 121 | + | return true | |
| 122 | + | } | |
| 123 | + | } | |
| 124 | + | ||
| 81 | 125 | func TestTransformLegacyConfig_DefaultAdaptiveDoesNotBlockLegacyCEFMigration(t *testing.T) { | |
| 82 | 126 | dir := t.TempDir() | |
| 83 | 127 | configPath := filepath.Join(dir, "config.toml") | |
| @@ -78,6 +78,50 @@ func TestCheckLegacyFormat_RuntimePrefix_ReturnsError(t *testing.T) { | |||||
| 78 | require.Contains(t, err.Error(), "dumber config migrate") | 78 | require.Contains(t, err.Error(), "dumber config migrate") | ||
| 79 | } | 79 | } | ||
| 80 | 80 | ||||
| 81 | + | func TestCheckLegacyFormat_MetadataBackedAliasesReturnError(t *testing.T) { | |||
| 82 | + | for _, alias := range legacyEngineAliases { | |||
| 83 | + | key := alias.legacyKey() | |||
| 84 | + | t.Run(key, func(t *testing.T) { | |||
| 85 | + | m := &Manager{viper: viper.New()} | |||
| 86 | + | m.viper.Set(key, sampleLegacyAliasValue(alias)) | |||
| 87 | + | ||||
| 88 | + | err := m.checkLegacyFormat() | |||
| 89 | + | ||||
| 90 | + | require.Error(t, err) | |||
| 91 | + | require.Contains(t, err.Error(), "dumber config migrate") | |||
| 92 | + | }) | |||
| 93 | + | } | |||
| 94 | + | } | |||
| 95 | + | ||||
| 96 | + | func sampleLegacyAliasValue(alias legacyEngineAlias) any { | |||
| 97 | + | switch alias.legacyKey() { | |||
| 98 | + | case "performance.profile": | |||
| 99 | + | return "balanced" | |||
| 100 | + | case "privacy.cookie_policy": | |||
| 101 | + | return "always" | |||
| 102 | + | case "rendering.mode": | |||
| 103 | + | return "gpu" | |||
| 104 | + | case "rendering.gsk_renderer", "media.gl_rendering_mode": | |||
| 105 | + | return "auto" | |||
| 106 | + | case "runtime.prefix": | |||
| 107 | + | return "/usr" | |||
| 108 | + | } | |||
| 109 | + | ||||
| 110 | + | switch alias.legacyField { | |||
| 111 | + | case "zoom_cache_size", "webview_pool_prewarm_count", | |||
| 112 | + | "skia_cpu_painting_threads", "skia_gpu_painting_threads", | |||
| 113 | + | "web_process_memory_limit_mb", "network_process_memory_limit_mb", | |||
| 114 | + | "gstreamer_debug_level": | |||
| 115 | + | return 1 | |||
| 116 | + | case "web_process_memory_poll_interval_sec", "network_process_memory_poll_interval_sec", | |||
| 117 | + | "web_process_memory_conservative_threshold", "web_process_memory_strict_threshold", | |||
| 118 | + | "network_process_memory_conservative_threshold", "network_process_memory_strict_threshold": | |||
| 119 | + | return 0.5 | |||
| 120 | + | default: | |||
| 121 | + | return true | |||
| 122 | + | } | |||
| 123 | + | } | |||
| 124 | + | ||||
| 81 | func TestTransformLegacyConfig_DefaultAdaptiveDoesNotBlockLegacyCEFMigration(t *testing.T) { | 125 | func TestTransformLegacyConfig_DefaultAdaptiveDoesNotBlockLegacyCEFMigration(t *testing.T) { | ||
| 82 | dir := t.TempDir() | 126 | dir := t.TempDir() | ||
| 83 | configPath := filepath.Join(dir, "config.toml") | 127 | configPath := filepath.Join(dir, "config.toml") | ||
M internal/infrastructure/config/migrate_engine.go +134 -88
| @@ -10,79 +10,127 @@ import ( | |||
| 10 | 10 | // configFilePermissions is the file permission used when writing migrated config files. | |
| 11 | 11 | const configFilePermissions fs.FileMode = 0o644 | |
| 12 | 12 | ||
| 13 | - | // engineMigrationMapping defines old key -> new key mappings for the engine migration. | |
| 14 | - | // Keys with empty new key ("") are dropped (not migrated). | |
| 15 | - | // The source section is the first part of the old key. | |
| 16 | - | type engineFieldMapping struct { | |
| 17 | - | oldSection string | |
| 18 | - | oldField string | |
| 19 | - | newSection string // "engine" or "engine.webkit" | |
| 20 | - | newField string // empty means drop | |
| 13 | + | // legacyEngineAlias defines the single metadata source for old engine config keys. | |
| 14 | + | // Dropped aliases are detected as legacy input but are not written to a new target. | |
| 15 | + | type legacyEngineAlias struct { | |
| 16 | + | legacySection string | |
| 17 | + | legacyField string | |
| 18 | + | targetSection string // "engine" or "engine.webkit"; empty when dropped | |
| 19 | + | targetField string // empty when dropped | |
| 20 | + | dropped bool | |
| 21 | 21 | } | |
| 22 | 22 | ||
| 23 | - | // engineUniversalMappings are fields that go into [engine] (not [engine.webkit]). | |
| 24 | - | var engineUniversalMappings = []engineFieldMapping{ | |
| 23 | + | func (a legacyEngineAlias) legacyKey() string { | |
| 24 | + | return a.legacySection + "." + a.legacyField | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | const ( | |
| 28 | + | engineTargetSection = "engine" | |
| 29 | + | engineWebKitTargetSection = "engine.webkit" | |
| 30 | + | ) | |
| 31 | + | ||
| 32 | + | func legacyEngineTarget(legacySection, legacyField, targetSection, targetField string) legacyEngineAlias { | |
| 33 | + | return legacyEngineAlias{ | |
| 34 | + | legacySection: legacySection, | |
| 35 | + | legacyField: legacyField, | |
| 36 | + | targetSection: targetSection, | |
| 37 | + | targetField: targetField, | |
| 38 | + | } | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | func legacyEngineSameTarget(legacySection, legacyField, targetSection string) legacyEngineAlias { | |
| 42 | + | return legacyEngineTarget(legacySection, legacyField, targetSection, legacyField) | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | func droppedLegacyEngineAlias(legacySection, legacyField string) legacyEngineAlias { | |
| 46 | + | return legacyEngineAlias{legacySection: legacySection, legacyField: legacyField, dropped: true} | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | // legacyEngineAliases is the canonical list of old-to-new engine config aliases. | |
| 50 | + | var legacyEngineAliases = []legacyEngineAlias{ | |
| 25 | 51 | // [performance] -> [engine] | |
| 26 | - | {oldSection: "performance", oldField: "profile", newSection: "engine", newField: "profile"}, | |
| 27 | - | {oldSection: "performance", oldField: "zoom_cache_size", newSection: "engine", newField: "zoom_cache_size"}, | |
| 28 | - | {oldSection: "performance", oldField: "webview_pool_prewarm_count", newSection: "engine", newField: "pool_prewarm_count"}, | |
| 52 | + | legacyEngineSameTarget("performance", "profile", engineTargetSection), | |
| 53 | + | legacyEngineSameTarget("performance", "zoom_cache_size", engineTargetSection), | |
| 54 | + | legacyEngineTarget("performance", "webview_pool_prewarm_count", engineTargetSection, "pool_prewarm_count"), | |
| 29 | 55 | // [privacy] -> [engine] | |
| 30 | - | {oldSection: "privacy", oldField: "cookie_policy", newSection: "engine", newField: "cookie_policy"}, | |
| 31 | - | } | |
| 56 | + | legacyEngineSameTarget("privacy", "cookie_policy", engineTargetSection), | |
| 32 | 57 | ||
| 33 | - | // engineWebkitMappings are fields that go into [engine.webkit]. | |
| 34 | - | var engineWebkitMappings = []engineFieldMapping{ | |
| 35 | 58 | // [privacy] -> [engine.webkit] | |
| 36 | - | {oldSection: "privacy", oldField: "itp_enabled", newSection: "engine.webkit", newField: "itp_enabled"}, | |
| 59 | + | legacyEngineSameTarget("privacy", "itp_enabled", engineWebKitTargetSection), | |
| 37 | 60 | ||
| 38 | 61 | // [rendering] -> [engine.webkit] (rendering.mode is dropped) | |
| 39 | - | {oldSection: "rendering", oldField: "disable_dmabuf_renderer", newSection: "engine.webkit", newField: "disable_dmabuf_renderer"}, | |
| 40 | - | {oldSection: "rendering", oldField: "force_compositing_mode", newSection: "engine.webkit", newField: "force_compositing_mode"}, | |
| 41 | - | {oldSection: "rendering", oldField: "disable_compositing_mode", newSection: "engine.webkit", newField: "disable_compositing_mode"}, | |
| 42 | - | {oldSection: "rendering", oldField: "gsk_renderer", newSection: "engine.webkit", newField: "gsk_renderer"}, | |
| 43 | - | {oldSection: "rendering", oldField: "disable_mipmaps", newSection: "engine.webkit", newField: "disable_mipmaps"}, | |
| 44 | - | {oldSection: "rendering", oldField: "prefer_gl", newSection: "engine.webkit", newField: "prefer_gl"}, | |
| 45 | - | {oldSection: "rendering", oldField: "draw_compositing_indicators", newSection: "engine.webkit", newField: "draw_compositing_indicators"}, | |
| 46 | - | {oldSection: "rendering", oldField: "show_fps", newSection: "engine.webkit", newField: "show_fps"}, | |
| 47 | - | {oldSection: "rendering", oldField: "sample_memory", newSection: "engine.webkit", newField: "sample_memory"}, | |
| 48 | - | {oldSection: "rendering", oldField: "debug_frames", newSection: "engine.webkit", newField: "debug_frames"}, | |
| 62 | + | droppedLegacyEngineAlias("rendering", "mode"), | |
| 63 | + | legacyEngineSameTarget("rendering", "disable_dmabuf_renderer", engineWebKitTargetSection), | |
| 64 | + | legacyEngineSameTarget("rendering", "force_compositing_mode", engineWebKitTargetSection), | |
| 65 | + | legacyEngineSameTarget("rendering", "disable_compositing_mode", engineWebKitTargetSection), | |
| 66 | + | legacyEngineSameTarget("rendering", "gsk_renderer", engineWebKitTargetSection), | |
| 67 | + | legacyEngineSameTarget("rendering", "disable_mipmaps", engineWebKitTargetSection), | |
| 68 | + | legacyEngineSameTarget("rendering", "prefer_gl", engineWebKitTargetSection), | |
| 69 | + | legacyEngineSameTarget("rendering", "draw_compositing_indicators", engineWebKitTargetSection), | |
| 70 | + | legacyEngineSameTarget("rendering", "show_fps", engineWebKitTargetSection), | |
| 71 | + | legacyEngineSameTarget("rendering", "sample_memory", engineWebKitTargetSection), | |
| 72 | + | legacyEngineSameTarget("rendering", "debug_frames", engineWebKitTargetSection), | |
| 49 | 73 | ||
| 50 | 74 | // [performance] -> [engine.webkit] (webkit-specific) — field names are identical | |
| 51 | - | {oldSection: "performance", oldField: "skia_cpu_painting_threads", | |
| 52 | - | newSection: "engine.webkit", newField: "skia_cpu_painting_threads"}, | |
| 53 | - | {oldSection: "performance", oldField: "skia_gpu_painting_threads", | |
| 54 | - | newSection: "engine.webkit", newField: "skia_gpu_painting_threads"}, | |
| 55 | - | {oldSection: "performance", oldField: "skia_enable_cpu_rendering", | |
| 56 | - | newSection: "engine.webkit", newField: "skia_enable_cpu_rendering"}, | |
| 57 | - | {oldSection: "performance", oldField: "web_process_memory_limit_mb", | |
| 58 | - | newSection: "engine.webkit", newField: "web_process_memory_limit_mb"}, | |
| 59 | - | {oldSection: "performance", oldField: "web_process_memory_poll_interval_sec", | |
| 60 | - | newSection: "engine.webkit", newField: "web_process_memory_poll_interval_sec"}, | |
| 61 | - | {oldSection: "performance", oldField: "web_process_memory_conservative_threshold", | |
| 62 | - | newSection: "engine.webkit", newField: "web_process_memory_conservative_threshold"}, | |
| 63 | - | {oldSection: "performance", oldField: "web_process_memory_strict_threshold", | |
| 64 | - | newSection: "engine.webkit", newField: "web_process_memory_strict_threshold"}, | |
| 65 | - | {oldSection: "performance", oldField: "network_process_memory_limit_mb", | |
| 66 | - | newSection: "engine.webkit", newField: "network_process_memory_limit_mb"}, | |
| 67 | - | {oldSection: "performance", oldField: "network_process_memory_poll_interval_sec", | |
| 68 | - | newSection: "engine.webkit", newField: "network_process_memory_poll_interval_sec"}, | |
| 69 | - | {oldSection: "performance", oldField: "network_process_memory_conservative_threshold", | |
| 70 | - | newSection: "engine.webkit", newField: "network_process_memory_conservative_threshold"}, | |
| 71 | - | {oldSection: "performance", oldField: "network_process_memory_strict_threshold", | |
| 72 | - | newSection: "engine.webkit", newField: "network_process_memory_strict_threshold"}, | |
| 75 | + | legacyEngineSameTarget("performance", "skia_cpu_painting_threads", engineWebKitTargetSection), | |
| 76 | + | legacyEngineSameTarget("performance", "skia_gpu_painting_threads", engineWebKitTargetSection), | |
| 77 | + | legacyEngineSameTarget("performance", "skia_enable_cpu_rendering", engineWebKitTargetSection), | |
| 78 | + | legacyEngineSameTarget("performance", "web_process_memory_limit_mb", engineWebKitTargetSection), | |
| 79 | + | legacyEngineSameTarget("performance", "web_process_memory_poll_interval_sec", engineWebKitTargetSection), | |
| 80 | + | legacyEngineSameTarget("performance", "web_process_memory_conservative_threshold", engineWebKitTargetSection), | |
| 81 | + | legacyEngineSameTarget("performance", "web_process_memory_strict_threshold", engineWebKitTargetSection), | |
| 82 | + | legacyEngineSameTarget("performance", "network_process_memory_limit_mb", engineWebKitTargetSection), | |
| 83 | + | legacyEngineSameTarget("performance", "network_process_memory_poll_interval_sec", engineWebKitTargetSection), | |
| 84 | + | legacyEngineSameTarget("performance", "network_process_memory_conservative_threshold", engineWebKitTargetSection), | |
| 85 | + | legacyEngineSameTarget("performance", "network_process_memory_strict_threshold", engineWebKitTargetSection), | |
| 73 | 86 | ||
| 74 | 87 | // [media] GStreamer fields -> [engine.webkit] | |
| 75 | - | {oldSection: "media", oldField: "force_vsync", newSection: "engine.webkit", newField: "force_vsync"}, | |
| 76 | - | {oldSection: "media", oldField: "gl_rendering_mode", newSection: "engine.webkit", newField: "gl_rendering_mode"}, | |
| 77 | - | {oldSection: "media", oldField: "gstreamer_debug_level", newSection: "engine.webkit", newField: "gstreamer_debug_level"}, | |
| 88 | + | legacyEngineSameTarget("media", "force_vsync", engineWebKitTargetSection), | |
| 89 | + | legacyEngineSameTarget("media", "gl_rendering_mode", engineWebKitTargetSection), | |
| 90 | + | legacyEngineSameTarget("media", "gstreamer_debug_level", engineWebKitTargetSection), | |
| 78 | 91 | ||
| 79 | 92 | // [runtime] -> [engine.webkit] | |
| 80 | - | {oldSection: "runtime", oldField: "prefix", newSection: "engine.webkit", newField: "prefix"}, | |
| 93 | + | legacyEngineSameTarget("runtime", "prefix", engineWebKitTargetSection), | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | func legacyEngineAliasesForSection(section string) []legacyEngineAlias { | |
| 97 | + | aliases := make([]legacyEngineAlias, 0) | |
| 98 | + | for _, alias := range legacyEngineAliases { | |
| 99 | + | if alias.legacySection == section { | |
| 100 | + | aliases = append(aliases, alias) | |
| 101 | + | } | |
| 102 | + | } | |
| 103 | + | return aliases | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | func legacyEngineSectionsToRemove() []string { | |
| 107 | + | sections := make([]string, 0) | |
| 108 | + | seen := make(map[string]struct{}) | |
| 109 | + | for _, alias := range legacyEngineAliases { | |
| 110 | + | if alias.legacySection == "media" { | |
| 111 | + | continue | |
| 112 | + | } | |
| 113 | + | if _, ok := seen[alias.legacySection]; ok { | |
| 114 | + | continue | |
| 115 | + | } | |
| 116 | + | seen[alias.legacySection] = struct{}{} | |
| 117 | + | sections = append(sections, alias.legacySection) | |
| 118 | + | } | |
| 119 | + | return sections | |
| 81 | 120 | } | |
| 82 | 121 | ||
| 83 | - | // oldSectionsToRemove are the sections that are fully removed after migration | |
| 84 | - | // (media is not here because it has fields that stay). | |
| 85 | - | var oldSectionsToRemove = []string{"rendering", "performance", "privacy", "runtime"} | |
| 122 | + | func hasLegacyEngineAlias(raw map[string]any, alias legacyEngineAlias) bool { | |
| 123 | + | sectionAny, exists := raw[alias.legacySection] | |
| 124 | + | if !exists { | |
| 125 | + | return false | |
| 126 | + | } | |
| 127 | + | sectionMap, ok := sectionAny.(map[string]any) | |
| 128 | + | if !ok { | |
| 129 | + | return false | |
| 130 | + | } | |
| 131 | + | _, exists = sectionMap[alias.legacyField] | |
| 132 | + | return exists | |
| 133 | + | } | |
| 86 | 134 | ||
| 87 | 135 | // MigrateToEngineConfig restructures old top-level config sections into [engine]/[engine.webkit]. | |
| 88 | 136 | // Returns true if migration was performed, false if already migrated or no old sections found. | |
| @@ -93,7 +141,7 @@ func MigrateToEngineConfig(configFile string) (bool, error) { | |||
| 93 | 141 | } | |
| 94 | 142 | ||
| 95 | 143 | if _, exists := raw["engine"]; exists { | |
| 96 | - | for _, section := range []string{"rendering", "performance", "privacy", "runtime"} { | |
| 144 | + | for _, section := range legacyEngineSectionsToRemove() { | |
| 97 | 145 | if _, hasLegacy := raw[section]; hasLegacy { | |
| 98 | 146 | return false, fmt.Errorf( | |
| 99 | 147 | "mixed old/new config: [engine] coexists with legacy [%s]; "+ | |
| @@ -102,16 +150,13 @@ func MigrateToEngineConfig(configFile string) (bool, error) { | |||
| 102 | 150 | ) | |
| 103 | 151 | } | |
| 104 | 152 | } | |
| 105 | - | // Check for deprecated media keys that moved to [engine.webkit]. | |
| 106 | - | if mediaRaw, ok := raw["media"].(map[string]any); ok { | |
| 107 | - | for _, key := range []string{"force_vsync", "gl_rendering_mode", "gstreamer_debug_level"} { | |
| 108 | - | if _, has := mediaRaw[key]; has { | |
| 109 | - | return false, fmt.Errorf( | |
| 110 | - | "mixed old/new config: [engine] coexists with deprecated "+ | |
| 111 | - | "media.%s; remove it from [media] (now in [engine.webkit])", | |
| 112 | - | key, | |
| 113 | - | ) | |
| 114 | - | } | |
| 153 | + | for _, alias := range legacyEngineAliasesForSection("media") { | |
| 154 | + | if hasLegacyEngineAlias(raw, alias) { | |
| 155 | + | return false, fmt.Errorf( | |
| 156 | + | "mixed old/new config: [engine] coexists with deprecated "+ | |
| 157 | + | "media.%s; remove it from [media] (now in [engine.webkit])", | |
| 158 | + | alias.legacyField, | |
| 159 | + | ) | |
| 115 | 160 | } | |
| 116 | 161 | } | |
| 117 | 162 | return false, nil // already migrated | |
| @@ -150,18 +195,14 @@ func readRawTOML(path string) (map[string]any, error) { | |||
| 150 | 195 | ||
| 151 | 196 | // hasOldEngineSections checks if the raw config has old sections that need migration. | |
| 152 | 197 | func hasOldEngineSections(raw map[string]any) bool { | |
| 153 | - | for _, section := range []string{"rendering", "performance", "privacy", "runtime"} { | |
| 198 | + | for _, section := range legacyEngineSectionsToRemove() { | |
| 154 | 199 | if _, exists := raw[section]; exists { | |
| 155 | 200 | return true | |
| 156 | 201 | } | |
| 157 | 202 | } | |
| 158 | - | if mediaAny, exists := raw["media"]; exists { | |
| 159 | - | if media, ok := mediaAny.(map[string]any); ok { | |
| 160 | - | for _, f := range []string{"force_vsync", "gl_rendering_mode", "gstreamer_debug_level"} { | |
| 161 | - | if _, has := media[f]; has { | |
| 162 | - | return true | |
| 163 | - | } | |
| 164 | - | } | |
| 203 | + | for _, alias := range legacyEngineAliasesForSection("media") { | |
| 204 | + | if hasLegacyEngineAlias(raw, alias) { | |
| 205 | + | return true | |
| 165 | 206 | } | |
| 166 | 207 | } | |
| 167 | 208 | return false | |
| @@ -172,14 +213,19 @@ func applyEngineMappings(raw map[string]any) { | |||
| 172 | 213 | engineMap := make(map[string]any) | |
| 173 | 214 | webkitMap := make(map[string]any) | |
| 174 | 215 | ||
| 175 | - | for _, m := range engineUniversalMappings { | |
| 176 | - | if val := getSectionField(raw, m.oldSection, m.oldField); val != nil { | |
| 177 | - | engineMap[m.newField] = val | |
| 216 | + | for _, alias := range legacyEngineAliases { | |
| 217 | + | if alias.dropped { | |
| 218 | + | continue | |
| 178 | 219 | } | |
| 179 | - | } | |
| 180 | - | for _, m := range engineWebkitMappings { | |
| 181 | - | if val := getSectionField(raw, m.oldSection, m.oldField); val != nil { | |
| 182 | - | webkitMap[m.newField] = val | |
| 220 | + | val := getSectionField(raw, alias.legacySection, alias.legacyField) | |
| 221 | + | if val == nil { | |
| 222 | + | continue | |
| 223 | + | } | |
| 224 | + | switch alias.targetSection { | |
| 225 | + | case "engine": | |
| 226 | + | engineMap[alias.targetField] = val | |
| 227 | + | case "engine.webkit": | |
| 228 | + | webkitMap[alias.targetField] = val | |
| 183 | 229 | } | |
| 184 | 230 | } | |
| 185 | 231 | ||
| @@ -189,14 +235,14 @@ func applyEngineMappings(raw map[string]any) { | |||
| 189 | 235 | } | |
| 190 | 236 | raw["engine"] = engineMap | |
| 191 | 237 | ||
| 192 | - | for _, section := range oldSectionsToRemove { | |
| 238 | + | for _, section := range legacyEngineSectionsToRemove() { | |
| 193 | 239 | delete(raw, section) | |
| 194 | 240 | } | |
| 195 | 241 | ||
| 196 | 242 | stripMigratedMediaFields(raw) | |
| 197 | 243 | } | |
| 198 | 244 | ||
| 199 | - | // stripMigratedMediaFields removes GStreamer fields from [media] while keeping the rest. | |
| 245 | + | // stripMigratedMediaFields removes migrated fields from [media] while keeping the rest. | |
| 200 | 246 | func stripMigratedMediaFields(raw map[string]any) { | |
| 201 | 247 | mediaAny, exists := raw["media"] | |
| 202 | 248 | if !exists { | |
| @@ -206,9 +252,9 @@ func stripMigratedMediaFields(raw map[string]any) { | |||
| 206 | 252 | if !ok { | |
| 207 | 253 | return | |
| 208 | 254 | } | |
| 209 | - | delete(media, "force_vsync") | |
| 210 | - | delete(media, "gl_rendering_mode") | |
| 211 | - | delete(media, "gstreamer_debug_level") | |
| 255 | + | for _, alias := range legacyEngineAliasesForSection("media") { | |
| 256 | + | delete(media, alias.legacyField) | |
| 257 | + | } | |
| 212 | 258 | if len(media) == 0 { | |
| 213 | 259 | delete(raw, "media") | |
| 214 | 260 | } | |
| @@ -10,79 +10,127 @@ import ( | |||||
| 10 | // configFilePermissions is the file permission used when writing migrated config files. | 10 | // configFilePermissions is the file permission used when writing migrated config files. | ||
| 11 | const configFilePermissions fs.FileMode = 0o644 | 11 | const configFilePermissions fs.FileMode = 0o644 | ||
| 12 | 12 | ||||
| 13 | - | // engineMigrationMapping defines old key -> new key mappings for the engine migration. | 13 | + | // legacyEngineAlias defines the single metadata source for old engine config keys. |
| 14 | - | // Keys with empty new key ("") are dropped (not migrated). | 14 | + | // Dropped aliases are detected as legacy input but are not written to a new target. |
| 15 | - | // The source section is the first part of the old key. | 15 | + | type legacyEngineAlias struct { |
| 16 | - | type engineFieldMapping struct { | 16 | + | legacySection string |
| 17 | - | oldSection string | 17 | + | legacyField string |
| 18 | - | oldField string | 18 | + | targetSection string // "engine" or "engine.webkit"; empty when dropped |
| 19 | - | newSection string // "engine" or "engine.webkit" | 19 | + | targetField string // empty when dropped |
| 20 | - | newField string // empty means drop | 20 | + | dropped bool |
| 21 | } | 21 | } | ||
| 22 | 22 | ||||
| 23 | - | // engineUniversalMappings are fields that go into [engine] (not [engine.webkit]). | 23 | + | func (a legacyEngineAlias) legacyKey() string { |
| 24 | - | var engineUniversalMappings = []engineFieldMapping{ | 24 | + | return a.legacySection + "." + a.legacyField |
| 25 | + | } | |||
| 26 | + | ||||
| 27 | + | const ( | |||
| 28 | + | engineTargetSection = "engine" | |||
| 29 | + | engineWebKitTargetSection = "engine.webkit" | |||
| 30 | + | ) | |||
| 31 | + | ||||
| 32 | + | func legacyEngineTarget(legacySection, legacyField, targetSection, targetField string) legacyEngineAlias { | |||
| 33 | + | return legacyEngineAlias{ | |||
| 34 | + | legacySection: legacySection, | |||
| 35 | + | legacyField: legacyField, | |||
| 36 | + | targetSection: targetSection, | |||
| 37 | + | targetField: targetField, | |||
| 38 | + | } | |||
| 39 | + | } | |||
| 40 | + | ||||
| 41 | + | func legacyEngineSameTarget(legacySection, legacyField, targetSection string) legacyEngineAlias { | |||
| 42 | + | return legacyEngineTarget(legacySection, legacyField, targetSection, legacyField) | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | func droppedLegacyEngineAlias(legacySection, legacyField string) legacyEngineAlias { | |||
| 46 | + | return legacyEngineAlias{legacySection: legacySection, legacyField: legacyField, dropped: true} | |||
| 47 | + | } | |||
| 48 | + | ||||
| 49 | + | // legacyEngineAliases is the canonical list of old-to-new engine config aliases. | |||
| 50 | + | var legacyEngineAliases = []legacyEngineAlias{ | |||
| 25 | // [performance] -> [engine] | 51 | // [performance] -> [engine] | ||
| 26 | - | {oldSection: "performance", oldField: "profile", newSection: "engine", newField: "profile"}, | 52 | + | legacyEngineSameTarget("performance", "profile", engineTargetSection), |
| 27 | - | {oldSection: "performance", oldField: "zoom_cache_size", newSection: "engine", newField: "zoom_cache_size"}, | 53 | + | legacyEngineSameTarget("performance", "zoom_cache_size", engineTargetSection), |
| 28 | - | {oldSection: "performance", oldField: "webview_pool_prewarm_count", newSection: "engine", newField: "pool_prewarm_count"}, | 54 | + | legacyEngineTarget("performance", "webview_pool_prewarm_count", engineTargetSection, "pool_prewarm_count"), |
| 29 | // [privacy] -> [engine] | 55 | // [privacy] -> [engine] | ||
| 30 | - | {oldSection: "privacy", oldField: "cookie_policy", newSection: "engine", newField: "cookie_policy"}, | 56 | + | legacyEngineSameTarget("privacy", "cookie_policy", engineTargetSection), |
| 31 | - | } | |||
| 32 | 57 | ||||
| 33 | - | // engineWebkitMappings are fields that go into [engine.webkit]. | |||
| 34 | - | var engineWebkitMappings = []engineFieldMapping{ | |||
| 35 | // [privacy] -> [engine.webkit] | 58 | // [privacy] -> [engine.webkit] | ||
| 36 | - | {oldSection: "privacy", oldField: "itp_enabled", newSection: "engine.webkit", newField: "itp_enabled"}, | 59 | + | legacyEngineSameTarget("privacy", "itp_enabled", engineWebKitTargetSection), |
| 37 | 60 | ||||
| 38 | // [rendering] -> [engine.webkit] (rendering.mode is dropped) | 61 | // [rendering] -> [engine.webkit] (rendering.mode is dropped) | ||
| 39 | - | {oldSection: "rendering", oldField: "disable_dmabuf_renderer", newSection: "engine.webkit", newField: "disable_dmabuf_renderer"}, | 62 | + | droppedLegacyEngineAlias("rendering", "mode"), |
| 40 | - | {oldSection: "rendering", oldField: "force_compositing_mode", newSection: "engine.webkit", newField: "force_compositing_mode"}, | 63 | + | legacyEngineSameTarget("rendering", "disable_dmabuf_renderer", engineWebKitTargetSection), |
| 41 | - | {oldSection: "rendering", oldField: "disable_compositing_mode", newSection: "engine.webkit", newField: "disable_compositing_mode"}, | 64 | + | legacyEngineSameTarget("rendering", "force_compositing_mode", engineWebKitTargetSection), |
| 42 | - | {oldSection: "rendering", oldField: "gsk_renderer", newSection: "engine.webkit", newField: "gsk_renderer"}, | 65 | + | legacyEngineSameTarget("rendering", "disable_compositing_mode", engineWebKitTargetSection), |
| 43 | - | {oldSection: "rendering", oldField: "disable_mipmaps", newSection: "engine.webkit", newField: "disable_mipmaps"}, | 66 | + | legacyEngineSameTarget("rendering", "gsk_renderer", engineWebKitTargetSection), |
| 44 | - | {oldSection: "rendering", oldField: "prefer_gl", newSection: "engine.webkit", newField: "prefer_gl"}, | 67 | + | legacyEngineSameTarget("rendering", "disable_mipmaps", engineWebKitTargetSection), |
| 45 | - | {oldSection: "rendering", oldField: "draw_compositing_indicators", newSection: "engine.webkit", newField: "draw_compositing_indicators"}, | 68 | + | legacyEngineSameTarget("rendering", "prefer_gl", engineWebKitTargetSection), |
| 46 | - | {oldSection: "rendering", oldField: "show_fps", newSection: "engine.webkit", newField: "show_fps"}, | 69 | + | legacyEngineSameTarget("rendering", "draw_compositing_indicators", engineWebKitTargetSection), |
| 47 | - | {oldSection: "rendering", oldField: "sample_memory", newSection: "engine.webkit", newField: "sample_memory"}, | 70 | + | legacyEngineSameTarget("rendering", "show_fps", engineWebKitTargetSection), |
| 48 | - | {oldSection: "rendering", oldField: "debug_frames", newSection: "engine.webkit", newField: "debug_frames"}, | 71 | + | legacyEngineSameTarget("rendering", "sample_memory", engineWebKitTargetSection), |
| 72 | + | legacyEngineSameTarget("rendering", "debug_frames", engineWebKitTargetSection), | |||
| 49 | 73 | ||||
| 50 | // [performance] -> [engine.webkit] (webkit-specific) — field names are identical | 74 | // [performance] -> [engine.webkit] (webkit-specific) — field names are identical | ||
| 51 | - | {oldSection: "performance", oldField: "skia_cpu_painting_threads", | 75 | + | legacyEngineSameTarget("performance", "skia_cpu_painting_threads", engineWebKitTargetSection), |
| 52 | - | newSection: "engine.webkit", newField: "skia_cpu_painting_threads"}, | 76 | + | legacyEngineSameTarget("performance", "skia_gpu_painting_threads", engineWebKitTargetSection), |
| 53 | - | {oldSection: "performance", oldField: "skia_gpu_painting_threads", | 77 | + | legacyEngineSameTarget("performance", "skia_enable_cpu_rendering", engineWebKitTargetSection), |
| 54 | - | newSection: "engine.webkit", newField: "skia_gpu_painting_threads"}, | 78 | + | legacyEngineSameTarget("performance", "web_process_memory_limit_mb", engineWebKitTargetSection), |
| 55 | - | {oldSection: "performance", oldField: "skia_enable_cpu_rendering", | 79 | + | legacyEngineSameTarget("performance", "web_process_memory_poll_interval_sec", engineWebKitTargetSection), |
| 56 | - | newSection: "engine.webkit", newField: "skia_enable_cpu_rendering"}, | 80 | + | legacyEngineSameTarget("performance", "web_process_memory_conservative_threshold", engineWebKitTargetSection), |
| 57 | - | {oldSection: "performance", oldField: "web_process_memory_limit_mb", | 81 | + | legacyEngineSameTarget("performance", "web_process_memory_strict_threshold", engineWebKitTargetSection), |
| 58 | - | newSection: "engine.webkit", newField: "web_process_memory_limit_mb"}, | 82 | + | legacyEngineSameTarget("performance", "network_process_memory_limit_mb", engineWebKitTargetSection), |
| 59 | - | {oldSection: "performance", oldField: "web_process_memory_poll_interval_sec", | 83 | + | legacyEngineSameTarget("performance", "network_process_memory_poll_interval_sec", engineWebKitTargetSection), |
| 60 | - | newSection: "engine.webkit", newField: "web_process_memory_poll_interval_sec"}, | 84 | + | legacyEngineSameTarget("performance", "network_process_memory_conservative_threshold", engineWebKitTargetSection), |
| 61 | - | {oldSection: "performance", oldField: "web_process_memory_conservative_threshold", | 85 | + | legacyEngineSameTarget("performance", "network_process_memory_strict_threshold", engineWebKitTargetSection), |
| 62 | - | newSection: "engine.webkit", newField: "web_process_memory_conservative_threshold"}, | |||
| 63 | - | {oldSection: "performance", oldField: "web_process_memory_strict_threshold", | |||
| 64 | - | newSection: "engine.webkit", newField: "web_process_memory_strict_threshold"}, | |||
| 65 | - | {oldSection: "performance", oldField: "network_process_memory_limit_mb", | |||
| 66 | - | newSection: "engine.webkit", newField: "network_process_memory_limit_mb"}, | |||
| 67 | - | {oldSection: "performance", oldField: "network_process_memory_poll_interval_sec", | |||
| 68 | - | newSection: "engine.webkit", newField: "network_process_memory_poll_interval_sec"}, | |||
| 69 | - | {oldSection: "performance", oldField: "network_process_memory_conservative_threshold", | |||
| 70 | - | newSection: "engine.webkit", newField: "network_process_memory_conservative_threshold"}, | |||
| 71 | - | {oldSection: "performance", oldField: "network_process_memory_strict_threshold", | |||
| 72 | - | newSection: "engine.webkit", newField: "network_process_memory_strict_threshold"}, | |||
| 73 | 86 | ||||
| 74 | // [media] GStreamer fields -> [engine.webkit] | 87 | // [media] GStreamer fields -> [engine.webkit] | ||
| 75 | - | {oldSection: "media", oldField: "force_vsync", newSection: "engine.webkit", newField: "force_vsync"}, | 88 | + | legacyEngineSameTarget("media", "force_vsync", engineWebKitTargetSection), |
| 76 | - | {oldSection: "media", oldField: "gl_rendering_mode", newSection: "engine.webkit", newField: "gl_rendering_mode"}, | 89 | + | legacyEngineSameTarget("media", "gl_rendering_mode", engineWebKitTargetSection), |
| 77 | - | {oldSection: "media", oldField: "gstreamer_debug_level", newSection: "engine.webkit", newField: "gstreamer_debug_level"}, | 90 | + | legacyEngineSameTarget("media", "gstreamer_debug_level", engineWebKitTargetSection), |
| 78 | 91 | ||||
| 79 | // [runtime] -> [engine.webkit] | 92 | // [runtime] -> [engine.webkit] | ||
| 80 | - | {oldSection: "runtime", oldField: "prefix", newSection: "engine.webkit", newField: "prefix"}, | 93 | + | legacyEngineSameTarget("runtime", "prefix", engineWebKitTargetSection), |
| 94 | + | } | |||
| 95 | + | ||||
| 96 | + | func legacyEngineAliasesForSection(section string) []legacyEngineAlias { | |||
| 97 | + | aliases := make([]legacyEngineAlias, 0) | |||
| 98 | + | for _, alias := range legacyEngineAliases { | |||
| 99 | + | if alias.legacySection == section { | |||
| 100 | + | aliases = append(aliases, alias) | |||
| 101 | + | } | |||
| 102 | + | } | |||
| 103 | + | return aliases | |||
| 104 | + | } | |||
| 105 | + | ||||
| 106 | + | func legacyEngineSectionsToRemove() []string { | |||
| 107 | + | sections := make([]string, 0) | |||
| 108 | + | seen := make(map[string]struct{}) | |||
| 109 | + | for _, alias := range legacyEngineAliases { | |||
| 110 | + | if alias.legacySection == "media" { | |||
| 111 | + | continue | |||
| 112 | + | } | |||
| 113 | + | if _, ok := seen[alias.legacySection]; ok { | |||
| 114 | + | continue | |||
| 115 | + | } | |||
| 116 | + | seen[alias.legacySection] = struct{}{} | |||
| 117 | + | sections = append(sections, alias.legacySection) | |||
| 118 | + | } | |||
| 119 | + | return sections | |||
| 81 | } | 120 | } | ||
| 82 | 121 | ||||
| 83 | - | // oldSectionsToRemove are the sections that are fully removed after migration | 122 | + | func hasLegacyEngineAlias(raw map[string]any, alias legacyEngineAlias) bool { |
| 84 | - | // (media is not here because it has fields that stay). | 123 | + | sectionAny, exists := raw[alias.legacySection] |
| 85 | - | var oldSectionsToRemove = []string{"rendering", "performance", "privacy", "runtime"} | 124 | + | if !exists { |
| 125 | + | return false | |||
| 126 | + | } | |||
| 127 | + | sectionMap, ok := sectionAny.(map[string]any) | |||
| 128 | + | if !ok { | |||
| 129 | + | return false | |||
| 130 | + | } | |||
| 131 | + | _, exists = sectionMap[alias.legacyField] | |||
| 132 | + | return exists | |||
| 133 | + | } | |||
| 86 | 134 | ||||
| 87 | // MigrateToEngineConfig restructures old top-level config sections into [engine]/[engine.webkit]. | 135 | // MigrateToEngineConfig restructures old top-level config sections into [engine]/[engine.webkit]. | ||
| 88 | // Returns true if migration was performed, false if already migrated or no old sections found. | 136 | // Returns true if migration was performed, false if already migrated or no old sections found. | ||
| @@ -93,7 +141,7 @@ func MigrateToEngineConfig(configFile string) (bool, error) { | |||||
| 93 | } | 141 | } | ||
| 94 | 142 | ||||
| 95 | if _, exists := raw["engine"]; exists { | 143 | if _, exists := raw["engine"]; exists { | ||
| 96 | - | for _, section := range []string{"rendering", "performance", "privacy", "runtime"} { | 144 | + | for _, section := range legacyEngineSectionsToRemove() { |
| 97 | if _, hasLegacy := raw[section]; hasLegacy { | 145 | if _, hasLegacy := raw[section]; hasLegacy { | ||
| 98 | return false, fmt.Errorf( | 146 | return false, fmt.Errorf( | ||
| 99 | "mixed old/new config: [engine] coexists with legacy [%s]; "+ | 147 | "mixed old/new config: [engine] coexists with legacy [%s]; "+ | ||
| @@ -102,16 +150,13 @@ func MigrateToEngineConfig(configFile string) (bool, error) { | |||||
| 102 | ) | 150 | ) | ||
| 103 | } | 151 | } | ||
| 104 | } | 152 | } | ||
| 105 | - | // Check for deprecated media keys that moved to [engine.webkit]. | 153 | + | for _, alias := range legacyEngineAliasesForSection("media") { |
| 106 | - | if mediaRaw, ok := raw["media"].(map[string]any); ok { | 154 | + | if hasLegacyEngineAlias(raw, alias) { |
| 107 | - | for _, key := range []string{"force_vsync", "gl_rendering_mode", "gstreamer_debug_level"} { | 155 | + | return false, fmt.Errorf( |
| 108 | - | if _, has := mediaRaw[key]; has { | 156 | + | "mixed old/new config: [engine] coexists with deprecated "+ |
| 109 | - | return false, fmt.Errorf( | 157 | + | "media.%s; remove it from [media] (now in [engine.webkit])", |
| 110 | - | "mixed old/new config: [engine] coexists with deprecated "+ | 158 | + | alias.legacyField, |
| 111 | - | "media.%s; remove it from [media] (now in [engine.webkit])", | 159 | + | ) |
| 112 | - | key, | |||
| 113 | - | ) | |||
| 114 | - | } | |||
| 115 | } | 160 | } | ||
| 116 | } | 161 | } | ||
| 117 | return false, nil // already migrated | 162 | return false, nil // already migrated | ||
| @@ -150,18 +195,14 @@ func readRawTOML(path string) (map[string]any, error) { | |||||
| 150 | 195 | ||||
| 151 | // hasOldEngineSections checks if the raw config has old sections that need migration. | 196 | // hasOldEngineSections checks if the raw config has old sections that need migration. | ||
| 152 | func hasOldEngineSections(raw map[string]any) bool { | 197 | func hasOldEngineSections(raw map[string]any) bool { | ||
| 153 | - | for _, section := range []string{"rendering", "performance", "privacy", "runtime"} { | 198 | + | for _, section := range legacyEngineSectionsToRemove() { |
| 154 | if _, exists := raw[section]; exists { | 199 | if _, exists := raw[section]; exists { | ||
| 155 | return true | 200 | return true | ||
| 156 | } | 201 | } | ||
| 157 | } | 202 | } | ||
| 158 | - | if mediaAny, exists := raw["media"]; exists { | 203 | + | for _, alias := range legacyEngineAliasesForSection("media") { |
| 159 | - | if media, ok := mediaAny.(map[string]any); ok { | 204 | + | if hasLegacyEngineAlias(raw, alias) { |
| 160 | - | for _, f := range []string{"force_vsync", "gl_rendering_mode", "gstreamer_debug_level"} { | 205 | + | return true |
| 161 | - | if _, has := media[f]; has { | |||
| 162 | - | return true | |||
| 163 | - | } | |||
| 164 | - | } | |||
| 165 | } | 206 | } | ||
| 166 | } | 207 | } | ||
| 167 | return false | 208 | return false | ||
| @@ -172,14 +213,19 @@ func applyEngineMappings(raw map[string]any) { | |||||
| 172 | engineMap := make(map[string]any) | 213 | engineMap := make(map[string]any) | ||
| 173 | webkitMap := make(map[string]any) | 214 | webkitMap := make(map[string]any) | ||
| 174 | 215 | ||||
| 175 | - | for _, m := range engineUniversalMappings { | 216 | + | for _, alias := range legacyEngineAliases { |
| 176 | - | if val := getSectionField(raw, m.oldSection, m.oldField); val != nil { | 217 | + | if alias.dropped { |
| 177 | - | engineMap[m.newField] = val | 218 | + | continue |
| 178 | } | 219 | } | ||
| 179 | - | } | 220 | + | val := getSectionField(raw, alias.legacySection, alias.legacyField) |
| 180 | - | for _, m := range engineWebkitMappings { | 221 | + | if val == nil { |
| 181 | - | if val := getSectionField(raw, m.oldSection, m.oldField); val != nil { | 222 | + | continue |
| 182 | - | webkitMap[m.newField] = val | 223 | + | } |
| 224 | + | switch alias.targetSection { | |||
| 225 | + | case "engine": | |||
| 226 | + | engineMap[alias.targetField] = val | |||
| 227 | + | case "engine.webkit": | |||
| 228 | + | webkitMap[alias.targetField] = val | |||
| 183 | } | 229 | } | ||
| 184 | } | 230 | } | ||
| 185 | 231 | ||||
| @@ -189,14 +235,14 @@ func applyEngineMappings(raw map[string]any) { | |||||
| 189 | } | 235 | } | ||
| 190 | raw["engine"] = engineMap | 236 | raw["engine"] = engineMap | ||
| 191 | 237 | ||||
| 192 | - | for _, section := range oldSectionsToRemove { | 238 | + | for _, section := range legacyEngineSectionsToRemove() { |
| 193 | delete(raw, section) | 239 | delete(raw, section) | ||
| 194 | } | 240 | } | ||
| 195 | 241 | ||||
| 196 | stripMigratedMediaFields(raw) | 242 | stripMigratedMediaFields(raw) | ||
| 197 | } | 243 | } | ||
| 198 | 244 | ||||
| 199 | - | // stripMigratedMediaFields removes GStreamer fields from [media] while keeping the rest. | 245 | + | // stripMigratedMediaFields removes migrated fields from [media] while keeping the rest. |
| 200 | func stripMigratedMediaFields(raw map[string]any) { | 246 | func stripMigratedMediaFields(raw map[string]any) { | ||
| 201 | mediaAny, exists := raw["media"] | 247 | mediaAny, exists := raw["media"] | ||
| 202 | if !exists { | 248 | if !exists { | ||
| @@ -206,9 +252,9 @@ func stripMigratedMediaFields(raw map[string]any) { | |||||
| 206 | if !ok { | 252 | if !ok { | ||
| 207 | return | 253 | return | ||
| 208 | } | 254 | } | ||
| 209 | - | delete(media, "force_vsync") | 255 | + | for _, alias := range legacyEngineAliasesForSection("media") { |
| 210 | - | delete(media, "gl_rendering_mode") | 256 | + | delete(media, alias.legacyField) |
| 211 | - | delete(media, "gstreamer_debug_level") | 257 | + | } |
| 212 | if len(media) == 0 { | 258 | if len(media) == 0 { | ||
| 213 | delete(raw, "media") | 259 | delete(raw, "media") | ||
| 214 | } | 260 | } | ||
M internal/infrastructure/config/migrate_engine_test.go +71 -0
| @@ -253,6 +253,77 @@ disable_dmabuf_renderer = false | |||
| 253 | 253 | assert.Equal(t, int64(3), webkit["gstreamer_debug_level"]) | |
| 254 | 254 | } | |
| 255 | 255 | ||
| 256 | + | func TestLegacyEngineAliasesMetadataCoverage(t *testing.T) { | |
| 257 | + | require.NotEmpty(t, legacyEngineAliases) | |
| 258 | + | ||
| 259 | + | aliases := make(map[string]legacyEngineAlias) | |
| 260 | + | for _, alias := range legacyEngineAliases { | |
| 261 | + | key := alias.legacyKey() | |
| 262 | + | require.NotEmpty(t, alias.legacySection, "%s should have a legacy section", key) | |
| 263 | + | require.NotEmpty(t, alias.legacyField, "%s should have a legacy field", key) | |
| 264 | + | require.NotContains(t, aliases, key, "duplicate metadata for %s", key) | |
| 265 | + | aliases[key] = alias | |
| 266 | + | ||
| 267 | + | if alias.dropped { | |
| 268 | + | assert.Empty(t, alias.targetSection, "%s is dropped and should not have a target section", key) | |
| 269 | + | assert.Empty(t, alias.targetField, "%s is dropped and should not have a target field", key) | |
| 270 | + | continue | |
| 271 | + | } | |
| 272 | + | ||
| 273 | + | require.Contains(t, []string{"engine", "engine.webkit"}, alias.targetSection, "%s should target a known engine section", key) | |
| 274 | + | require.NotEmpty(t, alias.targetField, "%s should have a canonical target field", key) | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | for _, key := range []string{ | |
| 278 | + | "media.force_vsync", | |
| 279 | + | "media.gl_rendering_mode", | |
| 280 | + | "media.gstreamer_debug_level", | |
| 281 | + | "runtime.prefix", | |
| 282 | + | "performance.profile", | |
| 283 | + | "privacy.cookie_policy", | |
| 284 | + | "privacy.itp_enabled", | |
| 285 | + | "rendering.disable_dmabuf_renderer", | |
| 286 | + | } { | |
| 287 | + | alias, ok := aliases[key] | |
| 288 | + | require.True(t, ok, "missing metadata for %s", key) | |
| 289 | + | require.False(t, alias.dropped, "%s should have a canonical target", key) | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | renderingMode, ok := aliases["rendering.mode"] | |
| 293 | + | require.True(t, ok, "missing dropped metadata for rendering.mode") | |
| 294 | + | assert.True(t, renderingMode.dropped) | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | func TestHasOldEngineSections_UsesMetadataForMigratedMediaKeys(t *testing.T) { | |
| 298 | + | for _, field := range []string{"force_vsync", "gl_rendering_mode", "gstreamer_debug_level"} { | |
| 299 | + | raw := map[string]any{"media": map[string]any{field: true}} | |
| 300 | + | assert.True(t, hasOldEngineSections(raw), "media.%s should trigger engine migration", field) | |
| 301 | + | } | |
| 302 | + | ||
| 303 | + | raw := map[string]any{"media": map[string]any{"hardware_decoding": true}} | |
| 304 | + | assert.False(t, hasOldEngineSections(raw), "non-migrated media keys should not trigger engine migration") | |
| 305 | + | } | |
| 306 | + | ||
| 307 | + | func TestMigrateToEngineConfig_MixedEngineAndMigratedMediaKey(t *testing.T) { | |
| 308 | + | tmpDir := t.TempDir() | |
| 309 | + | configFile := filepath.Join(tmpDir, "config.toml") | |
| 310 | + | ||
| 311 | + | content := ` | |
| 312 | + | [engine] | |
| 313 | + | type = "webkit" | |
| 314 | + | ||
| 315 | + | [media] | |
| 316 | + | gl_rendering_mode = "auto" | |
| 317 | + | ` | |
| 318 | + | err := os.WriteFile(configFile, []byte(content), 0o644) | |
| 319 | + | require.NoError(t, err) | |
| 320 | + | ||
| 321 | + | migrated, err := MigrateToEngineConfig(configFile) | |
| 322 | + | require.Error(t, err) | |
| 323 | + | assert.False(t, migrated) | |
| 324 | + | assert.Contains(t, err.Error(), "media.gl_rendering_mode") | |
| 325 | + | } | |
| 326 | + | ||
| 256 | 327 | func TestMigrateToEngineConfig_EngineTypeDefault(t *testing.T) { | |
| 257 | 328 | tmpDir := t.TempDir() | |
| 258 | 329 | configFile := filepath.Join(tmpDir, "config.toml") | |
| @@ -253,6 +253,77 @@ disable_dmabuf_renderer = false | |||||
| 253 | assert.Equal(t, int64(3), webkit["gstreamer_debug_level"]) | 253 | assert.Equal(t, int64(3), webkit["gstreamer_debug_level"]) | ||
| 254 | } | 254 | } | ||
| 255 | 255 | ||||
| 256 | + | func TestLegacyEngineAliasesMetadataCoverage(t *testing.T) { | |||
| 257 | + | require.NotEmpty(t, legacyEngineAliases) | |||
| 258 | + | ||||
| 259 | + | aliases := make(map[string]legacyEngineAlias) | |||
| 260 | + | for _, alias := range legacyEngineAliases { | |||
| 261 | + | key := alias.legacyKey() | |||
| 262 | + | require.NotEmpty(t, alias.legacySection, "%s should have a legacy section", key) | |||
| 263 | + | require.NotEmpty(t, alias.legacyField, "%s should have a legacy field", key) | |||
| 264 | + | require.NotContains(t, aliases, key, "duplicate metadata for %s", key) | |||
| 265 | + | aliases[key] = alias | |||
| 266 | + | ||||
| 267 | + | if alias.dropped { | |||
| 268 | + | assert.Empty(t, alias.targetSection, "%s is dropped and should not have a target section", key) | |||
| 269 | + | assert.Empty(t, alias.targetField, "%s is dropped and should not have a target field", key) | |||
| 270 | + | continue | |||
| 271 | + | } | |||
| 272 | + | ||||
| 273 | + | require.Contains(t, []string{"engine", "engine.webkit"}, alias.targetSection, "%s should target a known engine section", key) | |||
| 274 | + | require.NotEmpty(t, alias.targetField, "%s should have a canonical target field", key) | |||
| 275 | + | } | |||
| 276 | + | ||||
| 277 | + | for _, key := range []string{ | |||
| 278 | + | "media.force_vsync", | |||
| 279 | + | "media.gl_rendering_mode", | |||
| 280 | + | "media.gstreamer_debug_level", | |||
| 281 | + | "runtime.prefix", | |||
| 282 | + | "performance.profile", | |||
| 283 | + | "privacy.cookie_policy", | |||
| 284 | + | "privacy.itp_enabled", | |||
| 285 | + | "rendering.disable_dmabuf_renderer", | |||
| 286 | + | } { | |||
| 287 | + | alias, ok := aliases[key] | |||
| 288 | + | require.True(t, ok, "missing metadata for %s", key) | |||
| 289 | + | require.False(t, alias.dropped, "%s should have a canonical target", key) | |||
| 290 | + | } | |||
| 291 | + | ||||
| 292 | + | renderingMode, ok := aliases["rendering.mode"] | |||
| 293 | + | require.True(t, ok, "missing dropped metadata for rendering.mode") | |||
| 294 | + | assert.True(t, renderingMode.dropped) | |||
| 295 | + | } | |||
| 296 | + | ||||
| 297 | + | func TestHasOldEngineSections_UsesMetadataForMigratedMediaKeys(t *testing.T) { | |||
| 298 | + | for _, field := range []string{"force_vsync", "gl_rendering_mode", "gstreamer_debug_level"} { | |||
| 299 | + | raw := map[string]any{"media": map[string]any{field: true}} | |||
| 300 | + | assert.True(t, hasOldEngineSections(raw), "media.%s should trigger engine migration", field) | |||
| 301 | + | } | |||
| 302 | + | ||||
| 303 | + | raw := map[string]any{"media": map[string]any{"hardware_decoding": true}} | |||
| 304 | + | assert.False(t, hasOldEngineSections(raw), "non-migrated media keys should not trigger engine migration") | |||
| 305 | + | } | |||
| 306 | + | ||||
| 307 | + | func TestMigrateToEngineConfig_MixedEngineAndMigratedMediaKey(t *testing.T) { | |||
| 308 | + | tmpDir := t.TempDir() | |||
| 309 | + | configFile := filepath.Join(tmpDir, "config.toml") | |||
| 310 | + | ||||
| 311 | + | content := ` | |||
| 312 | + | [engine] | |||
| 313 | + | type = "webkit" | |||
| 314 | + | ||||
| 315 | + | [media] | |||
| 316 | + | gl_rendering_mode = "auto" | |||
| 317 | + | ` | |||
| 318 | + | err := os.WriteFile(configFile, []byte(content), 0o644) | |||
| 319 | + | require.NoError(t, err) | |||
| 320 | + | ||||
| 321 | + | migrated, err := MigrateToEngineConfig(configFile) | |||
| 322 | + | require.Error(t, err) | |||
| 323 | + | assert.False(t, migrated) | |||
| 324 | + | assert.Contains(t, err.Error(), "media.gl_rendering_mode") | |||
| 325 | + | } | |||
| 326 | + | ||||
| 256 | func TestMigrateToEngineConfig_EngineTypeDefault(t *testing.T) { | 327 | func TestMigrateToEngineConfig_EngineTypeDefault(t *testing.T) { | ||
| 257 | tmpDir := t.TempDir() | 328 | tmpDir := t.TempDir() | ||
| 258 | configFile := filepath.Join(tmpDir, "config.toml") | 329 | configFile := filepath.Join(tmpDir, "config.toml") | ||