/
githubmirror
/
amphtml
Обзор
Документация
Войти
/
githubmirror
/
amphtml
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/font-stylesheet-timeout.js
135 строк
5 KB
Justin Ridgewell
Except Google Translate from the font-stylesheet timeout (#38300)
21 июн 2022, 20:46
Не верифицирован
21 июн 2022, 20:46
dbc63de
Код
Авторство
О чём код?
import {onDocumentReady} from '#core/document/ready'; import {escapeCssSelectorIdent} from '#core/dom/css-selectors'; import * as urls from './config/urls'; /** * While browsers put a timeout on font downloads (3s by default, * some less on slow connections), there is no such timeout for style * sheets. In the case of AMP external stylesheets are ONLY used to * download fonts, but browsers have no reasonable timeout for * stylesheets. Users may thus wait a long time for these to download * even though all they do is reference fonts. * * For that reasons this function identifies (or rather infers) font * stylesheets that have not downloaded within timeout period of the page * response starting and reinserts equivalent link tags dynamically. This * removes their page-render-blocking nature and lets the doc render. * * @param {!Window} win */ export function fontStylesheetTimeout(win) { onDocumentReady(win.document, () => maybeTimeoutFonts(win)); } /** * @param {!Window} win */ function maybeTimeoutFonts(win) { // Educated guess 😅, but we're calculating the correct value further down // if available. let timeSinceNavigationStart = 1500; // If available, we start counting from the time the HTTP request // for the page started. The preload scanner should then quickly // start the CSS download. const perf = win.performance; if (perf && perf.timing && perf.timing.navigationStart) { timeSinceNavigationStart = Date.now() - perf.timing.navigationStart; } // Set timeout such that we have some time to paint fonts in time for // the desired goal of a 2500ms for LCP. const timeout = Math.max( 1, 2500 - 400 /* Estimated max time to paint */ - timeSinceNavigationStart ); // Avoid timer dependency since this runs very early in execution. win.setTimeout(() => { // Try again, more fonts might have loaded. timeoutFontFaces(win); const {styleSheets} = win.document; if (!styleSheets) { return; } // Find all stylesheets that aren't loaded from the AMP CDN (those are // critical if they are present). const styleLinkElements = win.document.querySelectorAll( `link[rel~="stylesheet"]:not([href^="https://translate.googleapis.com/translate_static/css/"]):not([href^="${escapeCssSelectorIdent( urls.cdn )}"])` ); // Compare external sheets against elements of document.styleSheets. // They do not appear in this list until they have been loaded. const timedoutStyleSheets = []; for (let i = 0; i < styleLinkElements.length; i++) { const link = styleLinkElements[i]; let found = false; for (let n = 0; n < styleSheets.length; n++) { if (styleSheets[n].ownerNode == link) { found = true; break; } } if (!found) { timedoutStyleSheets.push(link); } } for (let i = 0; i < timedoutStyleSheets.length; i++) { const link = timedoutStyleSheets[i]; // To avoid blocking the render, we assign a non-matching media // attribute first… const media = link.media || 'all'; link.media = 'print'; // And then switch it back to the original after the stylesheet // loaded. link.onload = () => { link.media = media; timeoutFontFaces(win); }; link.setAttribute('i-amphtml-timeout', timeout); // Pop/insert the same link. This causes Chrome to unblock, and doesn't // blank out Safari. #12521 link.parentNode.insertBefore(link, link.nextSibling); } }, timeout); } /** * Sets font faces that haven't been loaded by the time this was called to * `font-display: swap` in supported browsers. * See https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display * for details on behavior. * Swap effectively leads to immediate display of the fallback font with * the custom font being displayed when possible. * While this is not the most desirable setting, it is compatible with the * default (which does that but only waiting for 3 seconds). * Ideally websites would opt into `font-display: optional` which provides * nicer UX for non-icon fonts. * If fonts set a non default display mode, this does nothing. * @param {!Window} win */ function timeoutFontFaces(win) { const doc = win.document; // TODO(@cramforce) Switch to .values when FontFaceSet extern supports it. if (!doc.fonts || !doc.fonts['values']) { return; } const it = doc.fonts['values'](); let entry; while ((entry = it.next())) { const fontFace = entry.value; if (!fontFace) { return; } if (fontFace.status != 'loading') { continue; } // Not supported or non-default value. // If the publisher specified a non-default, we respect that, of course. if (!('display' in fontFace) || fontFace.display != 'auto') { continue; } fontFace.display = 'swap'; } }