USO bug workaround: collapse long URL vars

#195
This commit is contained in:
tophf 2018-04-12 09:13:31 +03:00
parent 1d7437d181
commit 2a75159d2a
3 changed files with 50 additions and 19 deletions

View File

@ -128,7 +128,7 @@ rules:
no-const-assign: [2] no-const-assign: [2]
no-constant-condition: [0] no-constant-condition: [0]
no-continue: [0] no-continue: [0]
no-control-regex: [2] no-control-regex: [0]
no-debugger: [2] no-debugger: [2]
no-delete-var: [2] no-delete-var: [2]
no-div-regex: [0] no-div-regex: [0]

View File

@ -214,18 +214,18 @@
} }
function getResource(url) { function getResource(url, options) {
return new Promise(resolve => { return new Promise(resolve => {
if (url.startsWith('#')) { if (url.startsWith('#')) {
resolve(document.getElementById(url.slice(1)).textContent); resolve(document.getElementById(url.slice(1)).textContent);
} else { } else {
chrome.runtime.sendMessage({ chrome.runtime.sendMessage(Object.assign({
url, url,
method: 'download', method: 'download',
timeout: 60e3, timeout: 60e3,
// USO can't handle POST requests for style json // USO can't handle POST requests for style json
body: null, body: null,
}, result => { }, options), result => {
const error = result && result.__ERROR__; const error = result && result.__ERROR__;
if (error) { if (error) {
alert('Error' + (error ? '\n' + error : '')); alert('Error' + (error ? '\n' + error : ''));
@ -239,14 +239,8 @@
function getStyleJson() { function getStyleJson() {
const url = getStyleURL(); return getResource(getStyleURL(), {responseType: 'json'})
return getResource(url).then(code => { .catch(() => null);
try {
return JSON.parse(code);
} catch (e) {
return fetch(url).then(r => r.json()).catch(() => null);
}
});
} }

View File

@ -61,6 +61,8 @@ const URLS = {
// TODO: remove when "minimum_chrome_version": "61" or higher // TODO: remove when "minimum_chrome_version": "61" or higher
chromeProtectsNTP: CHROME >= 3161, chromeProtectsNTP: CHROME >= 3161,
userstylesOrgJson: 'https://userstyles.org/styles/chrome/',
supported: url => ( supported: url => (
url.startsWith('http') && (FIREFOX || !url.startsWith(URLS.browserWebStore)) || url.startsWith('http') && (FIREFOX || !url.startsWith(URLS.browserWebStore)) ||
url.startsWith('ftp') || url.startsWith('ftp') ||
@ -575,13 +577,18 @@ function download(url, {
body = url.slice(queryPos); body = url.slice(queryPos);
url = url.slice(0, queryPos); url = url.slice(0, queryPos);
} }
// * USO can't handle POST requests for style json
// * XHR/fetch can't handle long URL
// So we need to collapse all long variables and expand them in the response
const usoVars = [];
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
url = new URL(url); const u = new URL(collapseUsoVars(url));
if (url.protocol === 'file:' && FIREFOX) { if (u.protocol === 'file:' && FIREFOX) {
// https://stackoverflow.com/questions/42108782/firefox-webextensions-get-local-files-content-by-path // https://stackoverflow.com/questions/42108782/firefox-webextensions-get-local-files-content-by-path
// FIXME: add FetchController when it is available. // FIXME: add FetchController when it is available.
const timer = setTimeout(reject, timeout, new Error('Timeout fetching ' + url.href)); const timer = setTimeout(reject, timeout, new Error('Timeout fetching ' + u.href));
fetch(url.href, {mode: 'same-origin'}) fetch(u.href, {mode: 'same-origin'})
.then(r => { .then(r => {
clearTimeout(timer); clearTimeout(timer);
return r.status === 200 ? r.text() : Promise.reject(r.status); return r.status === 200 ? r.text() : Promise.reject(r.status);
@ -595,20 +602,50 @@ function download(url, {
xhr.onloadend = event => { xhr.onloadend = event => {
if (event.type !== 'error' && ( if (event.type !== 'error' && (
xhr.status === requiredStatusCode || !requiredStatusCode || xhr.status === requiredStatusCode || !requiredStatusCode ||
url.protocol === 'file:')) { u.protocol === 'file:')) {
resolve(xhr.response); resolve(expandUsoVars(xhr.response));
} else { } else {
reject(xhr.status); reject(xhr.status);
} }
}; };
xhr.onerror = xhr.onloadend; xhr.onerror = xhr.onloadend;
xhr.responseType = responseType; xhr.responseType = responseType;
xhr.open(method, url.href, true); xhr.open(method, u.href, true);
for (const key in headers) { for (const key in headers) {
xhr.setRequestHeader(key, headers[key]); xhr.setRequestHeader(key, headers[key]);
} }
xhr.send(body); xhr.send(body);
}); });
function collapseUsoVars(url) {
if (queryPos < 0 ||
url.length < 2000 ||
!url.startsWith(URLS.userstylesOrgJson) ||
!/^get$/i.test(method)) {
return url;
}
const params = new URLSearchParams(url.slice(queryPos + 1));
for (const [k, v] of params.entries()) {
if (v.length < 10 || v.startsWith('ik-')) continue;
usoVars.push(v);
params.set(k, `\x01${usoVars.length}\x02`);
}
return url.slice(0, queryPos + 1) + params.toString();
}
function expandUsoVars(response) {
if (!usoVars.length || !response) return response;
const isText = typeof response === 'string';
const json = isText && tryJSONparse(response) || response;
json.updateUrl = url;
for (const section of json.sections || []) {
const {code} = section;
if (code.includes('\x01')) {
section.code = code.replace(/\x01(\d+)\x02/g, (_, num) => usoVars[num - 1] || '');
}
}
return isText ? JSON.stringify(json) : json;
}
} }