From 3e68af8666bdf9a6d8d605f7a3bb0432c8d6cb33 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Wed, 24 Jun 2020 21:46:13 -0400 Subject: Shadow DOM container for popup iframes (#623) * Add support for injecting stylesheets into a custom parent node * Add api.getStylesheetContent * Add support for injecting a CSS file's content * Add usePopupShadowDom option * Use a per-parentNode cache * Add support for using a shadow DOM wrapper around popup iframes * Ignore the popup container instead of the frame --- ext/mixed/js/api.js | 4 ++++ ext/mixed/js/dynamic-loader.js | 49 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 9 deletions(-) (limited to 'ext/mixed') diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index c54196e2..5c17d50e 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -121,6 +121,10 @@ const api = (() => { return this._invoke('injectStylesheet', {type, value}); } + getStylesheetContent(url) { + return this._invoke('getStylesheetContent', {url}); + } + getEnvironmentInfo() { return this._invoke('getEnvironmentInfo'); } diff --git a/ext/mixed/js/dynamic-loader.js b/ext/mixed/js/dynamic-loader.js index 37f85112..981d1ee5 100644 --- a/ext/mixed/js/dynamic-loader.js +++ b/ext/mixed/js/dynamic-loader.js @@ -21,14 +21,36 @@ const dynamicLoader = (() => { const injectedStylesheets = new Map(); + const injectedStylesheetsWithParent = new WeakMap(); - async function loadStyle(id, type, value, useWebExtensionApi=false) { + function getInjectedStylesheet(id, parentNode) { + if (parentNode === null) { + return injectedStylesheets.get(id); + } + const map = injectedStylesheetsWithParent.get(parentNode); + return typeof map !== 'undefined' ? map.get(id) : void 0; + } + + function setInjectedStylesheet(id, parentNode, value) { + if (parentNode === null) { + injectedStylesheets.set(id, value); + return; + } + let map = injectedStylesheetsWithParent.get(parentNode); + if (typeof map === 'undefined') { + map = new Map(); + injectedStylesheetsWithParent.set(parentNode, map); + } + map.set(id, value); + } + + async function loadStyle(id, type, value, useWebExtensionApi=false, parentNode=null) { if (useWebExtensionApi && yomichan.isExtensionUrl(window.location.href)) { // Permissions error will occur if trying to use the WebExtension API to inject into an extension page useWebExtensionApi = false; } - let styleNode = injectedStylesheets.get(id); + let styleNode = getInjectedStylesheet(id, parentNode); if (typeof styleNode !== 'undefined') { if (styleNode === null) { // Previously injected via WebExtension API @@ -38,21 +60,30 @@ const dynamicLoader = (() => { styleNode = null; } + if (type === 'file-content') { + value = await api.getStylesheetContent(value); + type = 'code'; + useWebExtensionApi = false; + } + if (useWebExtensionApi) { // Inject via WebExtension API if (styleNode !== null && styleNode.parentNode !== null) { styleNode.parentNode.removeChild(styleNode); } - injectedStylesheets.set(id, null); + setInjectedStylesheet(id, parentNode, null); await api.injectStylesheet(type, value); return null; } // Create node in document - const parentNode = document.head; - if (parentNode === null) { - throw new Error('No parent node'); + let parentNode2 = parentNode; + if (parentNode2 === null) { + parentNode2 = document.head; + if (parentNode2 === null) { + throw new Error('No parent node'); + } } // Create or reuse node @@ -74,12 +105,12 @@ const dynamicLoader = (() => { } // Update parent - if (styleNode.parentNode !== parentNode) { - parentNode.appendChild(styleNode); + if (styleNode.parentNode !== parentNode2) { + parentNode2.appendChild(styleNode); } // Add to map - injectedStylesheets.set(id, styleNode); + setInjectedStylesheet(id, parentNode, styleNode); return styleNode; } -- cgit v1.2.3