aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-02-16 22:01:03 -0500
committerGitHub <noreply@github.com>2020-02-16 22:01:03 -0500
commit2ace8d4ffa89d07a4fb07a410134054a1bccc431 (patch)
tree2da9c4e17b77ce0bf2ba8338e3fe301636667ffb
parent1c6ed1d2866d9912b3b65d9e5addf710a6f26b38 (diff)
parentae4ee9ddee0b791c1039595250db6106e66709fa (diff)
Merge pull request #367 from toasted-nutbread/defer-content-script-css-injection
Defer content script css injection
-rw-r--r--ext/bg/js/backend.js25
-rw-r--r--ext/bg/js/settings/popup-preview-frame.js26
-rw-r--r--ext/fg/js/popup.js144
-rw-r--r--ext/manifest.json1
-rw-r--r--ext/mixed/js/api.js4
5 files changed, 130 insertions, 70 deletions
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index d1a34f82..458ea483 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -499,19 +499,30 @@ class Backend {
return Promise.resolve({frameId});
}
- _onApiInjectStylesheet({css}, sender) {
+ _onApiInjectStylesheet({type, value}, sender) {
if (!sender.tab) {
return Promise.reject(new Error('Invalid tab'));
}
const tabId = sender.tab.id;
const frameId = sender.frameId;
- const details = {
- code: css,
- runAt: 'document_start',
- cssOrigin: 'user',
- allFrames: false
- };
+ const details = (
+ type === 'file' ?
+ {
+ file: value,
+ runAt: 'document_start',
+ cssOrigin: 'author',
+ allFrames: false,
+ matchAboutBlank: true
+ } :
+ {
+ code: value,
+ runAt: 'document_start',
+ cssOrigin: 'user',
+ allFrames: false,
+ matchAboutBlank: true
+ }
+ );
if (typeof frameId === 'number') {
details.frameId = frameId;
}
diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js
index 8fd06222..e900d4e2 100644
--- a/ext/bg/js/settings/popup-preview-frame.js
+++ b/ext/bg/js/settings/popup-preview-frame.js
@@ -22,7 +22,8 @@ class SettingsPopupPreview {
constructor() {
this.frontend = null;
this.apiOptionsGetOld = apiOptionsGet;
- this.popupInjectOuterStylesheetOld = Popup.injectOuterStylesheet;
+ this.popup = null;
+ this.popupSetCustomOuterCssOld = null;
this.popupShown = false;
this.themeChangeTimeout = null;
this.textSource = null;
@@ -50,19 +51,19 @@ class SettingsPopupPreview {
const popupHost = new PopupProxyHost();
await popupHost.prepare();
- const popup = popupHost.getOrCreatePopup();
- popup.setChildrenSupported(false);
+ this.popup = popupHost.getOrCreatePopup();
+ this.popup.setChildrenSupported(false);
- this.frontend = new Frontend(popup);
+ this.popupSetCustomOuterCssOld = this.popup.setCustomOuterCss;
+ this.popup.setCustomOuterCss = (...args) => this.popupSetCustomOuterCss(...args);
+
+ this.frontend = new Frontend(this.popup);
this.frontend.setEnabled = function () {};
this.frontend.searchClear = function () {};
await this.frontend.prepare();
- // Overwrite popup
- Popup.injectOuterStylesheet = (...args) => this.popupInjectOuterStylesheet(...args);
-
// Update search
this.updateSearch();
}
@@ -83,14 +84,13 @@ class SettingsPopupPreview {
return options;
}
- popupInjectOuterStylesheet(...args) {
+ async popupSetCustomOuterCss(...args) {
// This simulates the stylesheet priorities when injecting using the web extension API.
- const result = this.popupInjectOuterStylesheetOld(...args);
+ const result = await this.popupSetCustomOuterCssOld.call(this.popup, ...args);
- const outerStylesheet = Popup.outerStylesheet;
const node = document.querySelector('#client-css');
- if (node !== null && outerStylesheet !== null) {
- node.parentNode.insertBefore(outerStylesheet, node);
+ if (node !== null && result !== null) {
+ node.parentNode.insertBefore(result, node);
}
return result;
@@ -137,7 +137,7 @@ class SettingsPopupPreview {
setCustomOuterCss(css) {
if (this.frontend === null) { return; }
- this.frontend.popup.setCustomOuterCss(css, true);
+ this.frontend.popup.setCustomOuterCss(css, false);
}
async updateSearch() {
diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js
index 45203c03..59c46ab8 100644
--- a/ext/fg/js/popup.js
+++ b/ext/fg/js/popup.js
@@ -31,7 +31,6 @@ class Popup {
this._visible = false;
this._visibleOverride = null;
this._options = null;
- this._stylesheetInjectedViaApi = false;
this._contentScale = 1.0;
this._containerSizeContentScale = null;
@@ -158,21 +157,13 @@ class Popup {
this._container.dataset.yomichanSiteColor = this._getSiteColor();
}
- async setCustomOuterCss(css, injectDirectly) {
- // Cannot repeatedly inject stylesheets using web extension APIs since there is no way to remove them.
- if (this._stylesheetInjectedViaApi) { return; }
-
- if (injectDirectly || Popup._isOnExtensionPage()) {
- Popup.injectOuterStylesheet(css);
- } else {
- if (!css) { return; }
- try {
- await apiInjectStylesheet(css);
- this._stylesheetInjectedViaApi = true;
- } catch (e) {
- // NOP
- }
- }
+ async setCustomOuterCss(css, useWebExtensionApi) {
+ return await Popup._injectStylesheet(
+ 'yomichan-popup-outer-user-stylesheet',
+ 'code',
+ css,
+ useWebExtensionApi
+ );
}
setChildrenSupported(value) {
@@ -187,26 +178,6 @@ class Popup {
return this._container.getBoundingClientRect();
}
- static injectOuterStylesheet(css) {
- if (Popup.outerStylesheet === null) {
- if (!css) { return; }
- Popup.outerStylesheet = document.createElement('style');
- Popup.outerStylesheet.id = 'yomichan-popup-outer-stylesheet';
- }
-
- const outerStylesheet = Popup.outerStylesheet;
- if (css) {
- outerStylesheet.textContent = css;
-
- const par = document.head;
- if (par && outerStylesheet.parentNode !== par) {
- par.appendChild(outerStylesheet);
- }
- } else {
- outerStylesheet.textContent = '';
- }
- }
-
// Private functions
_inject() {
@@ -248,10 +219,24 @@ class Popup {
});
this._observeFullscreen(true);
this._onFullscreenChanged();
- this.setCustomOuterCss(this._options.general.customPopupOuterCss, false);
+ this._injectStyles();
});
}
+ async _injectStyles() {
+ try {
+ await Popup._injectStylesheet('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true);
+ } catch (e) {
+ // NOP
+ }
+
+ try {
+ await this.setCustomOuterCss(this._options.general.customPopupOuterCss, true);
+ } catch (e) {
+ // NOP
+ }
+ }
+
_observeFullscreen(observe) {
if (!observe) {
this._fullscreenEventListeners.removeAllEventListeners();
@@ -526,15 +511,6 @@ class Popup {
];
}
- static _isOnExtensionPage() {
- try {
- const url = chrome.runtime.getURL('/');
- return window.location.href.substring(0, url.length) === url;
- } catch (e) {
- // NOP
- }
- }
-
static _getViewport(useVisualViewport) {
const visualViewport = window.visualViewport;
if (visualViewport !== null && typeof visualViewport === 'object') {
@@ -567,6 +543,80 @@ class Popup {
bottom: window.innerHeight
};
}
+
+ static _isOnExtensionPage() {
+ try {
+ const url = chrome.runtime.getURL('/');
+ return window.location.href.substring(0, url.length) === url;
+ } catch (e) {
+ // NOP
+ }
+ }
+
+ static async _injectStylesheet(id, type, value, useWebExtensionApi) {
+ const injectedStylesheets = Popup._injectedStylesheets;
+
+ if (Popup._isOnExtensionPage()) {
+ // Permissions error will occur if trying to use the WebExtension API to inject
+ // into an extension page.
+ useWebExtensionApi = false;
+ }
+
+ let styleNode = injectedStylesheets.get(id);
+ if (typeof styleNode !== 'undefined') {
+ if (styleNode === null) {
+ // Previously injected via WebExtension API
+ throw new Error(`Stylesheet with id ${id} has already been injected using the WebExtension API`);
+ }
+ } else {
+ styleNode = null;
+ }
+
+ if (useWebExtensionApi) {
+ // Inject via WebExtension API
+ if (styleNode !== null && styleNode.parentNode !== null) {
+ styleNode.parentNode.removeChild(styleNode);
+ }
+
+ await apiInjectStylesheet(type, value);
+
+ injectedStylesheets.set(id, null);
+ return null;
+ }
+
+ // Create node in document
+ const parentNode = document.head;
+ if (parentNode === null) {
+ throw new Error('No parent node');
+ }
+
+ // Create or reuse node
+ const isFile = (type === 'file');
+ const tagName = isFile ? 'link' : 'style';
+ if (styleNode === null || styleNode.nodeName.toLowerCase() !== tagName) {
+ if (styleNode !== null && styleNode.parentNode !== null) {
+ styleNode.parentNode.removeChild(styleNode);
+ }
+ styleNode = document.createElement(tagName);
+ styleNode.id = id;
+ }
+
+ // Update node style
+ if (isFile) {
+ styleNode.rel = value;
+ } else {
+ styleNode.textContent = value;
+ }
+
+ // Update parent
+ if (styleNode.parentNode !== parentNode) {
+ parentNode.appendChild(styleNode);
+ }
+
+ // Add to map
+ injectedStylesheets.set(id, styleNode);
+ return styleNode;
+ }
}
-Popup.outerStylesheet = null;
+Popup._injectedStylesheets = new Map();
diff --git a/ext/manifest.json b/ext/manifest.json
index 68a8adb4..b86459f9 100644
--- a/ext/manifest.json
+++ b/ext/manifest.json
@@ -30,7 +30,6 @@
"fg/js/frontend.js",
"fg/js/frontend-initialize.js"
],
- "css": ["fg/css/client.css"],
"match_about_blank": true,
"all_frames": true
}],
diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js
index 86bdc73c..14900ecf 100644
--- a/ext/mixed/js/api.js
+++ b/ext/mixed/js/api.js
@@ -89,8 +89,8 @@ function apiFrameInformationGet() {
return _apiInvoke('frameInformationGet');
}
-function apiInjectStylesheet(css) {
- return _apiInvoke('injectStylesheet', {css});
+function apiInjectStylesheet(type, value) {
+ return _apiInvoke('injectStylesheet', {type, value});
}
function apiGetEnvironmentInfo() {