summaryrefslogtreecommitdiffstats
path: root/docs/superpowers/plans
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-20 10:26:32 +0200
committerDanilo M. <danix@danix.xyz>2026-08-20 10:26:32 +0200
commitc1e150b08360ff312732528fefb8bbc6cc7db588 (patch)
tree2fc94c4b8b0dcb95bdb0e22dac3238712b2278a6 /docs/superpowers/plans
parent694ff89b51873f4edf3328b639f421d8e0178dc9 (diff)
downloadqtmaildir-c1e150b08360ff312732528fefb8bbc6cc7db588.tar.gz
qtmaildir-c1e150b08360ff312732528fefb8bbc6cc7db588.zip
docs: record the dead link click and its context menu
Two defects found by hand, related but separate. 126: clicking a link in a message does nothing. The handler is already there and correct, calling QDesktopServices::openUrl from acceptNavigationRequest, and it has presumably never run. RequestInterceptor denies http and https whenever m_allowRemote is false, which is the default for every message, and it runs on the request before the page is asked whether to accept the navigation. The click is dropped at the network layer with no error, no navigation and no browser. That is the remote-content protection working as designed; the bug is that a deliberate click is indistinguishable from a resource the document fetched itself, at the layer where the decision is currently made. 127: a link's context menu still offers Open in new tab, Open in new window, Save link and Copy link. Item 100 removed the page-level actions and its list names four of them; the link actions are different WebAction values that Chromium adds only over a link, so item 100 never saw them. Two are dead (there are no tabs, and a second view is deliberately never created), one belongs with item 114's missing download handler, and Copy link works and is currently the user's whole workaround for 126. It follows 126, since a working click makes one "Open link" entry the right answer rather than a removal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDq53rMd3AQp7QmcZzpuBM
Diffstat (limited to 'docs/superpowers/plans')
-rw-r--r--docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md118
1 files changed, 118 insertions, 0 deletions
diff --git a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
index 8129704..2227cc6 100644
--- a/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
+++ b/docs/superpowers/plans/2026-08-03-post-0.1.0-usability.md
@@ -195,6 +195,9 @@ taking that too literally.
| 125 | A skipped sync leaves the spinner running for ever | defect | S | open, 2026-08-20, found by hand. `mailsync.sh` exits 75 (EX_TEMPFAIL) when another run holds the lock; the indicator never clears, and a held edit waits for a completion that never comes |
+| 126 | Clicking a link in a message does nothing | defect | S | open, 2026-08-20. The interceptor blocks the request before `acceptNavigationRequest` can hand it to the browser, so the `QDesktopServices::openUrl` already in the code is never reached |
+| 127 | A link's context menu offers four browser actions that cannot work | defect | XS | open, 2026-08-20. Item 100 removed the page-level actions and never saw these: they appear only on a link. Follows 126, which decides which of them should survive |
+
Sizes are rough: XS under an hour, S a sitting, M a session.
---
@@ -1053,6 +1056,121 @@ Then Delete a message. Verified by hand on 2026-08-20; this is how it was found.
**Size: S.**
+## 126. Clicking a link in a message does nothing
+
+**Observed (user, 2026-08-20):** "if I click on a link in a mail in the message
+pane, nothing happens. The only way now is to copy that link and paste it in a
+browser."
+
+**Cause (verified in the code, and it is not the missing handler it looks
+like).** `MessagePage::acceptNavigationRequest` (`src/messageview.cpp:89-109`)
+already handles this correctly: a `NavigationTypeLinkClicked` is passed to
+`QDesktopServices::openUrl(url)` and then refused as a navigation, so the pane
+cannot follow it. That code is right and has presumably never run.
+
+**`RequestInterceptor` blocks the request first.** `shouldAllow()`
+(`src/requestinterceptor.cpp:73-77`) denies `http` and `https` whenever
+`m_allowRemote` is false, which is the default for every message and is reset
+per message by `resetForNewMessage()`. The interceptor runs on the REQUEST,
+before the page is asked whether to accept the navigation, so the click is
+dropped at the network layer and `acceptNavigationRequest` is never consulted.
+The user sees nothing at all: no error, no navigation, no browser.
+
+**This is the remote-content protection working as designed**, and the fix must
+not weaken it. Blocking `https` is what stops a tracking pixel or a remote
+stylesheet phoning home when a message is merely displayed. The bug is that a
+deliberate CLICK is indistinguishable, at that layer, from a resource the
+document fetched on its own.
+
+**Approach.** The distinction the interceptor cannot make is available one level
+up: a click arrives as `ResourceTypeMainFrame` with a `NavigationTypeLinkClicked`,
+while a tracking pixel arrives as `ResourceTypeImage` and never as a navigation
+at all. Two shapes are worth considering, and the choice is the user's:
+
+- Let the main-frame navigation through the interceptor so
+ `acceptNavigationRequest` can see it, hand it to `QDesktopServices::openUrl`,
+ and still refuse the navigation. Nothing is ever fetched by the pane; the URL
+ only reaches the external browser.
+- Or intercept the click before it becomes a request at all, which avoids
+ relaxing anything in the interceptor but needs the link target from the
+ context-menu data or a `linkHovered` cache.
+
+**Constraints.**
+
+- **`m_allowRemote` must stay false by default**, and this must not become a
+ reason to loosen it. Opening a link in an external browser is the user's
+ explicit act; fetching a resource into the pane is not.
+- **The pane must still never navigate.** `acceptNavigationRequest` returning
+ false for a link click is what protects that, and it stays whatever else
+ changes.
+- **A `mailto:` link is a separate question** and probably belongs to item 123,
+ since composing is v2. Until then it should behave like any other scheme
+ rather than being special-cased into silence.
+- **`qtmaildir-query:` links must keep their existing path.** They are the
+ placeholder's own helper lines and are handled before the `openUrl` call;
+ a message body carrying one is already refused by the view.
+
+**Verification.** Not testable through the offscreen platform end to end, since
+it ends in an external browser. The reachable assertion is that a link click
+reaches `acceptNavigationRequest` at all, which is what fails today; the
+`openUrl` call itself is one line beyond that and is better confirmed by hand.
+
+**Size: S.**
+
+## 127. A link's context menu offers four browser actions that cannot work
+
+**Observed (user, 2026-08-20):** right-clicking a link still offers "Open in new
+tab", "open in new window", "save link", "copy link" and "select all". The user
+identified them as probable survivors of an earlier removal, which is exactly
+what they are.
+
+**Cause (verified).** Item 100 removed the page-level browser actions, and its
+list is explicit (`src/messageview.cpp:689-694`): `Back`, `Forward`, `Reload`,
+`SavePage`. Those four are what a standard menu offers on the PAGE. The
+link-specific actions are different `WebAction` values entirely
+(`QWebEnginePage::OpenLinkInNewTab`, `OpenLinkInNewWindow`, `DownloadLinkToDisk`,
+`CopyLinkToClipboard`), and Chromium adds them only when the menu is raised over
+a link. Item 100 was tested by right-clicking the page, so they were never in
+the menu it was filtering and were never considered.
+
+**Two of them are dead and two are not**, which is why this is not a single
+sweep:
+
+- `OpenLinkInNewTab` and `OpenLinkInNewWindow` cannot work at all. There are no
+ tabs, and a new window means a second `QWebEngineView`, which the pane
+ deliberately does not create (one Chromium render process per message is the
+ reason the pane renders a list into one view). Both are dead UI today.
+- `DownloadLinkToDisk` needs a `downloadRequested` handler, which item 114
+ records does not exist anywhere. It is dead for the same reason Save image is,
+ and should be decided WITH item 114 rather than separately.
+- `CopyLinkToClipboard` works and is useful. It is currently the user's entire
+ workaround for item 126, by their own description.
+
+**Approach.** Remove `OpenLinkInNewTab` and `OpenLinkInNewWindow` by pointer,
+the way `removeBrowserActions()` already does, so the removal survives
+translation. Keep `CopyLinkToClipboard`. Leave `DownloadLinkToDisk` to item 114.
+
+**This is downstream of item 126 and should follow it.** If 126 makes a click
+open the system browser, then "Open link" becomes a meaningful entry and the
+right menu is one entry that does what the click does, rather than two Chromium
+entries that do nothing. Removing them first and adding one back afterwards is
+two changes to the same menu.
+
+**Constraints.**
+
+- **Filter by `pageAction()` POINTER, never by text.** Item 100 established
+ this and the reason is translation: the menu is Italian under `LANG=it_IT`
+ and a text match would silently stop matching.
+- **Stranded separators must still be swept**, which `removeBrowserActions()`
+ already handles; removing two adjacent entries is exactly the case that
+ leaves one behind.
+- **The call site cannot be tested**, per item 117: `createStandardContextMenu()`
+ returns nothing outside a real context-menu event. Assert on the filter
+ function against a menu built by hand, and state the gap rather than faking
+ coverage.
+
+**Size: XS**, and smaller still if done in the same sitting as 126.
+
## Deferred, unsized, or split out
Items noted while triaging but not part of the original list. Same numbering