mirror of
https://github.com/NotAShelf/stash.git
synced 2026-04-12 22:17:41 +00:00
watch: deprioritize text/html in MIME negotiation
Firefox and Electron apps offer `text/html` first when copying images, which causes stash to store the HTML wrapper (`<img src="...">`) instead of the actual image data, which is what we want. We handicap, i.e., deprioritize `text/html` in the "any" preference mode and prefer `image/*` types first, then any non-html type. This sounds a little illogical, but in user will almost always prefer the image itself rather than the text representation. So it's intuitive. Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6bd5969344893e15226c27071442475f6a6a6964
This commit is contained in:
parent
3fd48896c1
commit
9afbe9ceca
1 changed files with 58 additions and 5 deletions
|
|
@ -140,7 +140,26 @@ fn negotiate_mime_type(
|
||||||
.find(|m| m.starts_with("image/"))
|
.find(|m| m.starts_with("image/"))
|
||||||
.or_else(|| offered.first())
|
.or_else(|| offered.first())
|
||||||
} else {
|
} else {
|
||||||
offered.first()
|
// XXX: When preference is "any", deprioritize text/html if a more
|
||||||
|
// concrete type is available. Browsers and Electron apps put
|
||||||
|
// text/html first even for "Copy Image", but the HTML is just
|
||||||
|
// a wrapper (<img src="...">), i.e., never what the user wants in a
|
||||||
|
// clipboard manager. Prefer image/* first, then any non-html
|
||||||
|
// type, and fall back to text/html only as a last resort.
|
||||||
|
let has_image = offered.iter().any(|m| m.starts_with("image/"));
|
||||||
|
if has_image {
|
||||||
|
offered
|
||||||
|
.iter()
|
||||||
|
.find(|m| m.starts_with("image/"))
|
||||||
|
.or_else(|| offered.first())
|
||||||
|
} else if offered.first().is_some_and(|m| m == "text/html") {
|
||||||
|
offered
|
||||||
|
.iter()
|
||||||
|
.find(|m| *m != "text/html")
|
||||||
|
.or_else(|| offered.first())
|
||||||
|
} else {
|
||||||
|
offered.first()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match chosen {
|
match chosen {
|
||||||
|
|
@ -392,7 +411,20 @@ fn pick_mime<'a>(
|
||||||
.find(|m| m.starts_with("image/"))
|
.find(|m| m.starts_with("image/"))
|
||||||
.or_else(|| offered.first())
|
.or_else(|| offered.first())
|
||||||
} else {
|
} else {
|
||||||
offered.first()
|
let has_image = offered.iter().any(|m| m.starts_with("image/"));
|
||||||
|
if has_image {
|
||||||
|
offered
|
||||||
|
.iter()
|
||||||
|
.find(|m| m.starts_with("image/"))
|
||||||
|
.or_else(|| offered.first())
|
||||||
|
} else if offered.first().is_some_and(|m| m == "text/html") {
|
||||||
|
offered
|
||||||
|
.iter()
|
||||||
|
.find(|m| *m != "text/html")
|
||||||
|
.or_else(|| offered.first())
|
||||||
|
} else {
|
||||||
|
offered.first()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -430,17 +462,38 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pick_source_order_preserved() {
|
fn test_pick_image_over_html_firefox_copy_image() {
|
||||||
// Firefox typically offers html first, then image, then text
|
// Firefox "Copy Image" offers html first, then image, then text.
|
||||||
|
// We should pick the image, not the html wrapper.
|
||||||
let offered = vec![
|
let offered = vec![
|
||||||
"text/html".to_string(),
|
"text/html".to_string(),
|
||||||
"image/png".to_string(),
|
"image/png".to_string(),
|
||||||
"text/plain".to_string(),
|
"text/plain".to_string(),
|
||||||
];
|
];
|
||||||
// With "any", we trust the source: first offered wins
|
assert_eq!(pick_mime(&offered, "any").unwrap(), "image/png");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pick_image_over_html_electron() {
|
||||||
|
// Electron apps also put text/html before image types
|
||||||
|
let offered = vec!["text/html".to_string(), "image/jpeg".to_string()];
|
||||||
|
assert_eq!(pick_mime(&offered, "any").unwrap(), "image/jpeg");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pick_html_fallback_when_only_html() {
|
||||||
|
// When text/html is the only type, pick it
|
||||||
|
let offered = vec!["text/html".to_string()];
|
||||||
assert_eq!(pick_mime(&offered, "any").unwrap(), "text/html");
|
assert_eq!(pick_mime(&offered, "any").unwrap(), "text/html");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pick_text_over_html_when_no_image() {
|
||||||
|
// Rich text copy: html + plain, no image — prefer plain text
|
||||||
|
let offered = vec!["text/html".to_string(), "text/plain".to_string()];
|
||||||
|
assert_eq!(pick_mime(&offered, "any").unwrap(), "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pick_file_manager_uri_list_first() {
|
fn test_pick_file_manager_uri_list_first() {
|
||||||
// File managers typically offer uri-list first
|
// File managers typically offer uri-list first
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue