Skip to content

Commit

Permalink
Rollup merge of #106915 - notriddle:notriddle/load-only-one-theme, r=…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
matthiaskrgr committed Mar 9, 2023
2 parents 39f2657 + 255fdb3 commit 83791f9
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 59 deletions.
28 changes: 26 additions & 2 deletions src/librustdoc/html/render/context.rs
Expand Up @@ -647,11 +647,35 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
</noscript>\
<link rel=\"stylesheet\" \
href=\"{static_root_path}{settings_css}\">\
<script defer src=\"{static_root_path}{settings_js}\"></script>",
<script defer src=\"{static_root_path}{settings_js}\"></script>\
<link rel=\"preload\" href=\"{static_root_path}{theme_light_css}\" \
as=\"style\">\
<link rel=\"preload\" href=\"{static_root_path}{theme_dark_css}\" \
as=\"style\">\
<link rel=\"preload\" href=\"{static_root_path}{theme_ayu_css}\" \
as=\"style\">",
static_root_path = page.get_static_root_path(),
settings_css = static_files::STATIC_FILES.settings_css,
settings_js = static_files::STATIC_FILES.settings_js,
)
theme_light_css = static_files::STATIC_FILES.theme_light_css,
theme_dark_css = static_files::STATIC_FILES.theme_dark_css,
theme_ayu_css = static_files::STATIC_FILES.theme_ayu_css,
);
// Pre-load all theme CSS files, so that switching feels seamless.
//
// When loading settings.html as a popover, the equivalent HTML is
// generated in main.js.
for file in &shared.style_files {
if let Ok(theme) = file.basename() {
write!(
buf,
"<link rel=\"preload\" href=\"{root_path}{theme}{suffix}.css\" \
as=\"style\">",
root_path = page.static_root_path.unwrap_or(""),
suffix = page.resource_suffix,
);
}
}
},
&shared.style_files,
);
Expand Down
39 changes: 27 additions & 12 deletions src/librustdoc/html/static/js/main.js
@@ -1,20 +1,9 @@
// Local js definitions:
/* global addClass, getSettingValue, hasClass, searchState */
/* global onEach, onEachLazy, removeClass */
/* global onEach, onEachLazy, removeClass, getVar */

"use strict";

// Get a value from the rustdoc-vars div, which is used to convey data from
// Rust to the JS. If there is no such element, return null.
function getVar(name) {
const el = document.getElementById("rustdoc-vars");
if (el) {
return el.attributes["data-" + name].value;
} else {
return null;
}
}

// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL
// for a resource under the root-path, with the resource-suffix.
function resourcePath(basename, extension) {
Expand Down Expand Up @@ -187,6 +176,15 @@ function loadCss(cssUrl) {
document.getElementsByTagName("head")[0].appendChild(link);
}

function preLoadCss(cssUrl) {
// https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload
const link = document.createElement("link");
link.href = cssUrl;
link.rel = "preload";
link.as = "style";
document.getElementsByTagName("head")[0].appendChild(link);
}

(function() {
const isHelpPage = window.location.pathname.endsWith("/help.html");

Expand All @@ -207,6 +205,23 @@ function loadCss(cssUrl) {
// hopefully be loaded when the JS will generate the settings content.
loadCss(getVar("static-root-path") + getVar("settings-css"));
loadScript(getVar("static-root-path") + getVar("settings-js"));
preLoadCss(getVar("static-root-path") + getVar("theme-light-css"));
preLoadCss(getVar("static-root-path") + getVar("theme-dark-css"));
preLoadCss(getVar("static-root-path") + getVar("theme-ayu-css"));
// Pre-load all theme CSS files, so that switching feels seamless.
//
// When loading settings.html as a standalone page, the equivalent HTML is
// generated in context.rs.
setTimeout(() => {
const themes = getVar("themes").split(",");
for (const theme of themes) {
// if there are no themes, do nothing
// "".split(",") == [""]
if (theme !== "") {
preLoadCss(getVar("root-path") + theme + ".css");
}
}
}, 0);
};

window.searchState = {
Expand Down
47 changes: 26 additions & 21 deletions src/librustdoc/html/static/js/storage.js
Expand Up @@ -7,7 +7,6 @@

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

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

const localStoredTheme = getSettingValue("theme");

const savedHref = [];

// eslint-disable-next-line no-unused-vars
function hasClass(elem, className) {
return elem && elem.classList && elem.classList.contains(className);
Expand Down Expand Up @@ -102,6 +99,7 @@ function onEach(arr, func, reversed) {
* @param {function(?)} func - The callback
* @param {boolean} [reversed] - Whether to iterate in reverse
*/
// eslint-disable-next-line no-unused-vars
function onEachLazy(lazyArray, func, reversed) {
return onEach(
Array.prototype.slice.call(lazyArray),
Expand All @@ -125,30 +123,37 @@ function getCurrentValue(name) {
}
}

function switchTheme(styleElem, mainStyleElem, newThemeName, saveTheme) {
// Get a value from the rustdoc-vars div, which is used to convey data from
// Rust to the JS. If there is no such element, return null.
const getVar = (function getVar(name) {
const el = document.getElementById("rustdoc-vars");
if (el) {
return el.attributes["data-" + name].value;
} else {
return null;
}
});

function switchTheme(newThemeName, saveTheme) {
// If this new value comes from a system setting or from the previously
// saved theme, no need to save it.
if (saveTheme) {
updateLocalStorage("theme", newThemeName);
}

if (savedHref.length === 0) {
onEachLazy(document.getElementsByTagName("link"), el => {
savedHref.push(el.href);
});
let newHref;

if (newThemeName === "light" || newThemeName === "dark" || newThemeName === "ayu") {
newHref = getVar("static-root-path") + getVar("theme-" + newThemeName + "-css");
} else {
newHref = getVar("root-path") + newThemeName + getVar("resource-suffix") + ".css";
}
const newHref = savedHref.find(url => {
const m = url.match(/static\.files\/(.*)-[a-f0-9]{16}\.css$/);
if (m && m[1] === newThemeName) {
return true;
}
const m2 = url.match(/\/([^/]*)\.css$/);
if (m2 && m2[1].startsWith(newThemeName)) {
return true;
}
});
if (newHref && newHref !== styleElem.href) {
styleElem.href = newHref;

if (!window.currentTheme) {
document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
window.currentTheme = document.getElementById("themeStyle");
} else if (newHref !== window.currentTheme.href) {
window.currentTheme.href = newHref;
}
}

Expand All @@ -164,7 +169,7 @@ const updateTheme = (function() {
*/
function updateTheme() {
const use = (theme, saveTheme) => {
switchTheme(window.currentTheme, window.mainTheme, theme, saveTheme);
switchTheme(theme, saveTheme);
};

// maybe the user has disabled the setting in the meantime!
Expand Down
39 changes: 21 additions & 18 deletions src/librustdoc/html/templates/page.html
Expand Up @@ -17,19 +17,28 @@
<link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.rustdoc_css}}" {#+ #}
id="mainThemeStyle"> {# #}
<link rel="stylesheet" id="themeStyle" href="{{static_root_path|safe}}{{files.theme_light_css}}"> {# #}
<link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {# #}
<link rel="stylesheet" disabled href="{{static_root_path|safe}}{{files.theme_ayu_css}}"> {# #}
{% for theme in themes %}
<link rel="stylesheet" disabled href="{{page.root_path|safe}}{{theme}}{{page.resource_suffix}}.css"> {# #}
{% endfor %}
{% if !layout.default_settings.is_empty() %}
<script id="default-settings" {#+ #}
{%~ for (k, v) in layout.default_settings ~%}
data-{{k}}="{{v}}"
{% endfor %}
></script> {# #}
{% endif %}
<div id="rustdoc-vars" {#+ #}
data-root-path="{{page.root_path|safe}}" {#+ #}
data-static-root-path="{{static_root_path|safe}}" {#+ #}
data-current-crate="{{layout.krate}}" {#+ #}
data-themes="{{themes|join(",") }}" {#+ #}
data-resource-suffix="{{page.resource_suffix}}" {#+ #}
data-rustdoc-version="{{rustdoc_version}}" {#+ #}
data-search-js="{{files.search_js}}" {#+ #}
data-settings-js="{{files.settings_js}}" {#+ #}
data-settings-css="{{files.settings_css}}" {#+ #}
data-theme-light-css="{{files.theme_light_css}}" {#+ #}
data-theme-dark-css="{{files.theme_dark_css}}" {#+ #}
data-theme-ayu-css="{{files.theme_ayu_css}}" {#+ #}
> {# #}
</div> {# #}
<script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {# #}
{% if page.css_class.contains("crate") %}
<script defer src="{{page.root_path|safe}}crates{{page.resource_suffix}}.js"></script> {# #}
Expand All @@ -44,6 +53,12 @@
<script defer src="{{static_root_path|safe}}{{files.scrape_examples_js}}"></script> {# #}
{% endif %}
<noscript> {# #}
<link rel="stylesheet" {#+ #}
media="(prefers-color-scheme:light)" {#+ #}
href="{{static_root_path|safe}}{{files.theme_light_css}}"> {# #}
<link rel="stylesheet" {#+ #}
media="(prefers-color-scheme:dark)" {#+ #}
href="{{static_root_path|safe}}{{files.theme_dark_css}}"> {# #}
<link rel="stylesheet" {#+ #}
href="{{static_root_path|safe}}{{files.noscript_css}}"> {# #}
</noscript> {# #}
Expand Down Expand Up @@ -132,17 +147,5 @@ <h2></h2> {# #}
{% if page.css_class != "source" %}</div>{% endif %}
</main> {# #}
{{ layout.external_html.after_content|safe }}
<div id="rustdoc-vars" {#+ #}
data-root-path="{{page.root_path|safe}}" {#+ #}
data-static-root-path="{{static_root_path|safe}}" {#+ #}
data-current-crate="{{layout.krate}}" {#+ #}
data-themes="{{themes|join(",") }}" {#+ #}
data-resource-suffix="{{page.resource_suffix}}" {#+ #}
data-rustdoc-version="{{rustdoc_version}}" {#+ #}
data-search-js="{{files.search_js}}" {#+ #}
data-settings-js="{{files.settings_js}}" {#+ #}
data-settings-css="{{files.settings_css}}" {#+ #}
> {# #}
</div> {# #}
</body> {# #}
</html> {# #}
2 changes: 1 addition & 1 deletion tests/run-make-fulldeps/rustdoc-themes/foo.rs
@@ -1,4 +1,4 @@
// @has test.css
// @has foo/struct.Foo.html
// @has - '//link[@rel="stylesheet"]/@href' '../test.css'
// @has - '//*[@id="rustdoc-vars"]/@data-themes' 'test'
pub struct Foo;
10 changes: 5 additions & 5 deletions tests/rustdoc-gui/scrape-examples-button-focus.goml
Expand Up @@ -8,24 +8,24 @@ focus: ".scraped-example-list > .scraped-example .next"
press-key: "Enter"
assert-property-false: (".scraped-example-list > .scraped-example pre", {
"scrollTop": |initialScrollTop|
})
}, NEAR)
focus: ".scraped-example-list > .scraped-example .prev"
press-key: "Enter"
assert-property: (".scraped-example-list > .scraped-example pre", {
"scrollTop": |initialScrollTop|
})
}, NEAR)

// The expand button increases the scrollHeight of the minimized code viewport
store-property: (smallOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
assert-property-false: (".scraped-example-list > .scraped-example pre", {
"scrollHeight": |smallOffsetHeight|
})
}, NEAR)
focus: ".scraped-example-list > .scraped-example .expand"
press-key: "Enter"
assert-property-false: (".scraped-example-list > .scraped-example pre", {
"offsetHeight": |smallOffsetHeight|
})
}, NEAR)
store-property: (fullOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
assert-property: (".scraped-example-list > .scraped-example pre", {
"scrollHeight": |fullOffsetHeight|
})
}, NEAR)

0 comments on commit 83791f9

Please sign in to comment.