fix USO install button
This commit is contained in:
parent
efc6d09d49
commit
d1f5468a81
|
@ -10,13 +10,12 @@
|
||||||
const USO = 'https://userstyles.org';
|
const USO = 'https://userstyles.org';
|
||||||
const apiUrl = `${USO}/api/v1/styles/${usoId}`;
|
const apiUrl = `${USO}/api/v1/styles/${usoId}`;
|
||||||
const md5Url = `https://update.userstyles.org/${usoId}.md5`;
|
const md5Url = `https://update.userstyles.org/${usoId}.md5`;
|
||||||
const CLICK = {
|
const CLICK = [
|
||||||
customize: '.customize_button',
|
['#install_stylish_style_button', onInstall],
|
||||||
install: '#install_style_button',
|
['#update_stylish_style_button', onInstall],
|
||||||
uninstall: '#uninstall_style_button',
|
['.customize_style_button', onCustomize],
|
||||||
update: '#update_style_button',
|
['.uninstall_stylish_style_button', onUninstall],
|
||||||
};
|
];
|
||||||
const CLICK_SEL = Object.values(CLICK).join(',');
|
|
||||||
const pageEventId = `${performance.now()}${Math.random()}`;
|
const pageEventId = `${performance.now()}${Math.random()}`;
|
||||||
const contentEventId = pageEventId + ':';
|
const contentEventId = pageEventId + ':';
|
||||||
const orphanEventId = chrome.runtime.id; // id won't be available in the orphaned script
|
const orphanEventId = chrome.runtime.id; // id won't be available in the orphaned script
|
||||||
|
@ -43,7 +42,7 @@
|
||||||
document.body || new Promise(resolve => addEventListener('load', resolve, {once: true})),
|
document.body || new Promise(resolve => addEventListener('load', resolve, {once: true})),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!dup.id) {
|
if (!dup) {
|
||||||
sendStylishEvent('styleCanBeInstalledChrome');
|
sendStylishEvent('styleCanBeInstalledChrome');
|
||||||
} else if (dup.originalMd5 && dup.originalMd5 !== md5 || !dup.usercssData || !dup.md5Url) {
|
} else if (dup.originalMd5 && dup.originalMd5 !== md5 || !dup.usercssData || !dup.md5Url) {
|
||||||
// allow update if 1) changed, 2) is a classic USO style, 3) is from USO-archive
|
// allow update if 1) changed, 2) is a classic USO style, 3) is from USO-archive
|
||||||
|
@ -53,38 +52,47 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onClick(e) {
|
async function onClick(e) {
|
||||||
const el = e.target.closest(CLICK_SEL);
|
for (const [sel, fn] of CLICK) {
|
||||||
if (!el) return;
|
const el = e.target.closest(sel);
|
||||||
el.disabled = true;
|
if (!el) continue;
|
||||||
const {id} = dup;
|
try {
|
||||||
try {
|
el.disabled = true;
|
||||||
if (el.matches(CLICK.uninstall)) {
|
await fn(e);
|
||||||
dup = style = false;
|
} catch (e) {
|
||||||
removeEventListener('change', onChange);
|
alert(chrome.i18n.getMessage('styleInstallFailed', e.message || e));
|
||||||
await API.styles.delete(id);
|
} finally {
|
||||||
return;
|
el.disabled = false;
|
||||||
}
|
}
|
||||||
if (el.matches(CLICK.customize)) {
|
|
||||||
const isOn = dup && !$('#style-settings');
|
|
||||||
toggleListener(isOn, 'change', onChange);
|
|
||||||
observeColors(isOn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.stopPropagation();
|
|
||||||
if (!style) await buildStyle();
|
|
||||||
style = dup = await API.usercss.install(style, {
|
|
||||||
dup: {id},
|
|
||||||
vars: getPageVars(),
|
|
||||||
});
|
|
||||||
sendStylishEvent('styleInstalledChrome');
|
|
||||||
API.uso.pingback(id);
|
|
||||||
} catch (e) {
|
|
||||||
alert(chrome.i18n.getMessage('styleInstallFailed', e.message || e));
|
|
||||||
} finally {
|
|
||||||
el.disabled = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onCustomize() {
|
||||||
|
const ss = $('#style-settings');
|
||||||
|
const willShow = !ss || !ss.offsetHeight;
|
||||||
|
observeColors(willShow);
|
||||||
|
toggleListener(willShow, 'change', onChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onInstall(e) {
|
||||||
|
const {id} = dup;
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!style) await buildStyle();
|
||||||
|
style = dup = await API.usercss.install(style, {
|
||||||
|
dup: {id},
|
||||||
|
vars: getPageVars(),
|
||||||
|
});
|
||||||
|
sendStylishEvent('styleInstalledChrome');
|
||||||
|
API.uso.pingback(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUninstall() {
|
||||||
|
const {id} = dup;
|
||||||
|
dup = style = false;
|
||||||
|
observeColors(false);
|
||||||
|
removeEventListener('change', onChange);
|
||||||
|
return API.styles.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
function onChange({target: el}) {
|
function onChange({target: el}) {
|
||||||
if (dup && el.matches('[name^="ik-"], [type=file]')) {
|
if (dup && el.matches('[name^="ik-"], [type=file]')) {
|
||||||
API.usercss.configVars(dup.id, getPageVars());
|
API.usercss.configVars(dup.id, getPageVars());
|
||||||
|
@ -121,7 +129,7 @@
|
||||||
const {vars} = (style || dup).usercssData;
|
const {vars} = (style || dup).usercssData;
|
||||||
for (const el of document.querySelectorAll('[name^="ik-"]')) {
|
for (const el of document.querySelectorAll('[name^="ik-"]')) {
|
||||||
const name = el.name.slice(3); // dropping "ik-"
|
const name = el.name.slice(3); // dropping "ik-"
|
||||||
const ik = badKeys[name] || name;
|
const ik = (badKeys || {})[name] || name;
|
||||||
const v = vars[ik] || false;
|
const v = vars[ik] || false;
|
||||||
const isImage = el.type === 'radio';
|
const isImage = el.type === 'radio';
|
||||||
if (v && (!isImage || el.checked)) {
|
if (v && (!isImage || el.checked)) {
|
||||||
|
@ -185,45 +193,56 @@
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function inPageContext(eventId, eventIdHost, styleId, apiUrl) {
|
function inPageContext(eventId, eventIdHost, styleId, apiUrl) {
|
||||||
|
let done, orphaned, vars;
|
||||||
|
if (!window.chrome) window.chrome = {runtime: {sendMessage: () => {}}}; // USO bug in FF
|
||||||
|
const EXT_ID = 'fjnbnpbmkenffdnngjfgmeleoegfcffe';
|
||||||
|
const {defineProperty} = Object;
|
||||||
const {dispatchEvent, CustomEvent, removeEventListener} = window;
|
const {dispatchEvent, CustomEvent, removeEventListener} = window;
|
||||||
const apply = Map.call.bind(Map.apply);
|
const apply = Map.call.bind(Map.apply);
|
||||||
const CR = chrome.runtime;
|
const OVR = [
|
||||||
const SEND = 'sendMessage';
|
[chrome.runtime, 'sendMessage', (fn, me, args) => {
|
||||||
const RP = Response.prototype;
|
const [id, /*msg*/, opts, cb = opts] = args;
|
||||||
const ORIG = {json: RP.json, [SEND]: CR[SEND]};
|
if (id !== EXT_ID) return apply(fn, me, args);
|
||||||
let done, orphaned, vars;
|
if (typeof cb !== 'function') return Promise.resolve(true);
|
||||||
CR[SEND] = ovrSend;
|
cb(true);
|
||||||
RP.json = ovrJson;
|
}],
|
||||||
|
[Response.prototype, 'json', async (fn, me, args) => {
|
||||||
|
const res = await apply(fn, me, args);
|
||||||
|
try {
|
||||||
|
if (!done && me.url === apiUrl) {
|
||||||
|
done = true;
|
||||||
|
send(res);
|
||||||
|
setVars(res);
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
return res;
|
||||||
|
}],
|
||||||
|
[window, 'fetch', (fn, me, args) =>
|
||||||
|
args[0] === `chrome-extension://${EXT_ID}/index.html`
|
||||||
|
? Promise.resolve(new Response('<!doctype html><html lang="en"></html>'))
|
||||||
|
: apply(fn, me, args),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
OVR.forEach(([obj, name, caller], i) => {
|
||||||
|
/* Using Proxy to make the override undetectable so Stylish cannot track our users,
|
||||||
|
* which was the primary reason privacy-concerned users abandoned Stylish.
|
||||||
|
* TODO: add a user option to allow USO see the user has Stylus? */
|
||||||
|
const orig = obj[name];
|
||||||
|
const ovr = new Proxy(orig, {
|
||||||
|
apply(fn, me, args) {
|
||||||
|
if (orphaned) restore(obj, name, ovr, fn);
|
||||||
|
return (orphaned ? apply : caller)(fn, me, args);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
defineProperty(obj, name, {value: ovr});
|
||||||
|
OVR[i] = [obj, name, ovr, orig]; // same args as restore()
|
||||||
|
});
|
||||||
window.isInstalled = true;
|
window.isInstalled = true;
|
||||||
addEventListener(eventId, onCommand, true);
|
addEventListener(eventId, onCommand, true);
|
||||||
function ovrSend(id, msg, opts, cb = opts) {
|
|
||||||
if (!orphaned &&
|
|
||||||
id === 'fjnbnpbmkenffdnngjfgmeleoegfcffe' &&
|
|
||||||
msg && msg.type === 'deleteStyle' &&
|
|
||||||
typeof cb === 'function') {
|
|
||||||
cb(true);
|
|
||||||
} else {
|
|
||||||
return ORIG[SEND](...arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async function ovrJson() {
|
|
||||||
const res = await apply(ORIG.json, this, arguments);
|
|
||||||
try {
|
|
||||||
if (!done && this.url === apiUrl) {
|
|
||||||
if (RP.json === ovrJson) RP.json = ORIG.json;
|
|
||||||
done = true;
|
|
||||||
send(res);
|
|
||||||
setVars(res);
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
function onCommand(e) {
|
function onCommand(e) {
|
||||||
if (e.detail === 'quit') {
|
if (e.detail === 'quit') {
|
||||||
removeEventListener(eventId, onCommand, true);
|
removeEventListener(eventId, onCommand, true);
|
||||||
// We can restore the hooks only if another script didn't modify them
|
OVR.forEach(restore);
|
||||||
if (CR[SEND] === ovrSend) CR[SEND] = ovrSend;
|
|
||||||
if (RP.json === ovrJson) RP.json = ORIG.json;
|
|
||||||
done = orphaned = true;
|
done = orphaned = true;
|
||||||
} else if (/^vars:/.test(e.detail)) {
|
} else if (/^vars:/.test(e.detail)) {
|
||||||
vars = JSON.parse(e.detail.slice(5));
|
vars = JSON.parse(e.detail.slice(5));
|
||||||
|
@ -231,6 +250,11 @@ function inPageContext(eventId, eventIdHost, styleId, apiUrl) {
|
||||||
send(e.relatedTarget.uploadedData);
|
send(e.relatedTarget.uploadedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function restore(obj, name, ovr, orig) { // same order as OVR after patching
|
||||||
|
if (obj[name] === ovr) {
|
||||||
|
defineProperty(obj, name, {value: orig});
|
||||||
|
}
|
||||||
|
}
|
||||||
function send(data) {
|
function send(data) {
|
||||||
dispatchEvent(new CustomEvent(eventIdHost, {__proto: null, detail: data}));
|
dispatchEvent(new CustomEvent(eventIdHost, {__proto: null, detail: data}));
|
||||||
}
|
}
|
||||||
|
@ -261,7 +285,7 @@ function inPageContext(eventId, eventIdHost, styleId, apiUrl) {
|
||||||
if (ss.setting_type === 'image') {
|
if (ss.setting_type === 'image') {
|
||||||
let isListed;
|
let isListed;
|
||||||
for (const opt of ss.style_setting_options) {
|
for (const opt of ss.style_setting_options) {
|
||||||
isListed |= opt.default = (opt.value === value);
|
isListed |= opt.default = (opt.install_key === value);
|
||||||
}
|
}
|
||||||
images.set(ik, {url: isNew && !isListed ? vars[`${ik}-custom`].value : value, isListed});
|
images.set(ik, {url: isNew && !isListed ? vars[`${ik}-custom`].value : value, isListed});
|
||||||
} else if (value.startsWith('ik-') || isNew && vars[ik].type === 'select') {
|
} else if (value.startsWith('ik-') || isNew && vars[ik].type === 'select') {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user