Skip to content

Commit

Permalink
fix: keep track of css injected to document (#11635)
Browse files Browse the repository at this point in the history
Stops css/fonts from being loaded multiple times to the document with embedded applications.

Closes #11608
  • Loading branch information
caalador authored Aug 27, 2021
1 parent 1b371a7 commit 8da204a
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ const createLinkReferences = (css, target) => {
const injectGlobalCssMethod = `
// target: Document | ShadowRoot
export const injectGlobalCss = (css, target, first) => {
if(target === document) {
const hash = getHash(css);
if (window.Vaadin.Flow.injectedGlobalCss.indexOf(hash) !== -1) {
return;
}
window.Vaadin.Flow.injectedGlobalCss.push(hash);
}
const sheet = new CSSStyleSheet();
sheet.replaceSync(createLinkReferences(css,target));
if (first) {
Expand Down Expand Up @@ -254,18 +260,48 @@ function generateThemeFile(themeFolder, themeName, themeProperties, productionMo
themeFile += imports.join('');
themeFile += `
window.Vaadin = window.Vaadin || {};
window.Vaadin['${globalCssFlag}'] = window.Vaadin['${globalCssFlag}'] || [];
window.Vaadin.Flow.injectedGlobalCss = [];
/**
* Calculate a 32 bit FNV-1a hash
* Found here: https://gist.github.com/vaiorabbit/5657561
* Ref.: http://isthe.com/chongo/tech/comp/fnv/
*
* @param {string} str the input value
* @returns {string} 32 bit (as 8 byte hex string)
*/
function hashFnv32a(str) {
/*jshint bitwise:false */
let i, l, hval = 0x811c9dc5;
for (i = 0, l = str.length; i < l; i++) {
hval ^= str.charCodeAt(i);
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24);
}
// Convert to 8 digit hex string
return ("0000000" + (hval >>> 0).toString(16)).substr(-8);
}
/**
* Calculate a 64 bit hash for the given input.
* Double hash is used to significantly lower the collision probability.
*
* @param {string} input value to get hash for
* @returns {string} 64 bit (as 16 byte hex string)
*/
function getHash(input) {
let h1 = hashFnv32a(input); // returns 32 bit (as 8 byte hex string)
return h1 + hashFnv32a(h1 + input);
}
`;

// Don't format as the generated file formatting will get wonky!
// If targets check that we only register the style parts once, checks exist for global css and component css
const themeFileApply = `export const applyTheme = (target) => {
${parentTheme}
const injectGlobal = (window.Vaadin['${globalCssFlag}'].length === 0) || (!window.Vaadin['${globalCssFlag}'].includes(target) && target !== document);
if (injectGlobal) {
${globalCssCode.join('')}
window.Vaadin['${globalCssFlag}'].push(target);
}
${globalCssCode.join('')}
if (!document['${componentCssFlag}']) {
${componentCssCode.join('')}
document['${componentCssFlag}'] = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,18 @@ public void stylesCssImport_externalLinkAddedToShadowroot() {
Assert.assertTrue("Missing link for external url", linkUrls
.contains("https://fonts.googleapis.com/css?family=Itim"));
}

@Test
public void multipleSameEmbedded_cssTargetingDocumentShouldOnlyAddElementsOneTime() {
open();
checkLogsForErrors();
Assert.assertEquals(
"document.css adds 2 font links and those should not duplicate",
2l, getCommandExecutor().executeScript(
"return document.head.getElementsByTagName('link').length"));
Assert.assertEquals(
"Project contains 2 css injections to document and both should be hashed",
2l, getCommandExecutor().executeScript(
"return window.Vaadin.Flow.injectedGlobalCss.length"));
}
}

0 comments on commit 8da204a

Please sign in to comment.