Commit cfff41ab authored by Mike Greiling's avatar Mike Greiling

Merge branch '230433-custom-svg-logic' into 'master'

Chrome/Edge 84: More performant svg workaround solution

Closes #230433

See merge request gitlab-org/gitlab!38716
parents 6474329b 5080cd40
import svg4everybody from 'svg4everybody'; import { debounce } from 'lodash';
/* /*
Chrome and Edge 84 have a bug relating to icon sprite svgs Chrome and Edge 84 have a bug relating to icon sprite svgs
https://bugs.chromium.org/p/chromium/issues/detail?id=1107442 https://bugs.chromium.org/p/chromium/issues/detail?id=1107442
If the SVG is loaded, under certain circumstances the icons are not If the SVG is loaded, under certain circumstances the icons are not
shown. As a workaround we use the well-tested svg4everybody and forcefully shown. We load our sprite icons with JS and add them to the body.
include the icon fragments into the DOM and thus circumventing the bug Then we iterate over all the `use` elements and replace their reference
to that svg which we added internally. In order to avoid id conflicts,
those are renamed with a unique prefix.
We do that once the DOMContentLoaded fired and otherwise we use a
mutation observer to re-trigger this logic.
In order to not have a big impact on performance or to cause flickering
of of content,
1. we only do it for each svg once
2. we debounce the event handler and just do it in a requestIdleCallback
Before we tried to do it with the library svg4everybody and it had a big
performance impact. See:
https://gitlab.com/gitlab-org/quality/performance/-/issues/312
*/ */
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', async () => {
svg4everybody({ polyfill: true }); const GITLAB_SVG_PREFIX = 'chrome-issue-230433-gitlab-svgs';
const FILE_ICON_PREFIX = 'chrome-issue-230433-file-icons';
const SKIP_ATTRIBUTE = 'data-replaced-by-chrome-issue-230433';
const fixSVGs = () => {
requestIdleCallback(() => {
document.querySelectorAll(`use:not([${SKIP_ATTRIBUTE}])`).forEach(use => {
const href = use?.getAttribute('href') ?? use?.getAttribute('xlink:href') ?? '';
if (href.includes(window.gon.sprite_icons)) {
use.removeAttribute('xlink:href');
use.setAttribute('href', `#${GITLAB_SVG_PREFIX}-${href.split('#')[1]}`);
} else if (href.includes(window.gon.sprite_file_icons)) {
use.removeAttribute('xlink:href');
use.setAttribute('href', `#${FILE_ICON_PREFIX}-${href.split('#')[1]}`);
}
use.setAttribute(SKIP_ATTRIBUTE, 'true');
});
});
};
const watchForNewSVGs = () => {
const observer = new MutationObserver(debounce(fixSVGs, 200));
observer.observe(document.querySelector('body'), {
childList: true,
attributes: false,
subtree: true,
});
};
const retrieveIconSprites = async (url, prefix) => {
const div = document.createElement('div');
div.classList.add('hidden');
const result = await fetch(url);
div.innerHTML = await result.text();
div.querySelectorAll('[id]').forEach(node => {
node.setAttribute('id', `${prefix}-${node.getAttribute('id')}`);
});
document.body.append(div);
};
if (window.gon && window.gon.sprite_icons) {
await retrieveIconSprites(window.gon.sprite_icons, GITLAB_SVG_PREFIX);
if (window.gon.sprite_file_icons) {
await retrieveIconSprites(window.gon.sprite_file_icons, FILE_ICON_PREFIX);
}
fixSVGs();
watchForNewSVGs();
}
}); });
...@@ -87,7 +87,7 @@ export default { ...@@ -87,7 +87,7 @@ export default {
<span> <span>
<gl-loading-icon v-if="loading" :inline="true" /> <gl-loading-icon v-if="loading" :inline="true" />
<gl-icon v-else-if="isSymlink" name="symlink" :size="size" /> <gl-icon v-else-if="isSymlink" name="symlink" :size="size" />
<svg v-else-if="!folder" :class="[iconSizeClass, cssClasses]"> <svg v-else-if="!folder" :key="spriteHref" :class="[iconSizeClass, cssClasses]">
<use v-bind="{ 'xlink:href': spriteHref }" /> <use v-bind="{ 'xlink:href': spriteHref }" />
</svg> </svg>
<gl-icon v-else :name="folderIconName" :size="size" class="folder-icon" /> <gl-icon v-else :name="folderIconName" :size="size" class="folder-icon" />
......
...@@ -61,7 +61,12 @@ export default { ...@@ -61,7 +61,12 @@ export default {
</script> </script>
<template> <template>
<svg :class="[iconSizeClass, iconTestClass]" aria-hidden="true" v-on="$listeners"> <svg
:key="spriteHref"
:class="[iconSizeClass, iconTestClass]"
aria-hidden="true"
v-on="$listeners"
>
<use v-bind="{ 'xlink:href': spriteHref }" /> <use v-bind="{ 'xlink:href': spriteHref }" />
</svg> </svg>
</template> </template>
...@@ -1044,7 +1044,6 @@ strip-json-comments,2.0.1,MIT ...@@ -1044,7 +1044,6 @@ strip-json-comments,2.0.1,MIT
style-loader,0.23.0,MIT style-loader,0.23.0,MIT
supports-color,2.0.0,MIT supports-color,2.0.0,MIT
supports-color,5.5.0,MIT supports-color,5.5.0,MIT
svg4everybody,2.1.9,CC0-1.0
symbol-observable,1.2.0,MIT symbol-observable,1.2.0,MIT
sys-filesystem,1.1.6,Artistic 2.0 sys-filesystem,1.1.6,Artistic 2.0
tapable,1.1.0,MIT tapable,1.1.0,MIT
......
...@@ -11208,11 +11208,6 @@ svg-tags@^1.0.0: ...@@ -11208,11 +11208,6 @@ svg-tags@^1.0.0:
resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
svg4everybody@^2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/svg4everybody/-/svg4everybody-2.1.9.tgz#5bd9f6defc133859a044646d4743fabc28db7e2d"
integrity sha1-W9n23vwTOFmgRGRtR0P6vCjbfi0=
swagger-ui-dist@^3.26.2: swagger-ui-dist@^3.26.2:
version "3.26.2" version "3.26.2"
resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-3.26.2.tgz#22c700906c8911b1c9956da6c3fca371dba6219f" resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-3.26.2.tgz#22c700906c8911b1c9956da6c3fca371dba6219f"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment