Skip to content

fix(uri): preserve percent escapes in plain local paths - #4303

Open
AndreyGatsuk wants to merge 1 commit into
noctalia-dev:mainfrom
AndreyGatsuk:fix/uri-literal-percent-paths
Open

fix(uri): preserve percent escapes in plain local paths#4303
AndreyGatsuk wants to merge 1 commit into
noctalia-dev:mainfrom
AndreyGatsuk:fix/uri-literal-percent-paths

Conversation

@AndreyGatsuk

Copy link
Copy Markdown

Summary

Only percent-decode file:// inputs in uri::normalizeFileUrl, preserving plain filesystem paths and icon names verbatim. Add a regression test and register it with Meson.

The change applies to all four consumers of this shared helper:

  • Notification toast icons.
  • Icons displayed in notification history.
  • Notification image snapshots loaded from image-path / image_path hints.
  • MPRIS artwork resolution.

Motivation

Noctalia already supports absolute filesystem paths for notification icons. The existing notification test script explicitly exercises both /usr/share/pixmaps/steam.png and file:///usr/share/pixmaps/steam.png.

The notification service forwards icon values without URI encoding. The toast icon resolver then normalizes them and uses the resulting absolute path for filesystem access.

RFC 3986 §2.1 defines percent-encoding for URI components, and RFC 8089 §4 describes its use when representing filesystem names as file URIs. Native Linux pathnames follow the byte-based rules documented in pathname(7); %20 in a native filename is a literal sequence, not an encoded space.

Previously, /tmp/icon%20name.png was incorrectly converted to /tmp/icon name.png, potentially selecting a different file or failing to find the requested image. This fix preserves literal percent sequences in plain paths while retaining decoding for file:// inputs.

Type of Change

  • Bug fix

Testing

The uri regression test fails with the original URI implementation and passes with the fix. It covers:

  • Normalization edge cases, including literal %2F and %00, icon names, localhost file URIs, lowercase hex escapes, empty input, and HTTP(S) rejection.
  • MPRIS artwork resolution followed by decoding the selected file and checking its pixels.
  • Notification ingestion with both image-path and image_path, checking the captured pixels.
  • Saving and restoring notification history after removing the source images. Restored colors allow a small tolerance because history uses lossy WebP compression.

Two distinct PNG files ensure that accidentally selecting the filename containing a space cannot pass as a successful load. Both native paths and encoded file URIs are exercised. A separate filesystem reproduction also confirmed that the original code opened the wrong file, while the fixed code opened the requested file.

Completed local CI checks:

  • just format with clang-format 22.1.8.
  • just build: debug build passed.
  • just test debug --print-errorlogs: all 113 tests passed, including uri.
  • just lint: all 572 production translation units passed.
  • clang-tidy -p build-debug tests/uri_test.cpp --warnings-as-errors='*': passed.
  • git diff --check: passed.

All four affected consumers were also exercised with the fixed debug build on Niri 26.04 running as a nested Wayland compositor, using isolated configuration and private D-Bus sessions:

  1. Notification toasts: icon values were sent directly through D-Bus in the app_icon argument.
  2. Notification history: those toasts were dismissed and their icons were checked in the history panel.
  3. Notification image-path: PNG paths were sent in the hint with an empty app_icon. The original PNG files were then deleted before capturing the displayed notifications.
  4. MPRIS artwork: a test D-Bus player exported each input as mpris:artUrl and emitted metadata changes. The artwork was checked in Noctalia's media panel.

Each visual check used two distinct image files: a green check and a red cross. All three inputs produced the expected result:

Input Expected and observed image
Native path containing literal %20 Green check
File URI containing %2520 Green check
File URI containing %20 Red cross from the filename containing a space

Manual Coverage

  • Tested on Niri
  • Tested on Hyprland
  • Tested on Sway
  • Tested on another compositor:
  • Tested with different bar positions and density settings
  • Tested at different interface scaling values
  • Tested with multiple monitors

Screenshots / Videos

1. Notification toasts (app_icon)

Notification toasts with the fixed build

2. Notification history

The same notifications after dismissing the toasts, shown newest first.

02-notification-history

3. Notification image-path

Notifications sent with an empty app_icon and an image-path hint. Source PNG files were removed after notification ingestion.

03-image-path

4. MPRIS artwork

Three cropped captures from the media panel, placed side by side in input order. The captures show sequential states of the same test player.

04-mpris

The red cross is the expected test image for the filename containing a space. Screenshots are crops of the actual Noctalia session.

Checklist

  • This PR is ready for review, or it is marked as Draft.
  • I read and followed the relevant guidance in CONTRIBUTING.md.
  • I ran just format with clang-format v22+ installed, or this PR has no code changes.
  • I ran the relevant build or test commands, or explained why they were not run.
  • I self-reviewed the changes.
  • I checked for new warnings or errors.
  • I updated user-facing documentation in docs/user/ when this PR changes documented behavior or configuration, or this PR does not require documentation changes.
  • I added or updated assets/translations/en.json, or this PR adds no new user-facing strings.
  • I did not edit non-English translation files unless this PR is explicitly for translation tooling, an import/export sync, or a maintainer-requested locale change.
  • I used the existing canonical names for config keys, IPC names, paths, and identifiers.

Decode percent sequences only for file:// inputs. Preserve native paths
and icon names so literal percent sequences cannot select another file.

Add one URI regression test covering normalization edge cases, decoded
MPRIS artwork, notification image-path ingestion, and history persistence.
Register it with Meson.

Validated with just format, the debug build, all 113 tests, just lint,
and clang-tidy for the expanded test. Verified notification toasts,
history, image-path snapshots, and MPRIS artwork in nested Niri.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant