rustdoc font links only emit crossorigin when needed
The `crossorigin` attribute may cause issues when the href is not actuall across origins. Specifically, the tag causes the browser to send a preflight OPTIONS request to the href even if it is same-origin. Some tempermental servers may reject all CORS preflect requests even if they're actually same-origin, which causes a CORS error and prevents the fonts from loading, even later on. This commit fixes that problem by not emitting `crossorigin` if the url looks like a domain-relative url. Co-authored-by: Guillaume Gomez <guillaume1.gomez@gmail.com>
This commit is contained in:
parent
4b55fe199c
commit
f516d4c9e3
3 changed files with 54 additions and 1 deletions
|
|
@ -8,6 +8,9 @@ use super::static_files::{STATIC_FILES, StaticFiles};
|
|||
use crate::externalfiles::ExternalHtml;
|
||||
use crate::html::render::{StylePath, ensure_trailing_slash};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub(crate) struct Layout {
|
||||
pub(crate) logo: String,
|
||||
pub(crate) favicon: String,
|
||||
|
|
@ -68,6 +71,13 @@ struct PageLayout<'a> {
|
|||
display_krate_version_extra: &'a str,
|
||||
}
|
||||
|
||||
impl PageLayout<'_> {
|
||||
/// See [`may_remove_crossorigin`].
|
||||
fn static_root_path_may_remove_crossorigin(&self) -> bool {
|
||||
may_remove_crossorigin(&self.static_root_path)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use crate::html::render::sidebar::filters;
|
||||
|
||||
pub(crate) fn render<T: Display, S: Display>(
|
||||
|
|
@ -134,3 +144,22 @@ pub(crate) fn redirect(url: &str) -> String {
|
|||
</html>"##,
|
||||
)
|
||||
}
|
||||
|
||||
/// Conservatively determines if `href` is relative to the current origin,
|
||||
/// so that `crossorigin` may be safely removed from `<link>` elements.
|
||||
pub(crate) fn may_remove_crossorigin(href: &str) -> bool {
|
||||
// Reject scheme-relative URLs (`//example.com/`).
|
||||
if href.starts_with("//") {
|
||||
return false;
|
||||
}
|
||||
// URL is interpreted as having a scheme iff: it starts with an ascii alpha, and only
|
||||
// contains ascii alphanumeric or `+` `-` `.` up to the `:`.
|
||||
// https://url.spec.whatwg.org/#url-parsing
|
||||
let has_scheme = href.split_once(':').is_some_and(|(scheme, _rest)| {
|
||||
let mut chars = scheme.chars();
|
||||
chars.next().is_some_and(|c| c.is_ascii_alphabetic())
|
||||
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '+' || c == '-' || c == '.')
|
||||
});
|
||||
// Reject anything with a scheme (`http:`, etc.).
|
||||
!has_scheme
|
||||
}
|
||||
|
|
|
|||
24
src/librustdoc/html/layout/tests.rs
Normal file
24
src/librustdoc/html/layout/tests.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#[test]
|
||||
fn test_may_remove_crossorigin() {
|
||||
use super::may_remove_crossorigin;
|
||||
|
||||
assert!(may_remove_crossorigin("font.woff2"));
|
||||
assert!(may_remove_crossorigin("/font.woff2"));
|
||||
assert!(may_remove_crossorigin("./font.woff2"));
|
||||
assert!(may_remove_crossorigin(":D/font.woff2"));
|
||||
assert!(may_remove_crossorigin("../font.woff2"));
|
||||
|
||||
assert!(!may_remove_crossorigin("//example.com/static.files"));
|
||||
assert!(!may_remove_crossorigin("http://example.com/static.files"));
|
||||
assert!(!may_remove_crossorigin("https://example.com/static.files"));
|
||||
assert!(!may_remove_crossorigin("https://example.com:8080/static.files"));
|
||||
|
||||
assert!(!may_remove_crossorigin("ftp://example.com/static.files"));
|
||||
assert!(!may_remove_crossorigin("blob:http://example.com/static.files"));
|
||||
assert!(!may_remove_crossorigin("javascript:alert('Hello, world!')"));
|
||||
assert!(!may_remove_crossorigin("//./C:"));
|
||||
assert!(!may_remove_crossorigin("file:////C:"));
|
||||
assert!(!may_remove_crossorigin("file:///./C:"));
|
||||
assert!(!may_remove_crossorigin("data:,Hello%2C%20World%21"));
|
||||
assert!(!may_remove_crossorigin("hi...:hello"));
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="description" content="{{page.description}}"> {# #}
|
||||
<title>{{page.title}}</title> {# #}
|
||||
<script>if(window.location.protocol!=="file:") {# Hack to skip preloading fonts locally - see #98769 #}
|
||||
document.head.insertAdjacentHTML("beforeend","{{files.source_serif_4_regular}},{{files.fira_sans_italic}},{{files.fira_sans_regular}},{{files.fira_sans_medium_italic}},{{files.fira_sans_medium}},{{files.source_code_pro_regular}},{{files.source_code_pro_semibold}}".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}${f}">`).join("")) {# #}
|
||||
document.head.insertAdjacentHTML("beforeend","{{files.source_serif_4_regular}},{{files.fira_sans_italic}},{{files.fira_sans_regular}},{{files.fira_sans_medium_italic}},{{files.fira_sans_medium}},{{files.source_code_pro_regular}},{{files.source_code_pro_semibold}}".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2"{% if !static_root_path_may_remove_crossorigin() %} crossorigin{% endif %} href="{{static_root_path|safe}}${f}">`).join("")) {# #}
|
||||
</script> {# #}
|
||||
<link rel="stylesheet" {#+ #}
|
||||
href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue