Lint as airbnb, refactor chrome.storage for localStorage

This commit is contained in:
Ilya Ig. Petrov 2017-02-24 11:04:15 +00:00
parent a606d25227
commit 79f51186c7
14 changed files with 611 additions and 542 deletions

View File

@ -1,5 +1,5 @@
module.exports = { module.exports = {
extends: ['eslint:recommended', 'google'], extends: ['eslint:recommended', 'airbnb'],
env: { env: {
browser: true, browser: true,
webextensions: true, webextensions: true,
@ -20,14 +20,23 @@ module.exports = {
'no-console': 'off', 'no-console': 'off',
'padded-blocks': 'off', 'padded-blocks': 'off',
'require-jsdoc': 'off', 'require-jsdoc': 'off',
'no-multi-assign': 'off',
// Taken from airbnb: 'arrow-parens': ['error', 'always'],
'no-plusplus': 'off',
'comma-dangle': ['error', {
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'always-multiline',
exports: 'always-multiline',
functions: 'ignore',
}],
/* Taken from airbnb:
'max-len': ['error', 100, 2, { 'max-len': ['error', 100, 2, {
ignoreUrls: true, ignoreUrls: true,
ignoreComments: false, ignoreComments: false,
ignoreRegExpLiterals: true, ignoreRegExpLiterals: true,
ignoreStrings: true, ignoreStrings: true,
ignoreTemplateLiterals: true, ignoreTemplateLiterals: true,
}], }],*/
}, },
}; };

View File

@ -8,8 +8,11 @@
"author": "Ilya Ig. Petrov", "author": "Ilya Ig. Petrov",
"license": "GPLv3", "license": "GPLv3",
"devDependencies": { "devDependencies": {
"eslint": "^3.15.0", "eslint": "^3.16.1",
"eslint-config-google": "^0.7.1" "eslint-config-airbnb": "^14.1.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.10.0"
}, },
"dependencies": { "dependencies": {
"del": "^2.2.2", "del": "^2.2.2",

View File

@ -0,0 +1,18 @@
/mnt/d/__Storage__/Workspace/repos/LATEST/extensions/chromium/runet-censorship-bypass/src/extension-full/20-ip-to-host-api.js
19:9 error Unexpected dangling '_' in '_match' no-underscore-dangle
23:35 error Expected property shorthand object-shorthand
27:9 error Unexpected dangling '_' in '_test' no-underscore-dangle
53:9 error Unexpected dangling '_' in '_state' no-underscore-dangle
58:9 error Unexpected dangling '_' in '_createHostObj' no-underscore-dangle
64:9 error Unexpected dangling '_' in '_getHostObj' no-underscore-dangle
73:11 error Unexpected dangling '_' in '_antizapret' no-underscore-dangle
174:9 error Unexpected dangling '_' in '_canonize' no-underscore-dangle
232:11 error Unexpected dangling '_' in '_purgeOldIpsForSync' no-underscore-dangle
252:24 error Unexpected dangling '_' in '_addAsync' no-underscore-dangle
300:7 error Unexpected dangling '_' in '_updateAllAsync' no-underscore-dangle
308:7 error Unexpected dangling '_' in '_updateAllAsync' no-underscore-dangle
330:7 error Unexpected dangling '_' in '_replaceAllAsync' no-underscore-dangle
✖ 13 problems (13 errors, 0 warnings)

View File

@ -8,11 +8,11 @@
// I believe logging objects precludes them from being GCed. // I believe logging objects precludes them from being GCed.
// I also don't remove logs for sake of client-side troubleshooting // I also don't remove logs for sake of client-side troubleshooting
// (though no one sent me logs so far). // (though no one sent me logs so far).
['log', 'warn', 'error'].forEach( (meth) => { ['log', 'warn', 'error'].forEach((meth) => {
const _meth = window.console[meth].bind(console); const originalMeth = window.console[meth].bind(console);
window.console[meth] = function(...args) { window.console[meth] = function wrappedForDebug(...args) {
_meth(...args.map((a) => '' + a)); originalMeth(...args.map((a) => `${a}`/* toString, but safer */));
}; };
}); });
@ -33,7 +33,7 @@
throwIfError(err) { throwIfError(err) {
if(err) { if (err) {
throw err; throw err;
} }
@ -45,7 +45,7 @@
// method invokation. // method invokation.
const err = chrome.runtime.lastError || chrome.extension.lastError; const err = chrome.runtime.lastError || chrome.extension.lastError;
if (!err) { if (!err) {
return; return null;
} }
console.warn('API returned error:', err); console.warn('API returned error:', err);
return new Error(err.message); // Add stack. return new Error(err.message); // Add stack.
@ -62,7 +62,7 @@
chromified(cb = self.mandatory()) { chromified(cb = self.mandatory()) {
// Take error first callback and convert it to chrome API callback. // Take error first callback and convert it to chrome API callback.
return function(...args) { return function cbForChromeApi(...args) {
const err = self.checkChromeError(); const err = self.checkChromeError();
self.timeouted(cb)(err, ...args); self.timeouted(cb)(err, ...args);
@ -71,35 +71,42 @@
}, },
getProp(obj, path = self.mandatory()) { getProp(targetObj, path = self.mandatory()) {
const props = path.split('.'); const props = path.split('.');
if (!props.length) { if (!props.length) {
throw new TypeError('Property must be supplied.'); throw new TypeError('Property must be supplied.');
} }
const lastProp = props.pop();
for( const prop of props ) { const ifSupportsInOp = (obj) =>
if (!(prop in obj)) { ['object', 'function'].includes(typeof obj);
return undefined;
return props.reduce((currentObj, prop) => {
if (
ifSupportsInOp(currentObj)
&& prop in currentObj
) {
return currentObj[prop];
} }
obj = obj[prop]; return undefined;
}
return obj[lastProp]; }, targetObj);
}, },
assert(value) { assert(value) {
if(!value) { if (!value) {
console.assert(value); console.assert(value);
throw new Error('Assert failed for:' + value); throw new Error(`Assert failed for:${value}`);
} }
}, },
addRequestResponder(requestType, responder) { addRequestResponder(requestType, responder) {
if( privates.requestToResponder[requestType] ) { if (privates.requestToResponder[requestType]) {
throw new TypeError(`Request ${requestType} already has responder!`); throw new TypeError(`Request ${requestType} already has responder!`);
} }
privates.requestToResponder[requestType] = responder; privates.requestToResponder[requestType] = responder;
@ -109,9 +116,9 @@
fireRequest(requestType, ...args) { fireRequest(requestType, ...args) {
const cb = args.slice(-1)[0]; const cb = args.slice(-1)[0];
self.assert(typeof(cb) === 'function'); self.assert(typeof cb === 'function');
const responder = privates.requestToResponder[requestType]; const responder = privates.requestToResponder[requestType];
if(responder) { if (responder) {
responder(...args); responder(...args);
} else { } else {
cb(); cb();
@ -123,18 +130,18 @@
return function state(key, value) { return function state(key, value) {
key = prefix + key; const prefixedKey = prefix + key;
if (value === null) { if (value === null) {
return localStorage.removeItem(key); return localStorage.removeItem(prefixedKey);
} }
if (value === undefined) { if (value === undefined) {
const item = localStorage.getItem(key); const item = localStorage.getItem(prefixedKey);
return item && JSON.parse(item); return item && JSON.parse(item);
} }
if (value instanceof Date) { if (value instanceof Date) {
throw new TypeError('Converting Date format to JSON is not supported.'); throw new TypeError('Converting Date format to JSON is not supported.');
} }
localStorage.setItem(key, JSON.stringify(value)); return localStorage.setItem(prefixedKey, JSON.stringify(value));
}; };
@ -168,15 +175,15 @@
searchSettingsForUrl(niddle) { searchSettingsForUrl(niddle) {
return 'chrome://settings/search#' + (chrome.i18n.getMessage(niddle) || niddle); return `chrome://settings/search#${(chrome.i18n.getMessage(niddle) || niddle)}`;
}, },
whichExtensionHtml() { whichExtensionHtml() {
return chrome.i18n.getMessage('noControl') + return `${chrome.i18n.getMessage('noControl')}
` <a href="${ this.searchSettingsForUrl('proxy') }"> <a href="${this.searchSettingsForUrl('proxy')}">
${ chrome.i18n.getMessage('which') } ${chrome.i18n.getMessage('which')}
</a>`; </a>`;
}, },

View File

@ -8,38 +8,36 @@
const errorJsonReplacer = function errorJsonReplacer(key, value) { const errorJsonReplacer = function errorJsonReplacer(key, value) {
// fooWindow.ErrorEvent !== barWindow.ErrorEvent // fooWindow.ErrorEvent !== barWindow.ErrorEvent
if (!( value && value.constructor if (!(value && value.constructor
&& ['Error', 'Event'].some( && ['Error', 'Event'].some(
(suff) => value.constructor.name.endsWith(suff) (suff) => value.constructor.name.endsWith(suff))
)
)) { )) {
return value; return value;
} }
const alt = {};
Object.getOwnPropertyNames(value).forEach(function(key) { const alt = Object.getOwnPropertyNames(value).reduce(
(acc, ownProp) =>
Object.assign(acc, { [ownProp]: value[ownProp] }),
{}
);
alt[key] = value[key]; for (const prop in value) { // eslint-disable-line no-restricted-syntax
}, value);
for(const prop in value) {
if (/^[A-Z]/.test(prop)) { if (/^[A-Z]/.test(prop)) {
// MOUSEMOVE, CLICK, KEYUP, NONE, etc. // MOUSEMOVE, CLICK, KEYUP, NONE, etc.
continue; continue; // eslint-disable-line no-continue
} }
alt[prop] = value[prop]; alt[prop] = value[prop];
} }
if (value.constructor.name === 'ErrorEvent') { if (value.constructor.name === 'ErrorEvent') {
for(const circularProp of [
[ // First line are circular props. 'target', 'srcElement', 'path', 'currentTarget',
'target', 'srcElement', 'path', 'currentTarget', 'bubbles', 'cancelBubble', 'cancelable', 'composed',
'bubbles', 'cancelBubble', 'cancelable', 'composed', 'defaultPrevented', 'eventPhase', 'isTrusted', 'returnValue',
'defaultPrevented', 'eventPhase', 'isTrusted', 'returnValue', 'timeStamp',
'timeStamp']) { ].forEach((circularProp) => {
delete alt[circularProp]; delete alt[circularProp];
} });
} }
if (value.name) { if (value.name) {
@ -53,8 +51,8 @@
const openAndFocus = function openAndFocus(url) { const openAndFocus = function openAndFocus(url) {
chrome.tabs.create( chrome.tabs.create(
{url: url}, { url },
(tab) => chrome.windows.update(tab.windowId, {focused: true}) (tab) => chrome.windows.update(tab.windowId, { focused: true })
); );
}; };
@ -69,13 +67,11 @@
viewError(type = window.utils.mandatory(), err) { viewError(type = window.utils.mandatory(), err) {
const errors = err ? {[type]: err} : this.idToError; const errors = err ? { [type]: err } : this.idToError;
const json = JSON.stringify(errors, errorJsonReplacer, 0); const json = JSON.stringify(errors, errorJsonReplacer, 0);
openAndFocus( openAndFocus(
'http://rebrand.ly/ac-error/?json=' + encodeURIComponent(json) + `http://rebrand.ly/ac-error/?json=${encodeURIComponent(json)}${type ? `&type=${encodeURIComponent(type)}` : ''}&version=${chrome.runtime.getManifest().version}`
(type ? '&type=' + encodeURIComponent(type) : '') +
'&version=' + chrome.runtime.getManifest().version
); );
}, },
@ -93,19 +89,18 @@
switch(onOffStr, eventName) { switch(onOffStr, eventName) {
if (!['on', 'off'].includes(onOffStr)) { if (!['on', 'off'].includes(onOffStr)) {
throw new TypeError('First argument bust be "on" or "off".'); throw new TypeError('First argument must be "on" or "off".');
}
for(
const name of (eventName ? [eventName] : this.getEventsMap().keys() )
) {
this.state( ifPrefix + name, onOffStr === 'on' ? 'on' : null );
} }
(eventName ? [eventName] : this.getEventsMap().keys()).forEach(
(name) =>
this.state(ifPrefix + name, onOffStr === 'on' ? 'on' : null)
);
}, },
isOn(eventName) { isOn(eventName) {
return this.state( ifPrefix + eventName ); return this.state(ifPrefix + eventName);
}, },
@ -123,7 +118,7 @@
} }
if (this.ifControlled) { if (this.ifControlled) {
chrome.browserAction.setIcon( {path: './icons/default-128.png'} ); chrome.browserAction.setIcon({ path: './icons/default-128.png' });
} else { } else {
chrome.browserAction.setIcon({ chrome.browserAction.setIcon({
path: './icons/default-grayscale-128.png', path: './icons/default-grayscale-128.png',
@ -163,12 +158,12 @@
id, title, errOrMessage, id, title, errOrMessage,
{ {
icon = 'default-128.png', icon = 'default-128.png',
context = extName + ' ' + extVersion, context = `${extName} ${extVersion}`,
ifSticky = true, ifSticky = true,
} = {} } = {}
) { ) {
if ( !this.isOn(id) ) { if (!this.isOn(id)) {
return; return;
} }
this.idToError[id] = errOrMessage; this.idToError[id] = errOrMessage;
@ -176,12 +171,12 @@
chrome.notifications.create( chrome.notifications.create(
id, id,
{ {
title: title, title,
message: message, message,
contextMessage: context, contextMessage: context,
requireInteraction: ifSticky, requireInteraction: ifSticky,
type: 'basic', type: 'basic',
iconUrl: './icons/' + icon, iconUrl: `./icons/${icon}`,
appIconMaskUrl: './icons/default-mask-128.png', appIconMaskUrl: './icons/default-mask-128.png',
isClickable: true, isClickable: true,
} }
@ -193,15 +188,16 @@
win.addEventListener('error', (errEvent) => { win.addEventListener('error', (errEvent) => {
console.warn(name + ':GLOBAL ERROR', errEvent); console.warn(`${name}:GLOBAL ERROR`, errEvent);
this.mayNotify('ext-error', 'Ошибка расширения', errEvent, this.mayNotify('ext-error', 'Ошибка расширения', errEvent,
{icon: 'ext-error-128.png'}); { icon: 'ext-error-128.png' }
);
}); });
win.addEventListener('unhandledrejection', (event) => { win.addEventListener('unhandledrejection', (event) => {
console.warn(name + ': Unhandled rejection. Throwing error.'); console.warn(`${name}: Unhandled rejection. Throwing error.`);
event.preventDefault(); event.preventDefault();
console.log('ev', event); console.log('ev', event);
throw event.reason; throw event.reason;
@ -225,24 +221,23 @@
chrome.proxy.settings.get( chrome.proxy.settings.get(
{}, {},
timeouted( handlers.isControllable.bind(handlers) ) timeouted(handlers.isControllable.bind(handlers))
); );
chrome.notifications.onClicked.addListener( timeouted( (notId) => { chrome.notifications.onClicked.addListener(timeouted((notId) => {
chrome.notifications.clear(notId); chrome.notifications.clear(notId);
if(notId === 'no-control') { if (notId === 'no-control') {
return openAndFocus( return openAndFocus(
window.utils.messages.searchSettingsForUrl('proxy') window.utils.messages.searchSettingsForUrl('proxy'));
);
} }
handlers.viewError(notId); return handlers.viewError(notId);
})); }));
handlers.installListenersOn(window, 'BG'); handlers.installListenersOn(window, 'BG');
chrome.proxy.onProxyError.addListener( timeouted( (details) => { chrome.proxy.onProxyError.addListener(timeouted((details) => {
if (!handlers.ifControlled) { if (!handlers.ifControlled) {
return; return;
@ -256,26 +251,26 @@
console.warn('PAC ERROR', details); console.warn('PAC ERROR', details);
// TOOD: add "view pac script at this line" button. // TOOD: add "view pac script at this line" button.
handlers.mayNotify('pac-error', 'Ошибка PAC!', handlers.mayNotify('pac-error', 'Ошибка PAC!',
details.error + '\n' + details.details, `${details.error}\n${details.details}`,
{icon: 'pac-error-128.png'} { icon: 'pac-error-128.png' }
); );
})); }));
chrome.proxy.settings.onChange.addListener( timeouted( (details) => { chrome.proxy.settings.onChange.addListener(timeouted((details) => {
console.log('Proxy settings changed.', details); console.log('Proxy settings changed.', details);
const noCon = 'no-control'; const noCon = 'no-control';
const ifWasControllable = handlers.ifControllable; const ifWasControllable = handlers.ifControllable;
if ( !handlers.isControllable(details) && ifWasControllable ) { if (!handlers.isControllable(details) && ifWasControllable) {
handlers.mayNotify( handlers.mayNotify(
noCon, noCon,
chrome.i18n.getMessage('noControl'), chrome.i18n.getMessage('noControl'),
chrome.i18n.getMessage('which'), chrome.i18n.getMessage('which'),
{icon: 'no-control-128.png', ifSticky: false} { icon: 'no-control-128.png', ifSticky: false }
); );
} else { } else {
chrome.notifications.clear( noCon ); chrome.notifications.clear(noCon);
} }
})); }));

View File

@ -1,4 +1,5 @@
'use strict'; 'use strict';
/* /*
* Error Library * Error Library
* PURPOSE 1: * PURPOSE 1:
@ -32,7 +33,7 @@
}, },
clarify: function(err, message = mandatory(), {data} = {}) { clarify(err, message = mandatory(), { data } = {}) {
if (!err) { if (!err) {
return err; return err;
@ -46,9 +47,9 @@
}, },
clarifyThen: function(message, cb = mandatory()) { clarifyThen(message, cb = mandatory()) {
return (err, ...args) => cb( self.clarify(err, message), ...args ); return (err, ...args) => cb(self.clarify(err, message), ...args);
}, },

View File

@ -39,35 +39,31 @@
get(url, cb = mandatory()) { get(url, cb = mandatory()) {
const start = Date.now(); const start = Date.now();
fetch(url, {cache: 'no-store'}).then( fetch(url, { cache: 'no-store' }).then(
(res) => { (res) => {
const textCb = const textCb =
(err) => { (err) => {
console.log('Reading response as text...');
res.text().then( res.text().then(
(text) => { (text) => cb(err, text),
console.log('Calling CB...');
cb(err, text);
},
cb cb
); );
}; };
const status = res.status; const status = res.status;
if ( !( status >= 200 && status < 300 || status === 304 ) ) { if (!((status >= 200 && status < 300) || status === 304)) {
return textCb( return textCb(
errorsLib.clarify( errorsLib.clarify(
res, res,
'Получен ответ с неудачным HTTP-кодом ' + status + '.' `Получен ответ с неудачным HTTP-кодом ${status}.`
) )
); );
} }
console.log('GETed with success:', url, Date.now() - start); console.log('GETed with success:', url, Date.now() - start);
textCb(); return textCb();
}, },
errorsLib.clarifyThen(checkCon, cb) errorsLib.clarifyThen(checkCon, cb)

View File

@ -65,36 +65,10 @@
const getDefaults = function getDefaults() { const getDefaults = function getDefaults() {
return Object.keys(configs).reduce((acc, key) => { return Object.keys(configs).reduce((acc, key) =>
Object.assign(acc, { [key]: configs[key].dflt }),
acc[key] = configs[key].dflt; {}
return acc; );
}, {});
};
const getCurrentConfigs = function getCurrentConfigs() {
const mods = kitchenState(modsKey);
return new PacModifiers(mods || {});
};
const getOrderedConfigsForUser = function getOrderedConfigs() {
const pacMods = getCurrentConfigs();
return Object.keys(configs).reduce((arr, key) => {
const conf = configs[key];
if(typeof(conf.index) === 'number') {
arr[conf.index] = conf;
conf.value = pacMods[key];
conf.key = key;
}
return arr;
}, []);
}; };
@ -110,17 +84,17 @@
); );
Object.assign(this, defaults, mods); Object.assign(this, defaults, mods);
this.ifNoMods = ifAllDefaults ? true : false; this.ifNoMods = ifAllDefaults;
let customProxyArray = []; let customProxyArray = [];
if (this.customProxyStringRaw) { if (this.customProxyStringRaw) {
customProxyArray = this.customProxyStringRaw customProxyArray = this.customProxyStringRaw
.replace(/#.*$/mg, '') // Strip comments. .replace(/#.*$/mg, '') // Strip comments.
.split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g ) .split(/(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g)
.map( (p) => p.trim() ) .map((p) => p.trim())
.filter( (p) => p && /\s+/g.test(p) ); .filter((p) => p && /\s+/g.test(p));
if (this.ifUseSecureProxiesOnly) { if (this.ifUseSecureProxiesOnly) {
customProxyArray = customProxyArray.filter( (p) => !p.startsWith('HTTP ') ); customProxyArray = customProxyArray.filter((p) => !p.startsWith('HTTP '));
} }
} }
if (this.ifUseLocalTor) { if (this.ifUseLocalTor) {
@ -142,13 +116,13 @@
if (this.ifMindExceptions && this.exceptions) { if (this.ifMindExceptions && this.exceptions) {
this.included = []; this.included = [];
this.excluded = []; this.excluded = [];
for(const host of Object.keys(this.exceptions)) { Object.keys(this.exceptions).forEach((host) => {
if (this.exceptions[host]) { if (this.exceptions[host]) {
this.included.push(host); this.included.push(host);
} else { } else {
this.excluded.push(host); this.excluded.push(host);
} }
} });
if (this.included && !this.filteredCustomsString) { if (this.included && !this.filteredCustomsString) {
throw new TypeError( throw new TypeError(
'Проксировать свои сайты можно только через свои прокси. Нет ни одного своего прокси, удовлетворяющего вашим требованиям!' 'Проксировать свои сайты можно только через свои прокси. Нет ни одного своего прокси, удовлетворяющего вашим требованиям!'
@ -160,6 +134,73 @@
} }
const getCurrentConfigs = function getCurrentConfigs() {
const mods = kitchenState(modsKey);
return new PacModifiers(mods || {});
};
const getOrderedConfigsForUser = function getOrderedConfigs() {
const pacMods = getCurrentConfigs();
return Object.keys(configs).reduce((arr, key) => {
const conf = configs[key];
if (typeof conf.index === 'number') {
Object.assign(arr, { [conf.index]: conf });
conf.value = pacMods[key];
conf.key = key;
}
return arr;
}, []);
};
const privates = {};
privates.tryNowAsync = function tryNowAsync(maybeDetails, maybeCb = throwIfError) {
let cb;
let detailsOrUndefined;
if (typeof maybeDetails === 'function') {
detailsOrUndefined = undefined;
cb = maybeDetails;
} else {
detailsOrUndefined = maybeDetails;
cb = maybeCb;
}
new Promise((resolve) => (
detailsOrUndefined
? resolve(detailsOrUndefined)
: chrome.proxy.settings.get({}, timeouted(resolve))
)).then((details) => {
if (
details.levelOfControl === 'controlled_by_this_extension'
) {
const pac = window.utils.getProp(details, 'value.pacScript');
if (pac && pac.data) {
// Delete old kitchen modifications.
pac.data = pac.data.replace(
new RegExp(`${kitchenStartsMark}[\\s\\S]*$`, 'g'),
''
);
return chrome.proxy.settings.set(details, chromified(cb));
}
}
kitchenState(ifIncontinence, true);
return cb(null, null, new TypeError(
'Не найдено активного PAC-скрипта! Изменения будут применены при возвращении контроля настроек прокси или установке нового PAC-скрипта.'
));
});
};
window.apis.pacKitchen = { window.apis.pacKitchen = {
getPacMods: getCurrentConfigs, getPacMods: getCurrentConfigs,
@ -167,13 +208,13 @@
cook(pacData, pacMods = mandatory()) { cook(pacData, pacMods = mandatory()) {
return pacMods.ifNoMods ? pacData : pacData + `${ kitchenStartsMark } return pacMods.ifNoMods ? pacData : `${pacData}${kitchenStartsMark}
;+function(global) { ;+function(global) {
"use strict"; "use strict";
const originalFindProxyForURL = FindProxyForURL; const originalFindProxyForURL = FindProxyForURL;
global.FindProxyForURL = function(url, host) { global.FindProxyForURL = function(url, host) {
${function() { ${((function generateNewFindProxyForURL() {
let res = pacMods.ifProhibitDns ? ` let res = pacMods.ifProhibitDns ? `
global.dnsResolve = function(host) { return null; }; global.dnsResolve = function(host) { return null; };
@ -203,42 +244,42 @@
`; `;
} }
if( if (
!pacMods.ifUseSecureProxiesOnly && !pacMods.ifUseSecureProxiesOnly &&
!pacMods.filteredCustomsString && !pacMods.filteredCustomsString &&
pacMods.ifUsePacScriptProxies pacMods.ifUsePacScriptProxies
) { ) {
return res + ` return `${res}
return originalFindProxyForURL(url, host); return originalFindProxyForURL(url, host);
`; `;
} }
return res + ` return `${res}
const originalProxyString = originalFindProxyForURL(url, host); const originalProxyString = originalFindProxyForURL(url, host);
let originalProxyArray = originalProxyString.split(/(?:\\s*;\\s*)+/g).filter( (p) => p ); let originalProxyArray = originalProxyString.split(/(?:\\s*;\\s*)+/g).filter( (p) => p );
if (originalProxyArray.every( (p) => /^DIRECT$/i.test(p) )) { if (originalProxyArray.every( (p) => /^DIRECT$/i.test(p) )) {
// Directs only or null, no proxies. // Directs only or null, no proxies.
return originalProxyString; return originalProxyString;
} }
return ` + return ${
function() { ((function getProxies() {
if (!pacMods.ifUsePacScriptProxies) { if (!pacMods.ifUsePacScriptProxies) {
return '"' + pacMods.filteredCustomsString + '"'; return `"${pacMods.filteredCustomsString}"`;
} }
let filteredOriginalsExp = 'originalProxyString'; let filteredOriginalsExp = 'originalProxyString';
if (pacMods.ifUseSecureProxiesOnly) { if (pacMods.ifUseSecureProxiesOnly) {
filteredOriginalsExp = filteredOriginalsExp =
'originalProxyArray.filter( (p) => !p.toUpperCase().startsWith("HTTP ") ).join("; ")'; 'originalProxyArray.filter( (p) => !p.toUpperCase().startsWith("HTTP ") ).join("; ")';
} }
if ( !pacMods.filteredCustomsString ) { if (!pacMods.filteredCustomsString) {
return filteredOriginalsExp; return filteredOriginalsExp;
} }
return '"' + pacMods.filteredCustomsString + '; " + ' + filteredOriginalsExp; return `"${pacMods.filteredCustomsString}; " + ${filteredOriginalsExp}`;
}() + ' + "; DIRECT";'; // Without DIRECT you will get 'PROXY CONN FAILED' pac-error. })())} + "; DIRECT";`; // Without DIRECT you will get 'PROXY CONN FAILED' pac-error.
}()} })())}
}; };
@ -246,68 +287,33 @@
}, },
_tryNowAsync(details, cb = throwIfError) {
if (typeof(details) === 'function') {
cb = details;
details = undefined;
}
new Promise((resolve) =>
details
? resolve(details)
: chrome.proxy.settings.get({}, timeouted(resolve) )
).then( (details) => {
if (
details.levelOfControl === 'controlled_by_this_extension'
) {
const pac = window.utils.getProp(details, 'value.pacScript');
if (pac && pac.data) {
// Delete old kitchen modifications.
pac.data = pac.data.replace(
new RegExp(kitchenStartsMark + '[\\s\\S]*$', 'g'),
''
);
return chrome.proxy.settings.set(details, chromified(cb));
}
}
kitchenState(ifIncontinence, true);
cb(null, null, new TypeError(
'Не найдено активного PAC-скрипта! Изменения будут применены при возвращении контроля настроек прокси или установке нового PAC-скрипта.'
));
});
},
checkIncontinence(details) { checkIncontinence(details) {
if ( kitchenState(ifIncontinence) ) { if (kitchenState(ifIncontinence)) {
this._tryNowAsync(details, () => {/* Swallow. */}); privates.tryNowAsync(details, () => { /* Swallow. */ });
} }
}, },
keepCookedNowAsync(pacMods = mandatory(), cb = throwIfError) { keepCookedNowAsync(maybePacMods = mandatory(), maybeCb = throwIfError) {
if (typeof(pacMods) === 'function') { let pacMods;
cb = pacMods; let cb;
if (typeof maybePacMods === 'function') {
cb = maybePacMods;
pacMods = getCurrentConfigs(); pacMods = getCurrentConfigs();
} else { } else {
try { try {
pacMods = new PacModifiers(pacMods); pacMods = new PacModifiers(maybePacMods);
} catch(e) { } catch (e) {
return cb(e); return cb(e);
} }
kitchenState(modsKey, pacMods); kitchenState(modsKey, pacMods);
cb = maybeCb;
} }
console.log('Keep cooked now...', pacMods); console.log('Keep cooked now...', pacMods);
this._tryNowAsync( return privates.tryNowAsync(
(err, res, ...warns) => { (err, res, ...warns) => {
console.log('Try now err:', err); console.log('Try now err:', err);
@ -320,8 +326,12 @@
return cb(null, res, ...warns); return cb(null, res, ...warns);
} }
const hosts = par.map( (ps) => ps.split(/\s+/)[1] ); const hosts = par.map((ps) => ps.split(/\s+/)[1]);
window.utils.fireRequest('ip-to-host-replace-all', hosts, (err, res, ...moreWarns) => cb( err, res, ...warns.concat(moreWarns) )); return window.utils.fireRequest(
'ip-to-host-replace-all',
hosts,
(ipErr, ipRes, ...ipWarns) => cb(ipErr, ipRes, ...warns.concat(ipWarns))
);
} }
); );
@ -340,20 +350,20 @@
const pacKitchen = window.apis.pacKitchen; const pacKitchen = window.apis.pacKitchen;
const originalSet = chrome.proxy.settings.set.bind( chrome.proxy.settings ); const originalSet = chrome.proxy.settings.set.bind(chrome.proxy.settings);
chrome.proxy.settings.set = function(details, cb) { chrome.proxy.settings.set = function modifiedSet(details, cb = () => {}) {
const pac = window.utils.getProp(details, 'value.pacScript'); const pac = window.utils.getProp(details, 'value.pacScript');
if (!(pac && pac.data)) { if (!(pac && pac.data)) {
return originalSet(details, cb); return originalSet(details, cb);
} }
const pacMods = getCurrentConfigs(); const pacMods = getCurrentConfigs();
pac.data = pacKitchen.cook( pac.data, pacMods ); pac.data = pacKitchen.cook(pac.data, pacMods);
originalSet({value: details.value}, (/* No args. */) => { return originalSet({ value: details.value }, (/* No args. */) => {
kitchenState(ifIncontinence, null); kitchenState(ifIncontinence, null);
cb && cb(); cb();
}); });

View File

@ -35,11 +35,11 @@
const asyncLogGroup = function asyncLogGroup(...args) { const asyncLogGroup = function asyncLogGroup(...args) {
const cb = args.pop(); const cb = args.pop();
if(!(cb && typeof(cb) === 'function')) { if (!cb || typeof cb !== 'function') {
throw new TypeError('cb must be a function, but got: ' + cb); throw new TypeError(`cb must be a function, but got: ${cb}`);
} }
console.group(...args); console.group(...args);
return function(...cbArgs) { return function finishLogGroup(...cbArgs) {
console.groupEnd(); console.groupEnd();
console.log('Group finished.'); console.log('Group finished.');
@ -61,23 +61,23 @@
}, },
}; };
console.log('Setting chrome proxy settings...'); console.log('Setting chrome proxy settings...');
chrome.proxy.settings.set( {value: config}, chromified((err) => { chrome.proxy.settings.set({ value: config }, chromified((err) => {
if (err) { if (err) {
return cb(err); return cb(err);
} }
handlers.updateControlState( () => { return handlers.updateControlState(() => {
if ( !handlers.ifControlled ) { if (!handlers.ifControlled) {
console.warn('Failed, other extension is in control.'); console.warn('Failed, other extension is in control.');
return cb( return cb(
new Error( window.utils.messages.whichExtensionHtml() ) new Error(window.utils.messages.whichExtensionHtml())
); );
} }
console.log('Successfuly set PAC in proxy settings..'); console.log('Successfuly set PAC in proxy settings..');
cb(); return cb();
}); });
@ -86,25 +86,25 @@
}; };
const updatePacProxyIps = function updatePacProxyIps( const updatePacProxyIps = function updatePacProxyIps(
cb = throwIfError originalCb = throwIfError
) { ) {
cb = asyncLogGroup( const cb = asyncLogGroup(
'Getting IPs for PAC hosts...', 'Getting IPs for PAC hosts...',
cb originalCb
); );
window.utils.fireRequest('ip-to-host-update-all', cb); window.utils.fireRequest('ip-to-host-update-all', cb);
}; };
const setPacScriptFromProviderAsync = function setPacScriptFromProviderAsync( const setPacScriptFromProviderAsync = function setPacScriptFromProviderAsync(
provider, lastModified = mandatory(), cb = throwIfError provider, lastModified = mandatory(), originalCb = throwIfError
) { ) {
const pacUrl = provider.pacUrls[0]; const pacUrl = provider.pacUrls[0];
cb = asyncLogGroup( const cb = asyncLogGroup(
'Getting PAC script from provider...', pacUrl, 'Getting PAC script from provider...', pacUrl,
cb originalCb
); );
httpLib.ifModifiedSince(pacUrl, lastModified, (err, newLastModified) => { httpLib.ifModifiedSince(pacUrl, lastModified, (err, newLastModified) => {
@ -112,10 +112,9 @@
if (!newLastModified) { if (!newLastModified) {
return cb( return cb(
null, null,
{lastModified}, { lastModified },
new Warning( new Warning(
'Ваш PAC-скрипт не нуждается в обновлении. Его дата: ' + `Ваш PAC-скрипт не нуждается в обновлении. Его дата: ${lastModified}.`
lastModified
) )
); );
} }
@ -126,7 +125,7 @@
() => new Promise( () => new Promise(
(resolve, reject) => httpLib.get( (resolve, reject) => httpLib.get(
url, url,
(newErr, pacData) => newErr ? reject(newErr) : resolve(pacData) (newErr, pacData) => (newErr ? reject(newErr) : resolve(pacData))
) )
) )
), ),
@ -139,29 +138,75 @@
setPacAsync( setPacAsync(
pacData, pacData,
(err, res) => cb( (pacErr, pacRes) => cb(
err, pacErr,
Object.assign(res || {}, {lastModified: newLastModified}) Object.assign(pacRes || {}, { lastModified: newLastModified })
) )
); );
}, },
clarifyThen( clarifyThen(
'Не удалось скачать PAC-скрипт с адресов: [ ' `Не удалось скачать PAC-скрипт с адресов: [ ${provider.pacUrls.join(' , ')} ].`,
+ provider.pacUrls.join(' , ') + ' ].',
cb cb
) )
); );
return undefined;
}); });
}; };
window.apis.antiCensorRu = { const currentVersion = chrome.runtime.getManifest().version;
version: chrome.runtime.getManifest().version, const privates = {
pacUpdatePeriodInMinutes: 12 * 60,
periodicUpdateAlarmReason: 'Периодичное обновление PAC-скрипта',
state: window.utils.createStorage('anti-censor-ru-'),
get version() {
return this.state('version');
},
set version(newValue) {
return this.state('version', newValue);
},
get ifFirstInstall() {
return this.version === null;
},
set ifFirstInstall(newValue) {
if (newValue) {
throw new TypeError('ifFirstInstall can\'t be set to true!');
}
this.version = currentVersion;
},
get currentPacProviderKey() {
return this.state('current-pac');
},
set currentPacProviderKey(newValue) {
return this.state('current-pac', newValue);
},
get lastPacUpdateStamp() {
return this.state('last-pac-update-stamp') || 0;
},
set lastPacUpdateStamp(newValue) {
return this.state('last-pac-update-stamp', newValue);
},
get currentPacProviderLastModified() {
return this.state('current-pac-last-mod') || 0;
},
set currentPacProviderLastModified(newValue) {
return this.state('current-pac-last-mod', newValue);
},
};
window.apis.antiCensorRu = {
pacProviders: { pacProviders: {
Антизапрет: { Антизапрет: {
@ -199,20 +244,24 @@
}, },
}, },
_currentPacProviderKey: 'Антизапрет',
/* Is it the first time extension installed? /* Is it the first time extension installed?
Do something, e.g. initiate PAC sync. Do something, e.g. initiate PAC sync.
*/ */
ifFirstInstall: false, get ifFirstInstall() {
lastPacUpdateStamp: 0,
_currentPacProviderLastModified: 0, // Not initialized. return privates.ifFirstInstall;
},
get lastPacUpdateStamp() {
return privates.lastPacUpdateStamp;
},
getLastModifiedForKey(key = mandatory()) { getLastModifiedForKey(key = mandatory()) {
if (this._currentPacProviderKey === key) { if (privates.currentPacProviderKey === key) {
return new Date(this._currentPacProviderLastModified).toUTCString(); return new Date(privates.currentPacProviderLastModified).toUTCString();
} }
return new Date(0).toUTCString(); return new Date(0).toUTCString();
@ -220,21 +269,21 @@
setLastModified(newValue = mandatory()) { setLastModified(newValue = mandatory()) {
this._currentPacProviderLastModified = newValue; privates.currentPacProviderLastModified = newValue;
}, },
mustBeKey(key = mandatory()) { mustBeKey(key = mandatory()) {
if ( !(key === null || this.pacProviders[key]) ) { if (key !== null && !this.pacProviders[key]) {
throw new TypeError('No provider for key:' + key); throw new TypeError(`No provider for key:${key}`);
} }
}, },
getCurrentPacProviderKey() { getCurrentPacProviderKey() {
return this._currentPacProviderKey; return privates.currentPacProviderKey;
}, },
@ -244,15 +293,17 @@
) { ) {
this.mustBeKey(newKey); this.mustBeKey(newKey);
this._currentPacProviderKey = newKey; privates.currentPacProviderKey = newKey;
this._currentPacProviderLastModified = lastModified; privates.currentPacProviderLastModified = lastModified;
}, },
getPacProvider(key) { getPacProvider(maybeKey) {
if(key) { let key;
this.mustBeKey(key); if (maybeKey) {
this.mustBeKey(maybeKey);
key = maybeKey;
} else { } else {
key = this.getCurrentPacProviderKey(); key = this.getCurrentPacProviderKey();
} }
@ -260,40 +311,18 @@
}, },
_periodicUpdateAlarmReason: 'Периодичное обновление PAC-скрипта', syncWithPacProviderAsync(maybeKey = this.currentPacProvierKey, maybeCb = throwIfError) {
pushToStorageAsync(cb = throwIfError) { let key;
let originalCb;
console.log('Pushing to storage...'); if (typeof maybeKey === 'function') {
// Copy only settable properties (except functions).
const onlySettable = {};
for(const key of Object.keys(this)) {
if (
Object.getOwnPropertyDescriptor(this, key).writable
&& typeof(this[key]) !== 'function'
) {
onlySettable[key] = this[key];
}
}
chrome.storage.local.clear(
() => chrome.storage.local.set(
onlySettable,
chromified(cb)
)
);
},
syncWithPacProviderAsync(
key = this.currentPacProvierKey, cb = throwIfError) {
if( typeof(key) === 'function' ) {
cb = key;
key = this.getCurrentPacProviderKey(); key = this.getCurrentPacProviderKey();
originalCb = maybeKey;
} else {
key = maybeKey;
originalCb = maybeCb;
} }
cb = asyncLogGroup('Syncing with PAC provider ' + key + '...', cb); const cb = asyncLogGroup(`Syncing with PAC provider ${key}...`, originalCb);
if (key === null) { if (key === null) {
// No pac provider set. // No pac provider set.
@ -303,15 +332,15 @@
const pacProvider = this.getPacProvider(key); const pacProvider = this.getPacProvider(key);
const pacSetPromise = new Promise( const pacSetPromise = new Promise(
(resolve, reject) => setPacScriptFromProviderAsync( (resolve) => setPacScriptFromProviderAsync(
pacProvider, pacProvider,
this.getLastModifiedForKey(key), this.getLastModifiedForKey(key),
(err, res, ...warns) => { (err, res, ...warns) => {
if (!err) { if (!err) {
this.setCurrentPacProviderKey(key, res.lastModified); this.setCurrentPacProviderKey(key, res.lastModified);
this.lastPacUpdateStamp = Date.now(); privates.lastPacUpdateStamp = Date.now();
this.ifFirstInstall = false; privates.ifFirstInstall = false;
this.setAlarms(); this.setAlarms();
} }
@ -322,7 +351,7 @@
); );
const ipsErrorPromise = new Promise( const ipsErrorPromise = new Promise(
(resolve, reject) => updatePacProxyIps( (resolve) => updatePacProxyIps(
resolve resolve
) )
); );
@ -337,27 +366,25 @@
if (ipsErr) { if (ipsErr) {
warns.push(ipsErr); warns.push(ipsErr);
} }
this.pushToStorageAsync( return cb(pacErr, null, ...warns);
(pushErr) => cb(pacErr || pushErr, null, ...warns)
);
}, },
cb cb
); );
return undefined;
}, },
_pacUpdatePeriodInMinutes: 12*60,
get pacUpdatePeriodInMinutes() { get pacUpdatePeriodInMinutes() {
return this._pacUpdatePeriodInMinutes; return privates.pacUpdatePeriodInMinutes;
}, },
setAlarms() { setAlarms() {
let nextUpdateMoment = this.lastPacUpdateStamp let nextUpdateMoment = this.lastPacUpdateStamp
+ this._pacUpdatePeriodInMinutes*60*1000; + (privates.pacUpdatePeriodInMinutes * 60 * 1000);
const now = Date.now(); const now = Date.now();
if (nextUpdateMoment < now) { if (nextUpdateMoment < now) {
nextUpdateMoment = now; nextUpdateMoment = now;
@ -369,10 +396,10 @@
); );
chrome.alarms.create( chrome.alarms.create(
this._periodicUpdateAlarmReason, privates.periodicUpdateAlarmReason,
{ {
when: nextUpdateMoment, when: nextUpdateMoment,
periodInMinutes: this._pacUpdatePeriodInMinutes, periodInMinutes: privates.pacUpdatePeriodInMinutes,
} }
); );
@ -390,14 +417,14 @@
if (this.currentProviderKey !== key) { if (this.currentProviderKey !== key) {
return this.syncWithPacProviderAsync(key, cb); return this.syncWithPacProviderAsync(key, cb);
} }
console.log(key + ' already installed.'); console.log(`${key} already installed.`);
cb(); return cb();
}, },
clearPacAsync(cb = throwIfError) { clearPacAsync(originalCb = throwIfError) {
cb = asyncLogGroup('Cearing alarms and PAC...', cb); const cb = asyncLogGroup('Cearing alarms and PAC...', originalCb);
chrome.alarms.clearAll( chrome.alarms.clearAll(
() => chrome.proxy.settings.clear( () => chrome.proxy.settings.clear(
{}, {},
@ -407,9 +434,7 @@
return cb(err); return cb(err);
} }
this.setCurrentPacProviderKey(null); this.setCurrentPacProviderKey(null);
this.pushToStorageAsync( return handlers.updateControlState(cb);
() => handlers.updateControlState(cb)
);
}) })
) )
@ -420,12 +445,7 @@
}; };
// ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE // ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE
chrome.storage.local.get(null, chromified( (err, oldStorage) => { ((async function init() {
if (err) {
throw err;
}
/* /*
Event handlers that ALWAYS work (even if installation is not done Event handlers that ALWAYS work (even if installation is not done
or failed). or failed).
@ -435,14 +455,14 @@
const antiCensorRu = window.apis.antiCensorRu; const antiCensorRu = window.apis.antiCensorRu;
chrome.alarms.onAlarm.addListener( chrome.alarms.onAlarm.addListener(
timeouted( (alarm) => { timeouted((alarm) => {
if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) { if (alarm.name === privates.periodicUpdateAlarmReason) {
console.log( console.log(
'Periodic PAC update triggered:', 'Periodic PAC update triggered:',
new Date().toLocaleString('ru-RU') new Date().toLocaleString('ru-RU')
); );
antiCensorRu.syncWithPacProviderAsync(() => {/* swallow */}); antiCensorRu.syncWithPacProviderAsync(() => { /* swallow */ });
} }
}) })
@ -456,24 +476,14 @@
}); });
console.log('Storage on init:', oldStorage);
antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0;
if (antiCensorRu.ifFirstInstall) { if (antiCensorRu.ifFirstInstall) {
// INSTALL // INSTALL
console.log('Installing...'); console.log('Installing...');
privates.currentPacProviderKey = 'Антизапрет';
return chrome.runtime.openOptionsPage(); return chrome.runtime.openOptionsPage();
} }
// LAUNCH, RELOAD, UPDATE // LAUNCH, RELOAD, UPDATE
// Use old or migrate to default.
antiCensorRu._currentPacProviderKey =
oldStorage._currentPacProviderKey || null;
antiCensorRu.lastPacUpdateStamp =
oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp;
antiCensorRu._currentPacProviderLastModified =
oldStorage._currentPacProviderLastModified
|| antiCensorRu._currentPacProviderLastModified;
console.log( console.log(
'Last PAC update was on', 'Last PAC update was on',
new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU') new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU')
@ -487,44 +497,48 @@
2. We have to check storage for migration before using it. 2. We have to check storage for migration before using it.
Better on each launch then on each pull. Better on each launch then on each pull.
*/ */
const ifUpdating = antiCensorRu.version !== oldStorage.version; const ifUpdating = currentVersion !== privates.version;
if (!ifUpdating) { if (!ifUpdating) {
// LAUNCH, RELOAD, ENABLE // LAUNCH, RELOAD, ENABLE
antiCensorRu.pacProviders = oldStorage.pacProviders;
console.log('Extension launched, reloaded or enabled.'); console.log('Extension launched, reloaded or enabled.');
} else { } else {
// UPDATE & MIGRATION // UPDATE & MIGRATION
const key = antiCensorRu._currentPacProviderKey; // Use old or migrate to defaults.
if (key !== null) { const oldStorage = await new Promise((resolve) =>
const ifVeryOld = !Object.keys(antiCensorRu.pacProviders).includes(key); chrome.storage.local.get(null, resolve)
const ifWasForced = localStorage.getItem('provider-backup'); );
if ( ifVeryOld || !ifWasForced ) { if (Object.keys(oldStorage).length) {
if (!ifWasForced) { // eslint-disable-next-line no-underscore-dangle
localStorage.setItem('provider-backup', antiCensorRu._currentPacProviderKey); let oldKey = oldStorage._currentPacProviderKey;
if (oldKey) {
const ifVeryOld = !Object.keys(antiCensorRu.pacProviders).includes(oldKey);
const ifWasForced = localStorage.getItem('provider-backup');
if (ifVeryOld || !ifWasForced) {
if (!ifWasForced) {
localStorage.setItem('provider-backup', oldKey);
}
oldKey = 'Антизапрет';
} }
antiCensorRu._currentPacProviderKey = 'Антизапрет';
} }
privates.currentPacProviderKey = oldKey || null;
// eslint-disable-next-line no-underscore-dangle
privates.currentPacProviderLastModified = oldStorage._currentPacProviderLastModified || 0;
privates.lastPacUpdateStamp = oldStorage.lastPacUpdateStamp || 0;
await new Promise((resolve) => chrome.storage.local.clear(resolve));
} }
privates.version = currentVersion;
console.log('Extension updated.'); console.log('Extension updated.');
} }
if (antiCensorRu.getPacProvider()) { if (antiCensorRu.getPacProvider()) {
antiCensorRu.setAlarms();
const ifAlarmTriggered = antiCensorRu.setAlarms();
if (ifAlarmTriggered) {
return; // Already pushed.
}
}
if( ifUpdating ) {
antiCensorRu.pushToStorageAsync();
} }
return undefined;
/* /*
History of Changes to Storage (Migration Guide) History of Changes to Storage (Migration Guide)
@ -540,7 +554,6 @@
* Change storage.ifNotInstalled to storage.ifFirstInstall. * Change storage.ifNotInstalled to storage.ifFirstInstall.
* Add storage.lastPacUpdateStamp. * Add storage.lastPacUpdateStamp.
**/ **/
})());
}));
} }

View File

@ -12,13 +12,13 @@
chrome.runtime.onInstalled.addListener( chrome.runtime.onInstalled.addListener(
() => chrome.contextMenus.create({ () => chrome.contextMenus.create({
id: id, id,
title: title, title,
contexts: ['browser_action'], contexts: ['browser_action'],
}, timeouted(() => { }, timeouted(() => {
const err = chrome.runtime.lastError; const err = chrome.runtime.lastError;
if(err) { if (err) {
console.warn('Context menu error:', err); console.warn('Context menu error:', err);
throw err; throw err;
} }
@ -28,9 +28,9 @@
chrome.contextMenus.onClicked.addListener((info, tab) => { chrome.contextMenus.onClicked.addListener((info, tab) => {
if(info.menuItemId === id) { if (info.menuItemId === id) {
Promise.resolve( tab2url( tab ) ) Promise.resolve(tab2url(tab))
.then( (url) => chrome.tabs.create({url: url}) ); .then((url) => chrome.tabs.create({ url }));
} }
}); });
@ -45,32 +45,32 @@
<input name='InstantCheckUrl' value='${new URL(tab.url).hostname}' <input name='InstantCheckUrl' value='${new URL(tab.url).hostname}'
type='hidden'> type='hidden'>
</form> </form>
<script>document.querySelector('.tracker-form').submit()<\/script>` <script>document.querySelector('.tracker-form').submit()</script>`
); );
createMenuLinkEntry( createMenuLinkEntry(
'Сайт в реестре блокировок?', 'Сайт в реестре блокировок?',
(tab) => 'https://antizapret.info/index.php?search=' + new URL(tab.url).hostname (tab) => `https://antizapret.info/index.php?search=${new URL(tab.url).hostname}`
); );
createMenuLinkEntry( createMenuLinkEntry(
'Из архива archive.org', 'Из архива archive.org',
(tab) => 'https://web.archive.org/web/*/' + tab.url (tab) => `https://web.archive.org/web/*/${tab.url}`
); );
createMenuLinkEntry( createMenuLinkEntry(
'Через Google Translate', 'Через Google Translate',
(tab) => 'https://translate.google.com/translate?hl=&sl=en&tl=ru&anno=2&sandbox=1&u=' + tab.url (tab) => `https://translate.google.com/translate?hl=&sl=en&tl=ru&anno=2&sandbox=1&u=${tab.url}`
); );
createMenuLinkEntry( createMenuLinkEntry(
'Другие варианты разблокировки', 'Другие варианты разблокировки',
(tab) => 'https://rebrand.ly/ac-unblock#' + tab.url (tab) => `https://rebrand.ly/ac-unblock#${tab.url}`
); );
createMenuLinkEntry( createMenuLinkEntry(
'У меня проблемы с расширением!', 'У меня проблемы с расширением!',
(tab) => 'https://rebrand.ly/ac-support' () => 'https://rebrand.ly/ac-support'
); );
} }

View File

@ -9,100 +9,42 @@
// IP REGEX starts. // IP REGEX starts.
const portOpt = '(:\\d+)?'; // The only capturing group, sic! const portOpt = '(:\\d+)?'; // The only capturing group, sic!
const ipv4portOpt = '(?:[0-9]{1,3}\\.){3}[0-9]{1,3}' + portOpt; const ipv4portOpt = `(?:[0-9]{1,3}\\.){3}[0-9]{1,3}${portOpt}`;
const ipv6nake = '(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}'; const ipv6nake = '(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}';
const ipv6portOpt = '(?:' + ipv6nake + '|' + '\\[' + ipv6nake + '\\]' + portOpt + ')'; const ipv6portOpt = `(?:${ipv6nake}|\\[${ipv6nake}\\]${portOpt})`;
const ipv4Re = new RegExp('^' + ipv4portOpt + '$'); const ipv4Re = new RegExp(`^${ipv4portOpt}$`);
const ipv6Re = new RegExp('^' + ipv6portOpt + '$'); const ipv6Re = new RegExp(`^${ipv6portOpt}$`);
const _match = function _match(ipRe, str) { const reMatchIp = function reMatchIp(ipRe, str) {
const m = (str.match(ipRe) || []).filter( (c) => c ); const m = (str.match(ipRe) || []).filter((c) => c);
const port = m.length > 1 ? m.pop() : false; const port = m.length > 1 ? m.pop() : false;
return {ifMatched: m.length, port: port}; return { ifMatched: m.length, port };
}; };
const _test = { const matchIpv4v6 = function matchIpv4v6(str) {
ipv4: _match.bind(null, ipv4Re), let mr = reMatchIp(ipv4Re, str);
ipv6: _match.bind(null, ipv6Re), if (mr.ifMatched) {
ipv4v6: function(str) { mr.ifv4 = true;
mr.canonical = str.replace(mr.port, '');
let mr = this.ipv4(str);
if (mr.ifMatched) {
mr.ifv4 = true;
mr.canonical = str.replace(mr.port, '');
return mr;
}
mr = this.ipv6(str);
if (mr.ifMatched) {
mr.ifv6 = true;
mr.canonical = str.replace(mr.port, '').replace(/[\[\]]/g, '');
return mr;
}
return mr; return mr;
}
}, mr = reMatchIp(ipv6Re, str);
if (mr.ifMatched) {
mr.ifv6 = true;
mr.canonical = str.replace(mr.port, '').replace(/[[\]]/g, '');
return mr;
}
return mr;
}; };
// IP REGEX ends. // IP REGEX ends.
const _state = window.utils.createStorage('ip-to-host'); // GET IPS starts.
const ip2host = '';
const privates = {};
const _createHostObj = function _addHostObj(hostStr) {
return (privates._strToHostObj[hostStr] = {host: hostStr});
};
const _getHostObj = function _getHostObj(hostStr) {
return privates._strToHostObj[hostStr] || _createHostObj(hostStr);
};
const reinit = function reinit() {
// Defaults.
const _antizapret = {
/* Don't use directly, please.
Encoded to counter abuse. */
host: '\x70\x72\x6f\x78\x79\x2e\x61\x6e\x74\x69\x7a\x61\x70\x72\x65\x74\x2e\x70\x72\x6f\x73\x74\x6f\x76\x70\x6e\x2e\x6f\x72\x67',
};
privates._strToHostObj = {
[_antizapret.host]: _antizapret,
};
privates._ipToHostObj = {};
for( const ip of [
// IPs of Antizapret.
'195.123.209.38',
'137.74.171.91',
'51.15.39.201',
'2001:bc8:4700:2300::1:d07',
'2a02:27ac::10',
] ) {
privates._ipToHostObj[ip] = _antizapret;
}
// Persisted.
const ipToHost = _state(ip2host);
if (ipToHost) {
for( const ip of Object.keys(ipToHost) ) {
const host = ipToHost[ip];
privates._ipToHostObj[ip] = _getHostObj(host);
}
}
};
reinit();
const getIpsFor = function getIpsFor(host, cb = mandatory()) { const getIpsFor = function getIpsFor(host, cb = mandatory()) {
@ -113,29 +55,32 @@
const promises = types.map( const promises = types.map(
(type) => new Promise((resolve) => (type) => new Promise((resolve) =>
httpLib.get( httpLib.get(
'https://dns.google.com/resolve?type=' + type + '&name=' + host, `https://dns.google.com/resolve?type=${type}&name=${host}`,
(err, res) => { (httpErr, httpRes) => {
if (res) { let res = httpRes;
let err = httpErr;
if (httpRes) {
let jsonRes = {};
try { try {
res = JSON.parse(res); jsonRes = JSON.parse(httpRes);
console.log('JSON parsed.'); res = jsonRes;
if (err || res.Status) { if (httpErr || jsonRes.Status) {
const msg = ['Answer', 'Comment', 'Status'] const msg = ['Answer', 'Comment', 'Status']
.filter( (prop) => res[prop] ) .filter((prop) => jsonRes[prop])
.map( (prop) => prop + ': ' + JSON.stringify( res[prop] ) ) .map((prop) => `${prop}: ${JSON.stringify(jsonRes[prop])}`)
.join(', \n'); .join(', \n');
err = clarify(err || {}, 'Сервер (json): ' + msg, {data: res}); err = clarify(httpErr || {}, `Сервер (json): ${msg}`, { data: jsonRes });
} else { } else {
res = res.Answer || []; res = (jsonRes.Answer || []).filter(
res = res.filter(
(record) => types.includes(record.type) (record) => types.includes(record.type)
).map( (ans) => ans.data ); ).map((ans) => ans.data);
} }
} catch(e) { } catch (e) {
err = clarify( err = clarify(
e, e,
'Сервер (текст): ' + res, err ? {data: err} : undefined `Сервер (текст): ${res}`,
httpErr ? { data: httpErr } : undefined
); );
} }
} }
@ -148,7 +93,7 @@
Promise.all(promises).then( Promise.all(promises).then(
([[v4err, v4res], [v6err, v6res]]) => { ([[v4err, v4res], [v6err, v6res]]) => {
if(v4err) { if (v4err) {
return cb(v4err, v4res); return cb(v4err, v4res);
} }
const ips = v4res; const ips = v4res;
@ -158,77 +103,101 @@
} else { } else {
warns = [v6err]; warns = [v6err];
} }
cb(null, ips, ...warns); return cb(null, ips, ...warns);
} }
); );
return undefined;
}; };
const _canonize = function canonize(addrArr) { // GET IPS ends.
const ipSet = new Set(); const ipToHostKey = '';
const hostSet = new Set(); const privates = {
for( const addr of addrArr ) { state: window.utils.createStorage('ip-to-host'),
const ipm = _test.ipv4v6(addr); createHostObj(hostStr) {
if (ipm.ifMatched) {
ipSet.add( ipm.canonical );
} else {
hostSet.add( addr.replace(/:\d+$/, '') );
}
} return (this.strToHostObj[hostStr] = { host: hostStr });
console.log('Canonized hosts/ips:', hostSet.size + '/' + ipSet.size);
return [ipSet, hostSet];
};
const self = window.apis.ipToHost = {
persistData() {
console.log('Persisting ipToHost...', privates);
const ipToHost = {};
for( const ip of Object.keys(privates._ipToHostObj) ) {
ipToHost[ip] = privates._ipToHostObj[ip].host;
}
_state(ip2host, ipToHost);
}, },
getHostObj(hostStr) {
return this.strToHostObj[hostStr] || this.createHostObj(hostStr);
},
reinit() {
// Defaults.
const antizapret = {
/* Don't use directly, please.
Encoded to counter abuse. */
host: '\x70\x72\x6f\x78\x79\x2e\x61\x6e\x74\x69\x7a\x61\x70\x72\x65\x74\x2e\x70\x72\x6f\x73\x74\x6f\x76\x70\x6e\x2e\x6f\x72\x67',
};
this.strToHostObj = {
[antizapret.host]: antizapret,
};
this.ipToHostObj = {};
[ // IPs of Antizapret.
'195.123.209.38',
'137.74.171.91',
'51.15.39.201',
'2001:bc8:4700:2300::1:d07',
'2a02:27ac::10',
].forEach((ip) => {
this.ipToHostObj[ip] = antizapret;
});
// Persisted.
const ipToHost = this.state(ipToHostKey);
if (ipToHost) {
Object.keys(ipToHost).forEach((ip) => {
const host = ipToHost[ip];
this.ipToHostObj[ip] = this.getHostObj(host);
});
}
},
// POST INIT
resetToDefaults() { resetToDefaults() {
_state(ip2host, null); this.state(ipToHostKey, null);
reinit(); this.reinit();
}, },
_purgeOldIpsForSync(hostStr) { purgeOldIpsForSync(hostStr) {
console.log('Purging old IPs for', hostStr); console.log('Purging old IPs for', hostStr);
for(const ip of Object.keys(privates._ipToHostObj)) { Object.keys(privates.ipToHostObj).forEach((ip) => {
if (hostStr === privates._ipToHostObj[ip].host) { if (hostStr === this.ipToHostObj[ip].host) {
delete privates._ipToHostObj[ip]; delete this.ipToHostObj[ip];
} }
} });
}, },
_addAsync(hostStr, cb = mandatory()) { addAsync(hostStr, cb = mandatory()) {
getIpsFor(hostStr, (err, ips, ...warns) => { getIpsFor(hostStr, (err, ips, ...warns) => {
console.log('Got IPs + err?:', ips, err); console.log('Got IPs + err?:', ips, err);
if (!err) { if (!err) {
this._purgeOldIpsForSync(hostStr); this.purgeOldIpsForSync(hostStr);
// Object may be shared, string can't. // Object may be shared, string can't.
const hostObj = _getHostObj(hostStr); const hostObj = privates.getHostObj(hostStr);
for(const ip of ips) { ips.forEach((ip) => {
privates._ipToHostObj[ip] = hostObj; privates.ipToHostObj[ip] = hostObj;
} });
} }
return cb(err, null, ...warns); return cb(err, null, ...warns);
@ -236,22 +205,22 @@
}, },
_updateAllAsync(cb = mandatory()) { updateAllAsync(cb = mandatory()) {
const hostArr = Object.keys(privates._strToHostObj); const hostArr = Object.keys(this.strToHostObj);
console.log('Update all:', hostArr); console.log('Update all:', hostArr);
const promises = hostArr.map( const promises = hostArr.map(
(hostStr) => new Promise( (hostStr) => new Promise(
(resolve) => this._addAsync( (resolve) => this.addAsync(
hostStr, hostStr,
(...args) => resolve(args) (...args) => resolve(args)
) )
) )
); );
Promise.all( promises ).then( (cbsRes) => { Promise.all(promises).then((cbsRes) => {
const errors = cbsRes.map( ([err]) => err ).filter( (err) => err ); const errors = cbsRes.map(([err]) => err).filter((err) => err);
let newError; let newError;
const ifAllErrors = cbsRes.length === errors.length; const ifAllErrors = cbsRes.length === errors.length;
if (errors.length) { if (errors.length) {
@ -270,25 +239,71 @@
return cb(newError); return cb(newError);
} }
} }
cb(null, null, newError); return cb(null, null, newError);
}); });
}, },
_replaceAllAsync(hostArr = mandatory(), cb) { replaceAllAsync(maybeHostArr = mandatory(), maybeCb) {
if (typeof(hostArr) === 'function') { let hostArr;
cb = hostArr; let cb;
hostArr = Object.keys(privates._strToHostObj); if (typeof maybeHostArr === 'function') {
hostArr = Object.keys(this.strToHostObj);
cb = maybeHostArr;
} else {
hostArr = maybeHostArr;
cb = maybeCb;
} }
this.resetToDefaults(); this.resetToDefaults();
for(const hostStr of hostArr) { hostArr.forEach((hostStr) => this.createHostObj(hostStr));
_createHostObj(hostStr);
this.updateAllAsync(cb);
},
};
privates.reinit();
const canonize = function canonize(addrArr) {
const ipSet = new Set();
const hostSet = new Set();
addrArr.forEach((addr) => {
const ipm = matchIpv4v6(addr);
if (ipm.ifMatched) {
ipSet.add(ipm.canonical);
} else {
hostSet.add(addr.replace(/:\d+$/, ''));
} }
this._updateAllAsync(cb); });
console.log(`Canonized hosts/ips: ${hostSet.size}/${ipSet.size}`);
return [ipSet, hostSet];
};
const theApi = window.apis.ipToHost = {
persistData() {
console.log('Persisting ipToHost...', privates);
const ipToHost = {};
Object.keys(privates.ipToHostObj).forEach((ip) => {
ipToHost[ip] = privates.ipToHostObj[ip].host;
});
privates.state(ipToHostKey, ipToHost);
},
resetToDefaults() {
privates.resetToDefaults();
}, },
@ -296,7 +311,7 @@
updateAllAsync(cb = mandatory()) { updateAllAsync(cb = mandatory()) {
this._updateAllAsync((err, ...args) => { privates.updateAllAsync((err, ...args) => {
if (!err) { if (!err) {
this.persistData(); this.persistData();
@ -310,14 +325,15 @@
replaceAllAsync(addrArr, cb = mandatory()) { replaceAllAsync(addrArr, cb = mandatory()) {
console.log('Replacing...'); console.log('Replacing...');
const [ipSet, hostSet] = _canonize(addrArr); const [ipSet, hostSet] = canonize(addrArr);
for( const ip of ipSet ) { ipSet.forEach((ip) => {
const host = _getHostObj(ip);
privates._ipToHostObj[ip] = host; privates.ipToHostObj[ip] = privates.getHostObj(ip);
}
});
const hostArr = Array.from(hostSet); const hostArr = Array.from(hostSet);
this._replaceAllAsync(hostArr, (allErr, ...args) => { privates.replaceAllAsync(hostArr, (allErr, ...args) => {
if (!allErr) { if (!allErr) {
this.persistData(); this.persistData();
@ -330,7 +346,7 @@
get(ip) { get(ip) {
const tmp = privates._ipToHostObj[ip]; const tmp = privates.ipToHostObj[ip];
return tmp && tmp.host; return tmp && tmp.host;
}, },
@ -338,14 +354,14 @@
}; };
window.utils.addRequestResponder( window.utils.addRequestResponder(
'ip-to-host-update-all', (...args) => self.updateAllAsync(...args) 'ip-to-host-update-all', (...args) => theApi.updateAllAsync(...args)
); );
window.utils.addRequestResponder( window.utils.addRequestResponder(
'ip-to-host-replace-all', (...args) => self.replaceAllAsync(...args) 'ip-to-host-replace-all', (...args) => theApi.replaceAllAsync(...args)
); );
window.utils.addRequestResponder( window.utils.addRequestResponder(
'ip-to-host-reset-to-defaults', (cb) => { 'ip-to-host-reset-to-defaults', (cb) => {
self.resetToDefaults(); theApi.resetToDefaults();
cb(); cb();
} }
); );

View File

@ -20,47 +20,52 @@
color: '#db4b2f', color: '#db4b2f',
}); });
const _tabCallbacks = {}; const privates = {};
privates.tabCallbacks = {};
const afterTabUpdated = function afterTabUpdated(tabId, cb) { const afterTabUpdated = function afterTabUpdated(tabId, cb) {
if (_tabCallbacks[tabId]) { if (privates.tabCallbacks[tabId]) {
_tabCallbacks[tabId].push(cb); privates.tabCallbacks[tabId].push(cb);
} else { } else {
_tabCallbacks[tabId] = [cb]; privates.tabCallbacks[tabId] = [cb];
} }
}; };
const onTabUpdate = function onTabUpdate(tabId) { const onTabUpdate = function onTabUpdate(tabId) {
if (_tabCallbacks[tabId]) { if (privates.tabCallbacks[tabId]) {
_tabCallbacks[tabId].map( (f) => f() ); privates.tabCallbacks[tabId].map((f) => f());
delete _tabCallbacks[tabId]; delete privates.tabCallbacks[tabId];
} }
}; };
chrome.tabs.onUpdated.addListener( onTabUpdate ); chrome.tabs.onUpdated.addListener(onTabUpdate);
const updateTitle = function updateTitle(requestDetails, proxyHost, cb) { const updateTitle = function updateTitle(requestDetails, proxyHost, cb) {
chrome.browserAction.getTitle( chrome.browserAction.getTitle(
{tabId: requestDetails.tabId}, { tabId: requestDetails.tabId },
(title) => { (title) => {
const ifTitleSetAlready = /\n/.test(title); const ifTitleSetAlready = /\n/.test(title);
const hostname = new URL( requestDetails.url ).hostname; const hostname = new URL(requestDetails.url).hostname;
let ifShouldUpdateTitle = false; let ifShouldUpdateTitle = false;
const indent = ' '; const indent = ' ';
const proxyTitle = 'Прокси:'; const proxyTitle = 'Прокси:';
let theLatestCb = cb;
let newTitle = title;
if (!ifTitleSetAlready) { if (!ifTitleSetAlready) {
title = 'Разблокированы:\n' + indent + hostname + '\n' newTitle = `Разблокированы:\n${indent}${hostname}\n${proxyTitle}\n`
+ proxyTitle + '\n' + indent + proxyHost; + `${indent}${proxyHost}`;
ifShouldUpdateTitle = true; ifShouldUpdateTitle = true;
chrome.browserAction.setBadgeText({ chrome.browserAction.setBadgeText({
@ -70,37 +75,36 @@
} else { } else {
const hostsProxiesPair = title.split(proxyTitle); const hostsProxiesPair = newTitle.split(proxyTitle);
if (hostsProxiesPair[1].indexOf(proxyHost) === -1) { if (hostsProxiesPair[1].indexOf(proxyHost) === -1) {
title = title.replace( newTitle = newTitle.replace(
hostsProxiesPair[1], hostsProxiesPair[1],
hostsProxiesPair[1] + '\n' + indent + proxyHost `${hostsProxiesPair[1]}\n${indent}${proxyHost}`
); );
ifShouldUpdateTitle = true; ifShouldUpdateTitle = true;
} }
if (hostsProxiesPair[0].indexOf(hostname) === -1) { if (hostsProxiesPair[0].indexOf(hostname) === -1) {
title = title.replace( newTitle = newTitle.replace(
proxyTitle, proxyTitle,
indent + hostname + '\n' + proxyTitle `${indent}${hostname}\n${proxyTitle}`
); );
ifShouldUpdateTitle = true; ifShouldUpdateTitle = true;
const _cb = cb; theLatestCb = () => chrome.browserAction.getBadgeText(
cb = () => chrome.browserAction.getBadgeText( { tabId: requestDetails.tabId },
{tabId: requestDetails.tabId},
(result) => { (result) => {
const charPrefix = isNaN(result.charAt(0)) ? result.charAt(0) : '';
chrome.browserAction.setBadgeText( chrome.browserAction.setBadgeText(
{ {
tabId: requestDetails.tabId, tabId: requestDetails.tabId,
text: (isNaN( result.charAt(0)) && result.charAt(0) || '') text: charPrefix + (hostsProxiesPair[0].split('\n').length - 1),
+ (hostsProxiesPair[0].split('\n').length - 1),
} }
); );
return _cb(); return cb();
} }
); );
@ -111,12 +115,12 @@
if (ifShouldUpdateTitle) { if (ifShouldUpdateTitle) {
chrome.browserAction.setTitle({ chrome.browserAction.setTitle({
title: title, title: newTitle,
tabId: requestDetails.tabId, tabId: requestDetails.tabId,
}); });
} }
return cb(); return theLatestCb();
} }
); );
@ -127,7 +131,7 @@
const tryProxyAndInform = function tryProxyAndInform(requestDetails) { const tryProxyAndInform = function tryProxyAndInform(requestDetails) {
const host = window.apis.ipToHost.get( requestDetails.ip ); const host = window.apis.ipToHost.get(requestDetails.ip);
if (!host) { if (!host) {
return; return;
} }
@ -137,7 +141,7 @@
previousUpdateTitleFinished = previousUpdateTitleFinished.then( previousUpdateTitleFinished = previousUpdateTitleFinished.then(
() => new Promise( () => new Promise(
(resolve) => { (resolve) => {
const cb = () => updateTitle( requestDetails, host, resolve ); const cb = () => updateTitle(requestDetails, host, resolve);
return ifMainFrame return ifMainFrame
? afterTabUpdated(requestDetails.tabId, cb) : cb(); ? afterTabUpdated(requestDetails.tabId, cb) : cb();
} }
@ -155,11 +159,11 @@
}; };
for(const eventName of ['onResponseStarted', 'onErrorOccurred']) { ['onResponseStarted', 'onErrorOccurred'].forEach((eventName) =>
chrome.webRequest[eventName].addListener( chrome.webRequest[eventName].addListener(
onRequest, onRequest,
{urls: ['<all_urls>']} { urls: ['<all_urls>'] }
); )
} );
} }

View File

@ -1,8 +1,5 @@
'use strict'; 'use strict';
{ window.apis.version.ifMini = true;
chrome.browserAction.setBadgeText({ text: 'M' });
window.apis.version.ifMini = true;
chrome.browserAction.setBadgeText({text: 'M'});
}

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
const commonContext = { const commonContext = {
version: '0.21', version: '0.22',
}; };
exports.contexts = {}; exports.contexts = {};