Skip to content

Commit 83791f9

Browse files
authored
Rollup merge of #106915 - notriddle:notriddle/load-only-one-theme, r=GuillaumeGomez,jsha
Only load one CSS theme by default This is a tweaked version of #103971 that uses `document.write` to create the stylesheet link at startup, avoiding a FOUC during page navigation. It also rebases the PR, making it work with the new hashed filenames. Fixes #82614 Preview: http://notriddle.com/notriddle-rustdoc-demos/load-only-one-theme-v2/std/index.html
2 parents 39f2657 + 255fdb3 commit 83791f9

File tree

6 files changed

+106
-59
lines changed

6 files changed

+106
-59
lines changed

src/librustdoc/html/render/context.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -647,11 +647,35 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
647647
</noscript>\
648648
<link rel=\"stylesheet\" \
649649
href=\"{static_root_path}{settings_css}\">\
650-
<script defer src=\"{static_root_path}{settings_js}\"></script>",
650+
<script defer src=\"{static_root_path}{settings_js}\"></script>\
651+
<link rel=\"preload\" href=\"{static_root_path}{theme_light_css}\" \
652+
as=\"style\">\
653+
<link rel=\"preload\" href=\"{static_root_path}{theme_dark_css}\" \
654+
as=\"style\">\
655+
<link rel=\"preload\" href=\"{static_root_path}{theme_ayu_css}\" \
656+
as=\"style\">",
651657
static_root_path = page.get_static_root_path(),
652658
settings_css = static_files::STATIC_FILES.settings_css,
653659
settings_js = static_files::STATIC_FILES.settings_js,
654-
)
660+
theme_light_css = static_files::STATIC_FILES.theme_light_css,
661+
theme_dark_css = static_files::STATIC_FILES.theme_dark_css,
662+
theme_ayu_css = static_files::STATIC_FILES.theme_ayu_css,
663+
);
664+
// Pre-load all theme CSS files, so that switching feels seamless.
665+
//
666+
// When loading settings.html as a popover, the equivalent HTML is
667+
// generated in main.js.
668+
for file in &shared.style_files {
669+
if let Ok(theme) = file.basename() {
670+
write!(
671+
buf,
672+
"<link rel=\"preload\" href=\"{root_path}{theme}{suffix}.css\" \
673+
as=\"style\">",
674+
root_path = page.static_root_path.unwrap_or(""),
675+
suffix = page.resource_suffix,
676+
);
677+
}
678+
}
655679
},
656680
&shared.style_files,
657681
);

src/librustdoc/html/static/js/main.js

+27-12
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,9 @@
11
// Local js definitions:
22
/* global addClass, getSettingValue, hasClass, searchState */
3-
/* global onEach, onEachLazy, removeClass */
3+
/* global onEach, onEachLazy, removeClass, getVar */
44

55
"use strict";
66

7-
// Get a value from the rustdoc-vars div, which is used to convey data from
8-
// Rust to the JS. If there is no such element, return null.
9-
function getVar(name) {
10-
const el = document.getElementById("rustdoc-vars");
11-
if (el) {
12-
return el.attributes["data-" + name].value;
13-
} else {
14-
return null;
15-
}
16-
}
17-
187
// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL
198
// for a resource under the root-path, with the resource-suffix.
209
function resourcePath(basename, extension) {
@@ -187,6 +176,15 @@ function loadCss(cssUrl) {
187176
document.getElementsByTagName("head")[0].appendChild(link);
188177
}
189178

179+
function preLoadCss(cssUrl) {
180+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
181+
const link = document.createElement("link");
182+
link.href = cssUrl;
183+
link.rel = "preload";
184+
link.as = "style";
185+
document.getElementsByTagName("head")[0].appendChild(link);
186+
}
187+
190188
(function() {
191189
const isHelpPage = window.location.pathname.endsWith("/help.html");
192190

@@ -207,6 +205,23 @@ function loadCss(cssUrl) {
207205
// hopefully be loaded when the JS will generate the settings content.
208206
loadCss(getVar("static-root-path") + getVar("settings-css"));
209207
loadScript(getVar("static-root-path") + getVar("settings-js"));
208+
preLoadCss(getVar("static-root-path") + getVar("theme-light-css"));
209+
preLoadCss(getVar("static-root-path") + getVar("theme-dark-css"));
210+
preLoadCss(getVar("static-root-path") + getVar("theme-ayu-css"));
211+
// Pre-load all theme CSS files, so that switching feels seamless.
212+
//
213+
// When loading settings.html as a standalone page, the equivalent HTML is
214+
// generated in context.rs.
215+
setTimeout(() => {
216+
const themes = getVar("themes").split(",");
217+
for (const theme of themes) {
218+
// if there are no themes, do nothing
219+
// "".split(",") == [""]
220+
if (theme !== "") {
221+
preLoadCss(getVar("root-path") + theme + ".css");
222+
}
223+
}
224+
}, 0);
210225
};
211226

212227
window.searchState = {

src/librustdoc/html/static/js/storage.js

+26-21
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
const darkThemes = ["dark", "ayu"];
99
window.currentTheme = document.getElementById("themeStyle");
10-
window.mainTheme = document.getElementById("mainThemeStyle");
1110

1211
// WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
1312
// If you update this line, then you also need to update the media query with the same
@@ -44,8 +43,6 @@ function getSettingValue(settingName) {
4443

4544
const localStoredTheme = getSettingValue("theme");
4645

47-
const savedHref = [];
48-
4946
// eslint-disable-next-line no-unused-vars
5047
function hasClass(elem, className) {
5148
return elem && elem.classList && elem.classList.contains(className);
@@ -102,6 +99,7 @@ function onEach(arr, func, reversed) {
10299
* @param {function(?)} func - The callback
103100
* @param {boolean} [reversed] - Whether to iterate in reverse
104101
*/
102+
// eslint-disable-next-line no-unused-vars
105103
function onEachLazy(lazyArray, func, reversed) {
106104
return onEach(
107105
Array.prototype.slice.call(lazyArray),
@@ -125,30 +123,37 @@ function getCurrentValue(name) {
125123
}
126124
}
127125

128-
function switchTheme(styleElem, mainStyleElem, newThemeName, saveTheme) {
126+
// Get a value from the rustdoc-vars div, which is used to convey data from
127+
// Rust to the JS. If there is no such element, return null.
128+
const getVar = (function getVar(name) {
129+
const el = document.getElementById("rustdoc-vars");
130+
if (el) {
131+
return el.attributes["data-" + name].value;
132+
} else {
133+
return null;
134+
}
135+
});
136+
137+
function switchTheme(newThemeName, saveTheme) {
129138
// If this new value comes from a system setting or from the previously
130139
// saved theme, no need to save it.
131140
if (saveTheme) {
132141
updateLocalStorage("theme", newThemeName);
133142
}
134143

135-
if (savedHref.length === 0) {
136-
onEachLazy(document.getElementsByTagName("link"), el => {
137-
savedHref.push(el.href);
138-
});
144+
let newHref;
145+
146+
if (newThemeName === "light" || newThemeName === "dark" || newThemeName === "ayu") {
147+
newHref = getVar("static-root-path") + getVar("theme-" + newThemeName + "-css");
148+
} else {
149+
newHref = getVar("root-path") + newThemeName + getVar("resource-suffix") + ".css";
139150
}
140-
const newHref = savedHref.find(url => {
141-
const m = url.match(/static\.files\/(.*)-[a-f0-9]{16}\.css$/);
142-
if (m && m[1] === newThemeName) {
143-
return true;
144-
}
145-
const m2 = url.match(/\/([^/]*)\.css$/);
146-
if (m2 && m2[1].startsWith(newThemeName)) {
147-
return true;
148-
}
149-
});
150-
if (newHref && newHref !== styleElem.href) {
151-
styleElem.href = newHref;
151+
152+
if (!window.currentTheme) {
153+
document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
154+
window.currentTheme = document.getElementById("themeStyle");
155+
} else if (newHref !== window.currentTheme.href) {
156+
window.currentTheme.href = newHref;
152157
}
153158
}
154159

@@ -164,7 +169,7 @@ const updateTheme = (function() {
164169
*/
165170
function updateTheme() {
166171
const use = (theme, saveTheme) => {
167-
switchTheme(window.currentTheme, window.mainTheme, theme, saveTheme);
172+
switchTheme(theme, saveTheme);
168173
};
169174

170175
// maybe the user has disabled the setting in the meantime!

src/librustdoc/html/templates/page.html

+21-18
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,28 @@
1717
<link rel="stylesheet" {#+ #}
1818
href="{{static_root_path|safe}}{{files.rustdoc_css}}" {#+ #}
1919
id="mainThemeStyle"> {# #}
20-
<link rel="stylesheet" id="themeStyle" href="{{static_root_path|safe}}{{files.theme_light_css}}"> {# #}
21-
<link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {# #}
22-
<link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_ayu_css}}"> {# #}
23-
{% for theme in themes %}
24-
<link rel="stylesheet" disabled href="{{page.root_path|safe}}{{theme}}{{page.resource_suffix}}.css"> {# #}
25-
{% endfor %}
2620
{% if !layout.default_settings.is_empty() %}
2721
<script id="default-settings" {#+ #}
2822
{%~ for (k, v) in layout.default_settings ~%}
2923
data-{{k}}="{{v}}"
3024
{% endfor %}
3125
></script> {# #}
3226
{% endif %}
27+
<div id="rustdoc-vars" {#+ #}
28+
data-root-path="{{page.root_path|safe}}" {#+ #}
29+
data-static-root-path="{{static_root_path|safe}}" {#+ #}
30+
data-current-crate="{{layout.krate}}" {#+ #}
31+
data-themes="{{themes|join(",") }}" {#+ #}
32+
data-resource-suffix="{{page.resource_suffix}}" {#+ #}
33+
data-rustdoc-version="{{rustdoc_version}}" {#+ #}
34+
data-search-js="{{files.search_js}}" {#+ #}
35+
data-settings-js="{{files.settings_js}}" {#+ #}
36+
data-settings-css="{{files.settings_css}}" {#+ #}
37+
data-theme-light-css="{{files.theme_light_css}}" {#+ #}
38+
data-theme-dark-css="{{files.theme_dark_css}}" {#+ #}
39+
data-theme-ayu-css="{{files.theme_ayu_css}}" {#+ #}
40+
> {# #}
41+
</div> {# #}
3342
<script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {# #}
3443
{% if page.css_class.contains("crate") %}
3544
<script defer src="{{page.root_path|safe}}crates{{page.resource_suffix}}.js"></script> {# #}
@@ -44,6 +53,12 @@
4453
<script defer src="{{static_root_path|safe}}{{files.scrape_examples_js}}"></script> {# #}
4554
{% endif %}
4655
<noscript> {# #}
56+
<link rel="stylesheet" {#+ #}
57+
media="(prefers-color-scheme:light)" {#+ #}
58+
href="{{static_root_path|safe}}{{files.theme_light_css}}"> {# #}
59+
<link rel="stylesheet" {#+ #}
60+
media="(prefers-color-scheme:dark)" {#+ #}
61+
href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {# #}
4762
<link rel="stylesheet" {#+ #}
4863
href="{{static_root_path|safe}}{{files.noscript_css}}"> {# #}
4964
</noscript> {# #}
@@ -132,17 +147,5 @@ <h2></h2> {# #}
132147
{% if page.css_class != "source" %}</div>{% endif %}
133148
</main> {# #}
134149
{{ layout.external_html.after_content|safe }}
135-
<div id="rustdoc-vars" {#+ #}
136-
data-root-path="{{page.root_path|safe}}" {#+ #}
137-
data-static-root-path="{{static_root_path|safe}}" {#+ #}
138-
data-current-crate="{{layout.krate}}" {#+ #}
139-
data-themes="{{themes|join(",") }}" {#+ #}
140-
data-resource-suffix="{{page.resource_suffix}}" {#+ #}
141-
data-rustdoc-version="{{rustdoc_version}}" {#+ #}
142-
data-search-js="{{files.search_js}}" {#+ #}
143-
data-settings-js="{{files.settings_js}}" {#+ #}
144-
data-settings-css="{{files.settings_css}}" {#+ #}
145-
> {# #}
146-
</div> {# #}
147150
</body> {# #}
148151
</html> {# #}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// @has test.css
22
// @has foo/struct.Foo.html
3-
// @has - '//link[@rel="stylesheet"]/@href' '../test.css'
3+
// @has - '//*[@id="rustdoc-vars"]/@data-themes' 'test'
44
pub struct Foo;

tests/rustdoc-gui/scrape-examples-button-focus.goml

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,24 @@ focus: ".scraped-example-list > .scraped-example .next"
88
press-key: "Enter"
99
assert-property-false: (".scraped-example-list > .scraped-example pre", {
1010
"scrollTop": |initialScrollTop|
11-
})
11+
}, NEAR)
1212
focus: ".scraped-example-list > .scraped-example .prev"
1313
press-key: "Enter"
1414
assert-property: (".scraped-example-list > .scraped-example pre", {
1515
"scrollTop": |initialScrollTop|
16-
})
16+
}, NEAR)
1717

1818
// The expand button increases the scrollHeight of the minimized code viewport
1919
store-property: (smallOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
2020
assert-property-false: (".scraped-example-list > .scraped-example pre", {
2121
"scrollHeight": |smallOffsetHeight|
22-
})
22+
}, NEAR)
2323
focus: ".scraped-example-list > .scraped-example .expand"
2424
press-key: "Enter"
2525
assert-property-false: (".scraped-example-list > .scraped-example pre", {
2626
"offsetHeight": |smallOffsetHeight|
27-
})
27+
}, NEAR)
2828
store-property: (fullOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
2929
assert-property: (".scraped-example-list > .scraped-example pre", {
3030
"scrollHeight": |fullOffsetHeight|
31-
})
31+
}, NEAR)

0 commit comments

Comments
 (0)