diff options
Diffstat (limited to 'app/assets/javascripts/lib/chrome_84_icon_fix.js')
-rw-r--r-- | app/assets/javascripts/lib/chrome_84_icon_fix.js | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/app/assets/javascripts/lib/chrome_84_icon_fix.js b/app/assets/javascripts/lib/chrome_84_icon_fix.js new file mode 100644 index 00000000000..60497186c19 --- /dev/null +++ b/app/assets/javascripts/lib/chrome_84_icon_fix.js @@ -0,0 +1,78 @@ +import { debounce } from 'lodash'; + +/* + Chrome and Edge 84 have a bug relating to icon sprite svgs + https://bugs.chromium.org/p/chromium/issues/detail?id=1107442 + + If the SVG is loaded, under certain circumstances the icons are not + shown. We load our sprite icons with JS and add them to the body. + 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', async () => { + 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(); + } +}); |