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 = {
extends: ['eslint:recommended', 'google'],
extends: ['eslint:recommended', 'airbnb'],
env: {
browser: true,
webextensions: true,
@ -20,14 +20,23 @@ module.exports = {
'no-console': 'off',
'padded-blocks': 'off',
'require-jsdoc': 'off',
// Taken from airbnb:
'no-multi-assign': 'off',
'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, {
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
}],
}],*/
},
};

View File

@ -8,8 +8,11 @@
"author": "Ilya Ig. Petrov",
"license": "GPLv3",
"devDependencies": {
"eslint": "^3.15.0",
"eslint-config-google": "^0.7.1"
"eslint": "^3.16.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": {
"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 also don't remove logs for sake of client-side troubleshooting
// (though no one sent me logs so far).
['log', 'warn', 'error'].forEach( (meth) => {
const _meth = window.console[meth].bind(console);
window.console[meth] = function(...args) {
['log', 'warn', 'error'].forEach((meth) => {
const originalMeth = window.console[meth].bind(console);
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) {
if(err) {
if (err) {
throw err;
}
@ -45,7 +45,7 @@
// method invokation.
const err = chrome.runtime.lastError || chrome.extension.lastError;
if (!err) {
return;
return null;
}
console.warn('API returned error:', err);
return new Error(err.message); // Add stack.
@ -62,7 +62,7 @@
chromified(cb = self.mandatory()) {
// Take error first callback and convert it to chrome API callback.
return function(...args) {
return function cbForChromeApi(...args) {
const err = self.checkChromeError();
self.timeouted(cb)(err, ...args);
@ -71,35 +71,42 @@
},
getProp(obj, path = self.mandatory()) {
getProp(targetObj, path = self.mandatory()) {
const props = path.split('.');
if (!props.length) {
throw new TypeError('Property must be supplied.');
}
const lastProp = props.pop();
for( const prop of props ) {
if (!(prop in obj)) {
const ifSupportsInOp = (obj) =>
['object', 'function'].includes(typeof obj);
return props.reduce((currentObj, prop) => {
if (
ifSupportsInOp(currentObj)
&& prop in currentObj
) {
return currentObj[prop];
}
return undefined;
}
obj = obj[prop];
}
return obj[lastProp];
}, targetObj);
},
assert(value) {
if(!value) {
if (!value) {
console.assert(value);
throw new Error('Assert failed for:' + value);
throw new Error(`Assert failed for:${value}`);
}
},
addRequestResponder(requestType, responder) {
if( privates.requestToResponder[requestType] ) {
if (privates.requestToResponder[requestType]) {
throw new TypeError(`Request ${requestType} already has responder!`);
}
privates.requestToResponder[requestType] = responder;
@ -109,9 +116,9 @@
fireRequest(requestType, ...args) {
const cb = args.slice(-1)[0];
self.assert(typeof(cb) === 'function');
self.assert(typeof cb === 'function');
const responder = privates.requestToResponder[requestType];
if(responder) {
if (responder) {
responder(...args);
} else {
cb();
@ -123,18 +130,18 @@
return function state(key, value) {
key = prefix + key;
const prefixedKey = prefix + key;
if (value === null) {
return localStorage.removeItem(key);
return localStorage.removeItem(prefixedKey);
}
if (value === undefined) {
const item = localStorage.getItem(key);
const item = localStorage.getItem(prefixedKey);
return item && JSON.parse(item);
}
if (value instanceof Date) {
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) {
return 'chrome://settings/search#' + (chrome.i18n.getMessage(niddle) || niddle);
return `chrome://settings/search#${(chrome.i18n.getMessage(niddle) || niddle)}`;
},
whichExtensionHtml() {
return chrome.i18n.getMessage('noControl') +
` <a href="${ this.searchSettingsForUrl('proxy') }">
${ chrome.i18n.getMessage('which') }
return `${chrome.i18n.getMessage('noControl')}
<a href="${this.searchSettingsForUrl('proxy')}">
${chrome.i18n.getMessage('which')}
</a>`;
},

View File

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

View File

@ -1,4 +1,5 @@
'use strict';
/*
* Error Library
* PURPOSE 1:
@ -32,7 +33,7 @@
},
clarify: function(err, message = mandatory(), {data} = {}) {
clarify(err, message = mandatory(), { data } = {}) {
if (!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()) {
const start = Date.now();
fetch(url, {cache: 'no-store'}).then(
fetch(url, { cache: 'no-store' }).then(
(res) => {
const textCb =
(err) => {
console.log('Reading response as text...');
res.text().then(
(text) => {
console.log('Calling CB...');
cb(err, text);
},
(text) => cb(err, text),
cb
);
};
const status = res.status;
if ( !( status >= 200 && status < 300 || status === 304 ) ) {
if (!((status >= 200 && status < 300) || status === 304)) {
return textCb(
errorsLib.clarify(
res,
'Получен ответ с неудачным HTTP-кодом ' + status + '.'
`Получен ответ с неудачным HTTP-кодом ${status}.`
)
);
}
console.log('GETed with success:', url, Date.now() - start);
textCb();
return textCb();
},
errorsLib.clarifyThen(checkCon, cb)

View File

@ -65,36 +65,10 @@
const getDefaults = function getDefaults() {
return Object.keys(configs).reduce((acc, key) => {
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;
}, []);
return Object.keys(configs).reduce((acc, key) =>
Object.assign(acc, { [key]: configs[key].dflt }),
{}
);
};
@ -110,17 +84,17 @@
);
Object.assign(this, defaults, mods);
this.ifNoMods = ifAllDefaults ? true : false;
this.ifNoMods = ifAllDefaults;
let customProxyArray = [];
if (this.customProxyStringRaw) {
customProxyArray = this.customProxyStringRaw
.replace(/#.*$/mg, '') // Strip comments.
.split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g )
.map( (p) => p.trim() )
.filter( (p) => p && /\s+/g.test(p) );
.split(/(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g)
.map((p) => p.trim())
.filter((p) => p && /\s+/g.test(p));
if (this.ifUseSecureProxiesOnly) {
customProxyArray = customProxyArray.filter( (p) => !p.startsWith('HTTP ') );
customProxyArray = customProxyArray.filter((p) => !p.startsWith('HTTP '));
}
}
if (this.ifUseLocalTor) {
@ -142,13 +116,13 @@
if (this.ifMindExceptions && this.exceptions) {
this.included = [];
this.excluded = [];
for(const host of Object.keys(this.exceptions)) {
Object.keys(this.exceptions).forEach((host) => {
if (this.exceptions[host]) {
this.included.push(host);
} else {
this.excluded.push(host);
}
}
});
if (this.included && !this.filteredCustomsString) {
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 = {
getPacMods: getCurrentConfigs,
@ -167,13 +208,13 @@
cook(pacData, pacMods = mandatory()) {
return pacMods.ifNoMods ? pacData : pacData + `${ kitchenStartsMark }
return pacMods.ifNoMods ? pacData : `${pacData}${kitchenStartsMark}
;+function(global) {
"use strict";
const originalFindProxyForURL = FindProxyForURL;
global.FindProxyForURL = function(url, host) {
${function() {
${((function generateNewFindProxyForURL() {
let res = pacMods.ifProhibitDns ? `
global.dnsResolve = function(host) { return null; };
@ -203,42 +244,42 @@
`;
}
if(
if (
!pacMods.ifUseSecureProxiesOnly &&
!pacMods.filteredCustomsString &&
pacMods.ifUsePacScriptProxies
) {
return res + `
return `${res}
return originalFindProxyForURL(url, host);
`;
}
return res + `
return `${res}
const originalProxyString = originalFindProxyForURL(url, host);
let originalProxyArray = originalProxyString.split(/(?:\\s*;\\s*)+/g).filter( (p) => p );
if (originalProxyArray.every( (p) => /^DIRECT$/i.test(p) )) {
// Directs only or null, no proxies.
return originalProxyString;
}
return ` +
function() {
return ${
((function getProxies() {
if (!pacMods.ifUsePacScriptProxies) {
return '"' + pacMods.filteredCustomsString + '"';
return `"${pacMods.filteredCustomsString}"`;
}
let filteredOriginalsExp = 'originalProxyString';
if (pacMods.ifUseSecureProxiesOnly) {
filteredOriginalsExp =
'originalProxyArray.filter( (p) => !p.toUpperCase().startsWith("HTTP ") ).join("; ")';
}
if ( !pacMods.filteredCustomsString ) {
if (!pacMods.filteredCustomsString) {
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) {
if ( kitchenState(ifIncontinence) ) {
this._tryNowAsync(details, () => {/* Swallow. */});
if (kitchenState(ifIncontinence)) {
privates.tryNowAsync(details, () => { /* Swallow. */ });
}
},
keepCookedNowAsync(pacMods = mandatory(), cb = throwIfError) {
keepCookedNowAsync(maybePacMods = mandatory(), maybeCb = throwIfError) {
if (typeof(pacMods) === 'function') {
cb = pacMods;
let pacMods;
let cb;
if (typeof maybePacMods === 'function') {
cb = maybePacMods;
pacMods = getCurrentConfigs();
} else {
try {
pacMods = new PacModifiers(pacMods);
} catch(e) {
pacMods = new PacModifiers(maybePacMods);
} catch (e) {
return cb(e);
}
kitchenState(modsKey, pacMods);
cb = maybeCb;
}
console.log('Keep cooked now...', pacMods);
this._tryNowAsync(
return privates.tryNowAsync(
(err, res, ...warns) => {
console.log('Try now err:', err);
@ -320,8 +326,12 @@
return cb(null, res, ...warns);
}
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) ));
const hosts = par.map((ps) => ps.split(/\s+/)[1]);
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 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');
if (!(pac && pac.data)) {
return originalSet(details, cb);
}
const pacMods = getCurrentConfigs();
pac.data = pacKitchen.cook( pac.data, pacMods );
originalSet({value: details.value}, (/* No args. */) => {
pac.data = pacKitchen.cook(pac.data, pacMods);
return originalSet({ value: details.value }, (/* No args. */) => {
kitchenState(ifIncontinence, null);
cb && cb();
cb();
});

View File

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

View File

@ -12,13 +12,13 @@
chrome.runtime.onInstalled.addListener(
() => chrome.contextMenus.create({
id: id,
title: title,
id,
title,
contexts: ['browser_action'],
}, timeouted(() => {
const err = chrome.runtime.lastError;
if(err) {
if (err) {
console.warn('Context menu error:', err);
throw err;
}
@ -28,9 +28,9 @@
chrome.contextMenus.onClicked.addListener((info, tab) => {
if(info.menuItemId === id) {
Promise.resolve( tab2url( tab ) )
.then( (url) => chrome.tabs.create({url: url}) );
if (info.menuItemId === id) {
Promise.resolve(tab2url(tab))
.then((url) => chrome.tabs.create({ url }));
}
});
@ -45,32 +45,32 @@
<input name='InstantCheckUrl' value='${new URL(tab.url).hostname}'
type='hidden'>
</form>
<script>document.querySelector('.tracker-form').submit()<\/script>`
<script>document.querySelector('.tracker-form').submit()</script>`
);
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(
'Из архива archive.org',
(tab) => 'https://web.archive.org/web/*/' + tab.url
(tab) => `https://web.archive.org/web/*/${tab.url}`
);
createMenuLinkEntry(
'Через 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(
'Другие варианты разблокировки',
(tab) => 'https://rebrand.ly/ac-unblock#' + tab.url
(tab) => `https://rebrand.ly/ac-unblock#${tab.url}`
);
createMenuLinkEntry(
'У меня проблемы с расширением!',
(tab) => 'https://rebrand.ly/ac-support'
() => 'https://rebrand.ly/ac-support'
);
}

View File

@ -9,100 +9,42 @@
// IP REGEX starts.
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 ipv6portOpt = '(?:' + ipv6nake + '|' + '\\[' + ipv6nake + '\\]' + portOpt + ')';
const ipv6portOpt = `(?:${ipv6nake}|\\[${ipv6nake}\\]${portOpt})`;
const ipv4Re = new RegExp('^' + ipv4portOpt + '$');
const ipv6Re = new RegExp('^' + ipv6portOpt + '$');
const ipv4Re = new RegExp(`^${ipv4portOpt}$`);
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;
return {ifMatched: m.length, port: port};
return { ifMatched: m.length, port };
};
const _test = {
const matchIpv4v6 = function matchIpv4v6(str) {
ipv4: _match.bind(null, ipv4Re),
ipv6: _match.bind(null, ipv6Re),
ipv4v6: function(str) {
let mr = this.ipv4(str);
let mr = reMatchIp(ipv4Re, str);
if (mr.ifMatched) {
mr.ifv4 = true;
mr.canonical = str.replace(mr.port, '');
return mr;
}
mr = this.ipv6(str);
mr = reMatchIp(ipv6Re, str);
if (mr.ifMatched) {
mr.ifv6 = true;
mr.canonical = str.replace(mr.port, '').replace(/[\[\]]/g, '');
mr.canonical = str.replace(mr.port, '').replace(/[[\]]/g, '');
return mr;
}
return mr;
},
};
// IP REGEX ends.
const _state = window.utils.createStorage('ip-to-host');
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();
// GET IPS starts.
const getIpsFor = function getIpsFor(host, cb = mandatory()) {
@ -113,29 +55,32 @@
const promises = types.map(
(type) => new Promise((resolve) =>
httpLib.get(
'https://dns.google.com/resolve?type=' + type + '&name=' + host,
(err, res) => {
`https://dns.google.com/resolve?type=${type}&name=${host}`,
(httpErr, httpRes) => {
if (res) {
let res = httpRes;
let err = httpErr;
if (httpRes) {
let jsonRes = {};
try {
res = JSON.parse(res);
console.log('JSON parsed.');
if (err || res.Status) {
jsonRes = JSON.parse(httpRes);
res = jsonRes;
if (httpErr || jsonRes.Status) {
const msg = ['Answer', 'Comment', 'Status']
.filter( (prop) => res[prop] )
.map( (prop) => prop + ': ' + JSON.stringify( res[prop] ) )
.filter((prop) => jsonRes[prop])
.map((prop) => `${prop}: ${JSON.stringify(jsonRes[prop])}`)
.join(', \n');
err = clarify(err || {}, 'Сервер (json): ' + msg, {data: res});
err = clarify(httpErr || {}, `Сервер (json): ${msg}`, { data: jsonRes });
} else {
res = res.Answer || [];
res = res.filter(
res = (jsonRes.Answer || []).filter(
(record) => types.includes(record.type)
).map( (ans) => ans.data );
).map((ans) => ans.data);
}
} catch(e) {
} catch (e) {
err = clarify(
e,
'Сервер (текст): ' + res, err ? {data: err} : undefined
`Сервер (текст): ${res}`,
httpErr ? { data: httpErr } : undefined
);
}
}
@ -148,7 +93,7 @@
Promise.all(promises).then(
([[v4err, v4res], [v6err, v6res]]) => {
if(v4err) {
if (v4err) {
return cb(v4err, v4res);
}
const ips = v4res;
@ -158,77 +103,101 @@
} else {
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 hostSet = new Set();
const ipToHostKey = '';
const privates = {
for( const addr of addrArr ) {
state: window.utils.createStorage('ip-to-host'),
const ipm = _test.ipv4v6(addr);
if (ipm.ifMatched) {
ipSet.add( ipm.canonical );
} else {
hostSet.add( addr.replace(/:\d+$/, '') );
}
createHostObj(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);
return (this.strToHostObj[hostStr] = { host: hostStr });
},
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() {
_state(ip2host, null);
reinit();
this.state(ipToHostKey, null);
this.reinit();
},
_purgeOldIpsForSync(hostStr) {
purgeOldIpsForSync(hostStr) {
console.log('Purging old IPs for', hostStr);
for(const ip of Object.keys(privates._ipToHostObj)) {
if (hostStr === privates._ipToHostObj[ip].host) {
delete privates._ipToHostObj[ip];
}
Object.keys(privates.ipToHostObj).forEach((ip) => {
if (hostStr === this.ipToHostObj[ip].host) {
delete this.ipToHostObj[ip];
}
});
},
_addAsync(hostStr, cb = mandatory()) {
addAsync(hostStr, cb = mandatory()) {
getIpsFor(hostStr, (err, ips, ...warns) => {
console.log('Got IPs + err?:', ips, err);
if (!err) {
this._purgeOldIpsForSync(hostStr);
this.purgeOldIpsForSync(hostStr);
// Object may be shared, string can't.
const hostObj = _getHostObj(hostStr);
for(const ip of ips) {
privates._ipToHostObj[ip] = hostObj;
}
const hostObj = privates.getHostObj(hostStr);
ips.forEach((ip) => {
privates.ipToHostObj[ip] = hostObj;
});
}
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);
const promises = hostArr.map(
(hostStr) => new Promise(
(resolve) => this._addAsync(
(resolve) => this.addAsync(
hostStr,
(...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;
const ifAllErrors = cbsRes.length === errors.length;
if (errors.length) {
@ -270,25 +239,71 @@
return cb(newError);
}
}
cb(null, null, newError);
return cb(null, null, newError);
});
},
_replaceAllAsync(hostArr = mandatory(), cb) {
replaceAllAsync(maybeHostArr = mandatory(), maybeCb) {
if (typeof(hostArr) === 'function') {
cb = hostArr;
hostArr = Object.keys(privates._strToHostObj);
let hostArr;
let cb;
if (typeof maybeHostArr === 'function') {
hostArr = Object.keys(this.strToHostObj);
cb = maybeHostArr;
} else {
hostArr = maybeHostArr;
cb = maybeCb;
}
this.resetToDefaults();
for(const hostStr of hostArr) {
_createHostObj(hostStr);
hostArr.forEach((hostStr) => this.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()) {
this._updateAllAsync((err, ...args) => {
privates.updateAllAsync((err, ...args) => {
if (!err) {
this.persistData();
@ -310,14 +325,15 @@
replaceAllAsync(addrArr, cb = mandatory()) {
console.log('Replacing...');
const [ipSet, hostSet] = _canonize(addrArr);
for( const ip of ipSet ) {
const host = _getHostObj(ip);
privates._ipToHostObj[ip] = host;
}
const [ipSet, hostSet] = canonize(addrArr);
ipSet.forEach((ip) => {
privates.ipToHostObj[ip] = privates.getHostObj(ip);
});
const hostArr = Array.from(hostSet);
this._replaceAllAsync(hostArr, (allErr, ...args) => {
privates.replaceAllAsync(hostArr, (allErr, ...args) => {
if (!allErr) {
this.persistData();
@ -330,7 +346,7 @@
get(ip) {
const tmp = privates._ipToHostObj[ip];
const tmp = privates.ipToHostObj[ip];
return tmp && tmp.host;
},
@ -338,14 +354,14 @@
};
window.utils.addRequestResponder(
'ip-to-host-update-all', (...args) => self.updateAllAsync(...args)
'ip-to-host-update-all', (...args) => theApi.updateAllAsync(...args)
);
window.utils.addRequestResponder(
'ip-to-host-replace-all', (...args) => self.replaceAllAsync(...args)
'ip-to-host-replace-all', (...args) => theApi.replaceAllAsync(...args)
);
window.utils.addRequestResponder(
'ip-to-host-reset-to-defaults', (cb) => {
self.resetToDefaults();
theApi.resetToDefaults();
cb();
}
);

View File

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

View File

@ -1,8 +1,5 @@
'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';
const commonContext = {
version: '0.21',
version: '0.22',
};
exports.contexts = {};