Skip to content

Commit

Permalink
broadcast via storage
Browse files Browse the repository at this point in the history
  • Loading branch information
tophf committed Feb 11, 2025
1 parent dc4db56 commit 502d969
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 111 deletions.
3 changes: 2 additions & 1 deletion src/.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ declare namespace Injection {
dark?: boolean;
name?: boolean;
nonce?: string;
top?: string | false;
top?: boolean;
topUrl?: string;
off?: boolean;
order?: Order;
wake?: boolean;
Expand Down
11 changes: 1 addition & 10 deletions src/background/broadcast-injector-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as prefs from '@/js/prefs';
import {isEmptyObj} from '@/js/util';
import {broadcast} from './broadcast';
import {bgBusy, onSchemeChange} from './common';
import {getUrlOrigin} from './tab-util';

let cfg;
let sentCfg = {};
Expand Down Expand Up @@ -39,18 +38,10 @@ const data = {
cfg,
};

function setTop(tab) {
data.cfg.top = tab && getUrlOrigin(tab.url);
return data;
}

function throttle() {
if (!isEmptyObj(cfg)) {
data.cfg = cfg;
broadcast(data, {
getData: cfg.top && setTop,
onlyIfStyled: !('off' in cfg || 'dark' in cfg),
});
broadcast(data);
}
sentCfg = cfg;
cfg = null;
Expand Down
91 changes: 9 additions & 82 deletions src/background/broadcast.js
Original file line number Diff line number Diff line change
@@ -1,91 +1,18 @@
import '@/js/browser';
import {kStyleIds, kUrl} from '@/js/consts';
import {kBroadcast} from '@/js/consts';
import {rxIgnorableError} from '@/js/msg-api';
import {ownRoot, supported} from '@/js/urls';
import {getActiveTab} from '@/js/util-webext';
import tabCache, {someInjectable} from './tab-manager';
import {getWindowClients} from './util';
import {chromeLocal, chromeSession} from '@/js/storage-util';

let /**@type{?[]}*/toBroadcast;
let /**@type{boolean[]}*/toBroadcastStyled;
let broadcasting;
let toBroadcast;

/**
* @param {?} data
* @param {{}} [opts]
* @param {boolean} [opts.onlyIfStyled] - only tabs that are known to contain styles
* @param {(tab?:Tab)=>?} [opts.getData] - provides data for this tab, nullish result = skips tab
* @return {Promise<?[]>}
*/
export function broadcast(data, {onlyIfStyled, getData} = {}) {
if (getData && !(data = getData())) {
return;
}
(toBroadcast ??= []).push(data);
(toBroadcastStyled ??= []).push(!!onlyIfStyled);
broadcasting ??= setTimeout(doBroadcast);
export function broadcast(data) {
toBroadcast ??= (setTimeout(doBroadcast), []);
toBroadcast.push(data);
}

async function doBroadcast() {
const jobs = [];
const nStyled = toBroadcastStyled.indexOf(false);
const bAllStyled = nStyled < 0;
const [client, tabs, activeTab] = await Promise.all([
!__.MV3 || getWindowClients(), // TODO: detect the popup in Chrome MV2 incognito window?
bAllStyled ? [] : browser.tabs.query({}),
bAllStyled && someInjectable() && getActiveTab(),
]);
const data = toBroadcast;
const styled = toBroadcastStyled;
toBroadcast = toBroadcastStyled = broadcasting = null;
let msgUnstyled, msgStyled;
// filter supported tabs in-place and move the active tab to the beginning
let tabsLen = 0;
if (!bAllStyled) {
for (let i = 0, t, url; i < tabs.length; i++) {
t = tabs[i];
if (t.discarded || !(url = t.url) || url.startsWith(ownRoot) || !supported(t.url)) {
continue;
}
if (i && t.active) {
tabs[tabsLen] = tabs[0];
tabs[0] = t.id;
} else {
tabs[tabsLen] = t.id;
}
tabsLen++;
}
tabs.length = tabsLen;
} else if (activeTab) {
for (const {id, [kUrl]: url, [kStyleIds]: styleIds} of Object.values(tabCache)) {
if (styleIds && url && url[0] && !url[0].startsWith(ownRoot)) {
tabsLen = tabs[id === activeTab.id ? 'unshift' : 'push'](id);
}
}
}
if (client) {
jobs.push(broadcastExtension(
data.length > 1 ? data : data[0],
data.length > 1
));
} else if (!tabsLen) {
return;
}
for (const tabId of tabs) {
const msg = !nStyled || tabCache[tabId]?.[kStyleIds]
? msgStyled ??= data
: msgUnstyled ??= data.filter((v, i) => !styled[i]);
const num = msg.length;
if (num && jobs.push(sendTab(
tabId,
num > 1 ? msg : msg[0],
undefined,
num > 1
)) > 20) {
await Promise.all(jobs.splice(0));
}
}
await Promise.all(jobs);
function doBroadcast() {
(chromeSession || chromeLocal).set({[kBroadcast]: toBroadcast});
toBroadcast = null;
}

export function broadcastExtension(data, multi) {
Expand Down
8 changes: 6 additions & 2 deletions src/background/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {DNR, getRuleIds, updateDynamicRules, updateSessionRules} from '@/js/dnr'
import {_execute, onMessage} from '@/js/msg';
import {API} from '@/js/msg-api';
import * as prefs from '@/js/prefs';
import {chromeSession} from '@/js/storage-util';
import {CHROME, FIREFOX, MOBILE, WINDOWS} from '@/js/ua';
import {sleep} from '@/js/util';
import {broadcast, pingTab} from './broadcast';
Expand Down Expand Up @@ -89,6 +90,9 @@ chrome.runtime.onInstalled.addListener(({reason, previousVersion}) => {
styleCache.clear(),
);
if (__.MV3) {
chromeSession.setAccessLevel({
accessLevel: 'TRUSTED_AND_UNTRUSTED_CONTEXTS',
});
(bgPreInit.length ? bgPreInit : bgInit).push(
stateDB.clear(),
DNR.getDynamicRules().then(rules => updateDynamicRules(undefined, getRuleIds(rules))),
Expand All @@ -105,10 +109,10 @@ chrome.runtime.onInstalled.addListener(({reason, previousVersion}) => {
});

if (__.MV3) {
chrome.storage.session.get('init', async ({init}) => {
chromeSession.get('init', async ({init}) => {
__.DEBUGLOG('new session:', !init);
if (init) return;
chrome.storage.session.set({init: true});
chromeSession.set({init: true});
onStartup();
await bgBusy;
reinjectContentScripts();
Expand Down
8 changes: 3 additions & 5 deletions src/background/style-manager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,8 @@ export function getSectionsByUrl(url, id, isInitialApply) {
// TODO: enable in FF when it supports sourceURL comment in style elements (also options.html)
name: p.exposeStyleName,
nonce: td.nonce?.[frameId],
top: isInitialApply && p.exposeIframes && (
isTop ? '' // apply.js will use location.origin
: getUrlOrigin(tab.url || td[kUrl]?.[0])
),
top: p.exposeIframes,
topUrl: isTop ? '' : getUrlOrigin(tab.url || td[kUrl]?.[0]),
wake: p[pKeepAlive] >= 0,
order,
};
Expand Down Expand Up @@ -318,7 +316,7 @@ export function remove(id, reason) {
broadcast({
method: 'styleDeleted',
style: {id},
}, {onlyIfStyled: true});
});
return id;
}

Expand Down
2 changes: 1 addition & 1 deletion src/background/style-manager/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function broadcastStyleUpdated(style, reason, isNew) {
id: style.id,
enabled: style.enabled,
},
}, {onlyIfStyled: !style.enabled});
});
}

export async function setOrderImpl(data, {
Expand Down
37 changes: 27 additions & 10 deletions src/content/apply.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// WARNING: make sure util-webext.js runs first and sets _deepCopy
import {k_deepCopy, kApplyPort} from '@/js/consts';
import {k_deepCopy, kApplyPort, kBroadcast} from '@/js/consts';
import {onMessage} from '@/js/msg';
import {API, isFrame, TDM, updateTDM} from '@/js/msg-api';
import * as styleInjector from './style-injector';
Expand Down Expand Up @@ -73,6 +73,10 @@ styleInjector.selfDestruct = selfDestruct;
init();
onMessage.set(applyOnMessage, true);
addEventListener(kPageShow, onBFCache);
{
const S = chrome.storage;
(S.session || S.local.onChanged && S.local || S).onChanged.addListener(onStorage);
}

async function init() {
if (isUnstylable) return API.styleViaAPI({method: 'styleApply'});
Expand Down Expand Up @@ -103,7 +107,8 @@ async function applyStyles(data, isInitial = !own.sections) {
if (!data) data = await API.styles.getSectionsByUrl(matchUrl, null, isInitial);
if (!data.cfg) data.cfg = own.cfg;
Object.assign(own, global[Symbol.for(SYM_ID)] = data);
if (!isFrame && own.cfg.top === '') own.cfg.top = location.origin; // used by child frames via parentStyles
// used by child frames via parentStyles
if (!isFrame && own.cfg.topUrl === '') own.cfg.topUrl = location.origin;
if (!isFrame && own.cfg.dark !== (mqDark ?? initMQ()).matches) mqDark.onchange(mqDark);
if (styleInjector.list.length) styleInjector.apply(own, true);
else if (!own.cfg.off) styleInjector.apply(own);
Expand All @@ -125,15 +130,18 @@ function getStylesViaXhr() {
}

function applyOnMessage(req, sender, multi) {
if (multi) {
if (req[1]) {
throttled ??= Promise.resolve().then(processThrottled) && [];
throttled.push(req);
return;
}
req = req[0];
}
if (isUnstylable && /^(style|urlChanged)/.test(req.method)) {
API.styleViaAPI(req);
return;
}
if (multi) {
throttled ??= Promise.resolve().then(processThrottled) && [];
throttled.push(req);
return;
}
const {style} = req;
switch (req.method) {
case 'ping':
Expand Down Expand Up @@ -189,7 +197,7 @@ function updateConfig({cfg}) {
for (const /** @type {keyof Injection.Config}*/ k in cfg) {
const v = cfg[k];
if (v === own.cfg[k]) continue;
if (k === 'top' && !isFrame) continue;
if (!isFrame && (k === 'top' || k === 'topUrl')) continue;
own.cfg[k] = v;
if (k === 'off') updateDisableAll();
else if (k === 'order') styleInjector.sort();
Expand All @@ -215,8 +223,9 @@ function updateExposeIframes() {
if (!el) return; // got no styles so styleInjector didn't wait for <html>
if (!own.cfg.top || !styleInjector.list.length) {
if (el.hasAttribute(attr)) el.removeAttribute(attr);
} else if (el.getAttribute(attr) !== own.cfg.top) { // Checking first to avoid DOM mutations
el.setAttribute(attr, own.cfg.top);
} else if (el.getAttribute(attr) !== own.cfg.topUrl) {
// Checking first to avoid DOM mutations
el.setAttribute(attr, own.cfg.topUrl);
}
}

Expand Down Expand Up @@ -300,6 +309,14 @@ function onReified(e) {
}
}

async function onStorage(changes) {
if ((changes = changes[kBroadcast]) && (changes = changes.newValue)) {
if (document.visibilityState !== 'visible')
await new Promise(setTimeout);
applyOnMessage(changes, null, true);
}
}

function onUrlChanged() {
updateUrl(parent.location.href);
}
Expand Down
1 change: 1 addition & 0 deletions src/js/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const kAboutBlank = 'about:blank';
export const kAppJson = 'application/json';
export const kAppUrlencoded = 'application/x-www-form-urlencoded';
export const kApplyPort = 'apply';
export const kBroadcast = 'broadcast';
export const kCodeMirror = 'CodeMirror';
export const kContentType = 'content-type'; // must be lowercase!
export const kCssPropSuffix = ': ';
Expand Down
2 changes: 2 additions & 0 deletions src/js/msg.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ function onRuntimeMessage({data, multi, TDM}, sender, sendResponse) {
if (__.ENTRY === true && !__.IS_BG && data.method === kInvokeAPI) {
return;
}
if (multi && !(multi = data.length > 1))
data = data[0];
sender.TDM = TDM;
let res = __.IS_BG && global[k_busy];
res = res
Expand Down

0 comments on commit 502d969

Please sign in to comment.