Skip to content

Commit 85a5209

Browse files
committed
wake up SW earlier to avoid FOUC
1 parent 5d89669 commit 85a5209

12 files changed

Lines changed: 109 additions & 26 deletions

File tree

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
"@babel/core": "^7.26.0",
2323
"@babel/plugin-transform-runtime": "^7.25.9",
2424
"@babel/preset-env": "^7.26.0",
25+
"@types/chrome": "^0.0.299",
2526
"@types/codemirror": "^5.60.15",
27+
"@types/firefox-webext-browser": "^120.0.4",
2628
"@types/webpack": "^5.28.5",
2729
"autoprefixer": "^10.4.20",
2830
"babel-loader": "^9.2.1",
2931
"chalk": "^4.1.2",
32+
"chrome-types": "^0.1.334",
3033
"copy-webpack-plugin": "^12.0.2",
3134
"css-loader": "^7.1.2",
3235
"css-minimizer-webpack-plugin": "^7.0.0",

pnpm-lock.yaml

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/.types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ declare namespace Injection {
9191
top?: string | false;
9292
off?: boolean;
9393
order?: Order;
94+
wake?: boolean;
9495
}
9596
interface Order {
9697
main: Record<string,number>;

src/background/broadcast-injector-config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {pKeepAlive} from '@/js/consts';
12
import * as prefs from '@/js/prefs';
23
import {isEmptyObj} from '@/js/util';
34
import {broadcast} from './broadcast';
@@ -9,6 +10,7 @@ let sentCfg = {};
910
const INJECTOR_CONFIG_MAP = {
1011
exposeIframes: 'top',
1112
disableAll: 'off',
13+
[pKeepAlive]: 'wake',
1214
styleViaASS: 'ass',
1315
};
1416

@@ -19,6 +21,8 @@ onSchemeChange.add(broadcastInjectorConfig.bind(null, 'dark'));
1921

2022
export default function broadcastInjectorConfig(key, val) {
2123
key = INJECTOR_CONFIG_MAP[key] || key;
24+
if (key === pKeepAlive)
25+
val = val >= 0;
2226
if (!cfg) {
2327
cfg = {};
2428
cfg[key] = val;

src/background/style-manager/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {IMPORT_THROTTLE, k_size, kStyleViaXhr, kUrl, UCD} from '@/js/consts';
1+
import {IMPORT_THROTTLE, k_size, kStyleViaXhr, kUrl, pKeepAlive, UCD} from '@/js/consts';
22
import * as prefs from '@/js/prefs';
33
import {calcStyleDigest, styleCodeEmpty} from '@/js/sections-util';
44
import {calcObjSize, mapObj} from '@/js/util';
@@ -188,6 +188,7 @@ export function getSectionsByUrl(url, id, isInitialApply) {
188188
isTop ? '' // apply.js will use location.origin
189189
: getUrlOrigin(tab.url || td[kUrl]?.[0])
190190
),
191+
wake: p[pKeepAlive] >= 0,
191192
order,
192193
};
193194
if (isInitialApply === 'cfg') {

src/background/sw/keep-alive.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {pKeepAlive} from '@/js/consts';
12
import * as prefs from '@/js/prefs';
23
import {bgBusy} from '../common';
34

@@ -12,7 +13,7 @@ let idleDuration;
1213

1314
keepAlive(bgBusy);
1415
__.KEEP_ALIVE = keepAlive;
15-
prefs.subscribe('keepAlive', (_, val) => {
16+
prefs.subscribe(pKeepAlive, (_, val) => {
1617
idleDuration = Math.max(30, val * 60 | 0/*to integer*/ || 0/*if val is not a number*/);
1718
TTL = val * 60e3;
1819
if (!pulse || !TTL && !busy) reschedule();

src/background/tab-manager.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,6 @@ function onPortDisconnected(port) {
131131
const {sender} = port;
132132
const tabId = sender.tab?.id;
133133
const frameId = sender.frameId;
134+
if (!frameId) return; // ignoring unload of previous page while navigating to a new URL
134135
for (const fn of onUnload) fn(tabId, frameId, port);
135136
}

src/content/apply.js

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {k_deepCopy, kApplyPort} from '@/js/consts';
33
import {onMessage} from '@/js/msg';
44
import {API, isFrame, TDM, updateTDM} from '@/js/msg-api';
55
import * as styleInjector from './style-injector';
6-
import {FF, isXml, own, ownId} from './style-injector';
6+
import {FF, isXml, own, ownId, runtime} from './style-injector';
77

88
const SYM_ID = 'styles';
99
const kPageShow = 'pageshow';
@@ -27,7 +27,7 @@ const instanceId = (FF || !__.MV3 && !CSS.supports('top', '1ic')) && Math.random
2727
* so we'll add the styles only if the iframe becomes visible */
2828
const xoEventId = `${instanceId || Math.random()}`;
2929

30-
const NAV_ID = 'url:' + chrome.runtime.id;
30+
const NAV_ID = 'url:' + runtime.id;
3131
/** Firefox disallows direct access to global variables in the parent's "isolated world".
3232
* Chrome 63 and older can't construct EventTarget, so we detect them via ResizeObserver,
3333
* using a typeof check to skip an implicit global for <html id="ResizeObserver"> */
@@ -70,16 +70,8 @@ if (TDM < 0) {
7070
}
7171
styleInjector.onInjectorUpdate = () => {
7272
updateCount();
73-
if (isFrame) {
74-
updateExposeIframes();
75-
if (!styleInjector.list.length) {
76-
port?.disconnect();
77-
port = null;
78-
} else if (!port) {
79-
port = chrome.runtime.connect({name: kApplyPort});
80-
port.onDisconnect.addListener(() => (port = null));
81-
}
82-
}
73+
if (isFrame) updateExposeIframes();
74+
if (isFrame || own.cfg.wake) updatePort();
8375
};
8476
styleInjector.selfDestruct = selfDestruct;
8577
// Declare all vars before init() or it'll throw due to "temporal dead zone" of const/let
@@ -98,7 +90,8 @@ async function init() {
9890
else data = !__.ENTRY && !isFrameSameOrigin && !isXml && getStylesViaXhr();
9991
// XML in Chrome will be auto-converted to html later, so we can't style it via XHR now
10092
}
101-
if (!chrome.runtime.id) return selfDestruct();
93+
if (!runtime.id)
94+
return selfDestruct();
10295
await applyStyles(data, true);
10396
}
10497

@@ -118,7 +111,7 @@ function getStylesViaXhr() {
118111
try {
119112
const blobId = (document.cookie.split(ownId + '=')[1] || '').split(';')[0];
120113
if (!blobId) return; // avoiding an exception so we don't spoil debugging in devtools
121-
const url = 'blob:' + chrome.runtime.getURL(blobId);
114+
const url = 'blob:' + runtime.getURL(blobId);
122115
document.cookie = `${ownId}=1; max-age=0; SameSite=Lax`; // remove our cookie
123116
const xhr = new XMLHttpRequest();
124117
xhr.open('GET', url, false); // synchronous
@@ -189,14 +182,15 @@ function processThrottled() {
189182
}
190183

191184
function updateConfig({cfg}) {
192-
for (const k in cfg) {
185+
for (const /** @type {keyof Injection.Config}*/ k in cfg) {
193186
const v = cfg[k];
194187
if (v === own.cfg[k]) continue;
195188
if (k === 'top' && !isFrame) continue;
196189
own.cfg[k] = v;
197190
if (k === 'off') updateDisableAll();
198191
else if (k === 'order') styleInjector.sort();
199192
else if (k === 'top') updateExposeIframes();
193+
else if (k === 'wake' && __.MV3) updatePort();
200194
else styleInjector.updateConfig(own.cfg);
201195
}
202196
}
@@ -234,6 +228,16 @@ function updateCount() {
234228
}
235229
}
236230

231+
function updatePort() {
232+
if (!(__.MV3 && own.cfg.wake) && !styleInjector.list.length) {
233+
port?.disconnect();
234+
port = null;
235+
} else if (!port && (isFrame || __.MV3 && own.cfg.wake)) {
236+
port = runtime.connect({name: kApplyPort});
237+
port.onDisconnect.addListener(onPortDisconnected);
238+
}
239+
}
240+
237241
function updateUrl(url) {
238242
if (url !== matchUrl) {
239243
matchUrl = url;
@@ -249,7 +253,8 @@ function onFrameElementInView(cb) {
249253

250254
/** @param {IntersectionObserverEntry[]} entries */
251255
function onIntersect(entries) {
252-
if (!chrome.runtime.id) return selfDestruct();
256+
if (!runtime.id)
257+
return selfDestruct();
253258
for (const e of entries) {
254259
if (e.intersectionRatio) {
255260
xo.unobserve(e.target);
@@ -259,15 +264,30 @@ function onIntersect(entries) {
259264
}
260265

261266
function onBFCache(e) {
262-
if (!chrome.runtime.id) return selfDestruct();
267+
if (!runtime.id)
268+
return selfDestruct();
263269
if (e.isTrusted && e.persisted) {
264270
throttledCount = '';
265271
init(); // styles may have been toggled while we were in bfcache
266272
}
267273
}
268274

275+
function onPortDisconnected() {
276+
if (__.MV3 && own.cfg.wake)
277+
addEventListener('mousedown', wakeUpSW, true);
278+
port = null;
279+
}
280+
281+
function wakeUpSW(e) {
282+
if (!runtime.id)
283+
return selfDestruct();
284+
if (!port && e.target.closest('a')?.href)
285+
updatePort();
286+
}
287+
269288
function onReified(e) {
270-
if (!chrome.runtime.id) return selfDestruct();
289+
if (!runtime.id)
290+
return selfDestruct();
271291
if (e.isTrusted) {
272292
updateTDM(2);
273293
document.onprerenderingchange = null;
@@ -287,6 +307,7 @@ function selfDestruct() {
287307
if (mqDark) mqDark.onchange = null;
288308
if (offscreen) for (const fn of offscreen) fn();
289309
if (TDM < 0) document.onprerenderingchange = null;
310+
if (__.MV3) removeEventListener('mousedown', wakeUpSW, true);
290311
navHubParent?.removeEventListener(NAV_ID, onUrlChanged, true);
291312
offscreen = null;
292313
styleInjector.shutdown();

src/content/style-injector.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const kAss = 'adoptedStyleSheets';
1010
export const own = /** @type {Injection.Response} */{
1111
cfg: {off: false, top: ''},
1212
};
13-
export const ownId = chrome.runtime.id;
13+
export const runtime = chrome.runtime;
14+
export const ownId = runtime.id;
1415
export const isXml = !__.ENTRY && document instanceof XMLDocument;
1516
const wrappedDoc = __.BUILD !== 'chrome' && FF && document.wrappedJSObject
1617
|| document;
@@ -221,7 +222,7 @@ function setTextAndName(el, {id, code, name}) {
221222
if (el.dataset.name !== name) el.dataset.name = name;
222223
if (!FF) { // Firefox doesn't support sourceURL comment in CSS
223224
name = encodeURIComponent(name.replace(/[?#/']/g, toSafeChar));
224-
code += `\n/*# sourceURL=${chrome.runtime.getURL(name)}.user.css#${id}${
225+
code += `\n/*# sourceURL=${runtime.getURL(name)}.user.css#${id}${
225226
window !== top ? '#' + Math.random().toString(36).slice(2) : '' // https://crbug.com/1298600
226227
} */`;
227228
}
@@ -297,7 +298,8 @@ function removeStyle(style) {
297298
}
298299

299300
function restoreOrder(mutations) {
300-
if (!chrome.runtime.id) return selfDestruct();
301+
if (!runtime.id)
302+
return selfDestruct();
301303
let bad;
302304
let el = list.length && list[0].el;
303305
if (!el) {
@@ -360,7 +362,8 @@ export function updateConfig(cfg) {
360362
}
361363

362364
function updateRoot() {
363-
if (!chrome.runtime.id) return selfDestruct();
365+
if (!runtime.id)
366+
return selfDestruct();
364367
// Known to change mysteriously in iframes without triggering RewriteObserver
365368
if (root !== document.documentElement) {
366369
root = document.documentElement;

src/js/consts.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ export const IMPORT_THROTTLE = 100; //ms
4343

4444
export const BIT_DARK = 1;
4545
export const BIT_SYS_DARK = 2;
46+
47+
//#region prefs
48+
export const pKeepAlive = 'keepAlive';
49+
//#endregion

0 commit comments

Comments
 (0)