From a9eef144a9edb1403bd88cc6350d43ddd0711022 Mon Sep 17 00:00:00 2001 From: "Ilya Ig. Petrov" Date: Tue, 17 Jan 2017 15:00:53 +0000 Subject: [PATCH] Add incontinence/hunting mode for kitchen, amend utils and kitchen, refactor informer --- .../extension/00-init-apis.js | 169 ++++++++++++------ .../extension/11-api-error-handlers.js | 23 +-- .../extension/13-pac-kitchen.js | 106 +++++++---- ...-api-sync-pac-script-with-pac-provider.js} | 44 +---- .../extension/30-block-informer.js | 75 ++++---- .../pages/choose-pac-provider/index.html | 3 + .../pages/choose-pac-provider/index.js | 4 +- 7 files changed, 245 insertions(+), 179 deletions(-) rename extensions/chromium/runet-censorship-bypass/extension/{12-api-sync-pac-script-with-pac-provider.js => 15-api-sync-pac-script-with-pac-provider.js} (95%) diff --git a/extensions/chromium/runet-censorship-bypass/extension/00-init-apis.js b/extensions/chromium/runet-censorship-bypass/extension/00-init-apis.js index 445fd3d..6e55fee 100644 --- a/extensions/chromium/runet-censorship-bypass/extension/00-init-apis.js +++ b/extensions/chromium/runet-censorship-bypass/extension/00-init-apis.js @@ -1,74 +1,139 @@ 'use strict'; -const IF_DEBUG = false; +{ -if (!IF_DEBUG) { - // 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) { + const IF_DEBUG = false; - _meth(...args.map((a) => '' + a)); + if (!IF_DEBUG) { + // 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) { - }; - }); -} + _meth(...args.map((a) => '' + a)); + }; + }); + } -window.utils = { + const self = window.utils = { - mandatory() { + mandatory() { - throw new TypeError('Missing required argument. ' + - 'Be explicit if you swallow errors.'); + throw new TypeError('Missing required argument. ' + + 'Be explicit if you swallow errors.'); - }, + }, - getProp(obj, path = this.mandatory()) { + throwIfError(err) { - 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)) { - return undefined; + if(err) { + throw err; } - obj = obj[prop]; - } - return obj[lastProp]; - - }, - - areSettingsNotControlledFor(details) { - - return ['controlled_by_other', 'not_controllable'] - .some( (prefix) => details.levelOfControl.startsWith(prefix) ); - - }, - - messages: { - - searchSettingsForUrl(niddle) { - - return 'chrome://settings/search#' + (chrome.i18n.getMessage(niddle) || niddle); }, - whichExtensionHtml() { + checkChromeError(betterStack) { - return chrome.i18n.getMessage('noControl') + - ` - ${ chrome.i18n.getMessage('which') } - `; + // Chrome API calls your cb in a context different from the point of API + // method invokation. + const err = chrome.runtime.lastError || chrome.extension.lastError; + if (err) { + const args = ['API returned error:', err]; + if (betterStack) { + args.push('\n' + betterStack); + } + console.warn(...args); + } + return err; }, - }, + chromified(cb = self.mandatory(), ...replaceArgs) { -}; + const stack = (new Error()).stack; + // Take error first callback and convert it to chrome api callback. + return function(...args) { -window.apis = {}; + if (replaceArgs.length) { + args = replaceArgs; + } + const err = self.checkChromeError(stack); + // setTimeout fixes error context. + setTimeout( cb.bind(null, err, ...args), 0 ); + + }; + + }, + + getProp(obj, 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)) { + return undefined; + } + obj = obj[prop]; + } + return obj[lastProp]; + + }, + + createStorage(prefix) { + + return function state(key, value) { + + key = prefix + key; + if (value === null) { + return localStorage.removeItem(key); + } + if (value === undefined) { + const item = localStorage.getItem(key); + 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)); + + } + + }, + + areSettingsNotControlledFor(details) { + + return ['controlled_by_other', 'not_controllable'] + .some( (prefix) => details.levelOfControl.startsWith(prefix) ); + + }, + + messages: { + + searchSettingsForUrl(niddle) { + + return 'chrome://settings/search#' + (chrome.i18n.getMessage(niddle) || niddle); + + }, + + whichExtensionHtml() { + + return chrome.i18n.getMessage('noControl') + + ` + ${ chrome.i18n.getMessage('which') } + `; + + }, + + }, + + }; + + window.apis = {}; + +} diff --git a/extensions/chromium/runet-censorship-bypass/extension/11-api-error-handlers.js b/extensions/chromium/runet-censorship-bypass/extension/11-api-error-handlers.js index 42c0c4c..ab42735 100644 --- a/extensions/chromium/runet-censorship-bypass/extension/11-api-error-handlers.js +++ b/extensions/chromium/runet-censorship-bypass/extension/11-api-error-handlers.js @@ -47,23 +47,6 @@ } - const handlersState = function(key, value) { - - key = 'handlers-' + key; - if (value === null) { - return localStorage.removeItem(key); - } - if (value === undefined) { - const item = localStorage.getItem(key); - 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)); - - }; - const openAndFocus = (url) => { chrome.tabs.create( @@ -78,6 +61,8 @@ window.apis.errorHandlers = { + state: window.utils.createStorage('handlers-'), + viewErrorVoid(type = window.utils.mandatory(), err) { let errors = {}; @@ -113,14 +98,14 @@ for( const name of (eventName ? [eventName] : this.getEventsMap().keys() ) ) { - handlersState( ifPrefix + name, onOffStr === 'on' ? 'on' : null ); + this.state( ifPrefix + name, onOffStr === 'on' ? 'on' : null ); } }, isOn(eventName) { - return handlersState( ifPrefix + eventName); + return this.state( ifPrefix + eventName ); }, diff --git a/extensions/chromium/runet-censorship-bypass/extension/13-pac-kitchen.js b/extensions/chromium/runet-censorship-bypass/extension/13-pac-kitchen.js index bfa2f49..65415a3 100644 --- a/extensions/chromium/runet-censorship-bypass/extension/13-pac-kitchen.js +++ b/extensions/chromium/runet-censorship-bypass/extension/13-pac-kitchen.js @@ -2,21 +2,26 @@ { // Private namespace starts. - const kitchenStorageKey = 'pac-kitchen-kept-mods'; + const mandatory = window.utils.mandatory; + const throwIfError = window.utils.throwIfError; + const chromified = window.utils.chromified; + const kitchenStartsMark = '\n\n//%#@@@@@@ PAC_KITCHEN_STARTS @@@@@@#%'; + const kitchenState = window.utils.createStorage('pac-kitchen-'); + const ifIncontinence = 'if-incontinence'; const configs = { ifProxyHttpsUrlsOnly: { dflt: false, label: 'проксировать только HTTPS-сайты', - desc: 'Проксировать только сайты, доступные по шифрованному протоколу HTTPS. Прокси и провайдер смогут видеть только адреса посещаемых вами ресурсов, но не их содержимое.', + desc: 'Проксировать только сайты, доступные по шифрованному протоколу HTTPS. Прокси и провайдер смогут видеть только адреса проксируемых ресурсов, но не их содержимое.', index: 0, }, ifUseSecureProxiesOnly: { dflt: false, label: 'только шифрованная связь с прокси', - desc: 'Шифровать соединение до прокси от провайдера. Провайдер всё же сможет видеть адреса (но не содержимое) посещаемых вами ресурсов из протокола DNS.', + desc: 'Шифровать соединение до прокси от провайдера. Провайдер всё же сможет видеть адреса (но не содержимое) проксируемых ресурсов из протокола DNS.', index: 1, }, ifUsePacScriptProxies: { @@ -53,11 +58,11 @@ const getCurrentConfigs = function getCurrentConfigs() { - const json = localStorage.getItem(kitchenStorageKey); - if (!json) { + const mods = kitchenState('mods'); + if (!mods) { return null; } - return new PacModifiers(JSON.parse(json)); + return new PacModifiers(mods); }; @@ -122,7 +127,7 @@ getConfigs: getOrderedConfigsForUser, - cook(pacData, pacMods = window.utils.mandatory()) { + cook(pacData, pacMods = mandatory()) { return pacMods.ifNoMods ? pacData : pacData + `${ kitchenStartsMark } ;+function(global) { @@ -183,23 +188,20 @@ }, - keepCookedNow(pacMods = window.utils.mandatory(), cb) { + _tryNowAsync(details, cb = throwIfError) { - if (typeof(pacMods) === 'function') { - cb = pacMods; - const pacMods = getCurrentConfigs(); - if (!pacMods) { - return cb(TypeError('PAC mods were never initialized.')); - } - } else { - try { - pacMods = new PacModifiers(pacMods); - } catch(e) { - return cb(e); - } - localStorage.setItem(kitchenStorageKey, JSON.stringify(pacMods)); + if (typeof(details) === 'function') { + cb = details; + details = undefined; } - chrome.proxy.settings.get({}, (details) => { + + new Promise((resolve) => + + details + ? resolve(details) + : chrome.proxy.settings.get({}, resolve) + + ).then( (details) => { if ( details.levelOfControl === 'controlled_by_this_extension' @@ -211,28 +213,60 @@ new RegExp(kitchenStartsMark + '[\\s\\S]*$', 'g'), '' ); - return chrome.proxy.settings.set(details, cb); + return chrome.proxy.settings.set(details, chromified(cb)); } } - return cb( - null, - null, - [new TypeError('PAC-скрипт не обнаружен, но настройки будут активированы при установке PAC-скрипта.')] - ); + + kitchenState(ifIncontinence, true); + cb(new TypeError( + 'Не найдено активного PAC-скрипта! Изменения будут применены при возвращении контроля настроек прокси или установке нового PAC-скрипта.' + )); }); }, + checkIncontinence(details) { + + if ( kitchenState(ifIncontinence) ) { + this._tryNowAsync(details, () => {/* Swallow. */}); + } + + }, + + + keepCookedNowAsync(pacMods = mandatory(), cb = throwIfError) { + + if (typeof(pacMods) === 'function') { + cb = pacMods; + const pacMods = getCurrentConfigs(); + if (!pacMods) { + return cb(TypeError('PAC mods were never initialized and you haven\'t supplied any.')); + } + } else { + try { + pacMods = new PacModifiers(pacMods); + } catch(e) { + return cb(e); + } + kitchenState('mods', pacMods); + } + this._tryNowAsync( (err) => cb(null, null, err && [err]) ); + + }, + resetToDefaultsVoid() { - delete localStorage[kitchenStorageKey]; - this.keepCookedNow({}); + kitchenState('mods', null); + kitchenState(ifIncontinence, null); + this.keepCookedNowAsync({}); }, }; + const pacKitchen = window.apis.pacKitchen; + const originalSet = chrome.proxy.settings.set.bind( chrome.proxy.settings ); chrome.proxy.settings.set = function(details, cb) { @@ -243,10 +277,18 @@ } const pacMods = getCurrentConfigs(); if (pacMods) { - pac.data = window.apis.pacKitchen.cook( pac.data, pacMods ); + pac.data = pacKitchen.cook( pac.data, pacMods ); } - originalSet({ value: details.value }, cb); + originalSet({ value: details.value }, (/* No args. */) => { + + kitchenState(ifIncontinence, null); + cb && cb(); + + }); }; + pacKitchen.checkIncontinence(); + chrome.proxy.settings.onChange.addListener( pacKitchen.checkIncontinence.bind(pacKitchen) ); + } // Private namespace ends. diff --git a/extensions/chromium/runet-censorship-bypass/extension/12-api-sync-pac-script-with-pac-provider.js b/extensions/chromium/runet-censorship-bypass/extension/15-api-sync-pac-script-with-pac-provider.js similarity index 95% rename from extensions/chromium/runet-censorship-bypass/extension/12-api-sync-pac-script-with-pac-provider.js rename to extensions/chromium/runet-censorship-bypass/extension/15-api-sync-pac-script-with-pac-provider.js index fefc98a..a1beec4 100644 --- a/extensions/chromium/runet-censorship-bypass/extension/12-api-sync-pac-script-with-pac-provider.js +++ b/extensions/chromium/runet-censorship-bypass/extension/15-api-sync-pac-script-with-pac-provider.js @@ -22,14 +22,9 @@ { // Private namespace starts. const mandatory = window.utils.mandatory; - - const throwIfError = function throwIfError(err) { - - if(err) { - throw err; - } - - }; + const throwIfError = window.utils.throwIfError; + const chromified = window.utils.chromified; + const checkChromeError = window.utils.checkChromeError; const asyncLogGroup = function asyncLogGroup(...args) { @@ -48,39 +43,6 @@ }; - const checkChromeError = function checkChromeError(betterStack) { - - // Chrome API calls your cb in a context different from the point of API - // method invokation. - const err = chrome.runtime.lastError || chrome.extension.lastError; - if (err) { - const args = ['API returned error:', err]; - if (betterStack) { - args.push('\n' + betterStack); - } - console.warn(...args); - } - return err; - - }; - - const chromified = function chromified(cb = mandatory(), ...replaceArgs) { - - const stack = (new Error()).stack; - // Take error first callback and convert it to chrome api callback. - return function(...args) { - - if (replaceArgs.length) { - args = replaceArgs; - } - const err = checkChromeError(stack); - // setTimeout fixes error context. - setTimeout( cb.bind(null, err, ...args), 0 ); - - }; - - }; - class Clarification { constructor(message = mandatory(), prevClarification) { diff --git a/extensions/chromium/runet-censorship-bypass/extension/30-block-informer.js b/extensions/chromium/runet-censorship-bypass/extension/30-block-informer.js index 9369518..3f185e4 100644 --- a/extensions/chromium/runet-censorship-bypass/extension/30-block-informer.js +++ b/extensions/chromium/runet-censorship-bypass/extension/30-block-informer.js @@ -14,50 +14,38 @@ Crazy parallel Chrome. **/ -const antiCensorRu = window.apis.antiCensorRu; - -window.chrome.browserAction.setBadgeBackgroundColor({ - color: '#db4b2f', -}); - -window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! - { + window.chrome.browserAction.setBadgeBackgroundColor({ + color: '#db4b2f', + }); + const _tabCallbacks = {}; - function afterTabUpdated(tabId, cb) { - if (_tabCallbacks[tabId]) - _tabCallbacks[tabId].push(cb); - else _tabCallbacks[tabId] = [cb]; - } + const afterTabUpdated = function afterTabUpdated(tabId, cb) { + + if (_tabCallbacks[tabId]) { + _tabCallbacks[tabId].push(cb); + } else { + _tabCallbacks[tabId] = [cb]; + } + + }; + + const onTabUpdate = function onTabUpdate(tabId) { - function onTabUpdate(tabId) { if (_tabCallbacks[tabId]) { _tabCallbacks[tabId].map( (f) => f() ); delete _tabCallbacks[tabId]; } - } + + }; chrome.tabs.onUpdated.addListener( onTabUpdate ); - function isInsideTabWithIp(requestDetails) { - return requestDetails.tabId !== -1 && requestDetails.ip; - } + const antiCensorRu = window.apis.antiCensorRu; - chrome.webRequest.onErrorOccurred.addListener( - (requestDetails) => - isInsideTabWithIp(requestDetails) - && isProxiedAndInformed(requestDetails), - {urls: ['']} - ); - - chrome.tabs.onRemoved.addListener( (tabId) => { - onTabUpdate(tabId); - delete window.tabWithError2ip[tabId]; - }); - - function updateTitle(requestDetails, cb) { + const updateTitle = function updateTitle(requestDetails, cb) { chrome.browserAction.getTitle( {tabId: requestDetails.tabId}, @@ -74,6 +62,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! const proxyTitle = 'Прокси:'; if (!ifTitleSetAlready) { + title = 'Разблокированы:\n' + indent + hostname + '\n' + proxyTitle + '\n' + indent + proxyHost; ifShouldUpdateTitle = true; @@ -84,6 +73,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! }); } else { + const hostsProxiesPair = title.split(proxyTitle); if (hostsProxiesPair[1].indexOf(proxyHost) === -1) { @@ -95,6 +85,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! } if (hostsProxiesPair[0].indexOf(hostname) === -1) { + title = title.replace( proxyTitle, indent + hostname + '\n' + proxyTitle @@ -119,6 +110,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! ); } + } if (ifShouldUpdateTitle) { @@ -132,11 +124,12 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! } ); - } + + }; let previousUpdateTitleFinished = Promise.resolve(); - function isProxiedAndInformed(requestDetails) { + const isProxiedAndInformed = function isProxiedAndInformed(requestDetails) { if ( !(requestDetails.ip && antiCensorRu.isProxied( requestDetails.ip )) ) { @@ -156,7 +149,14 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! ); return true; - } + + }; + + const isInsideTabWithIp = function isInsideTabWithIp(requestDetails) { + + return requestDetails.tabId !== -1 && requestDetails.ip; + + }; chrome.webRequest.onResponseStarted.addListener( (requestDetails) => isInsideTabWithIp(requestDetails) @@ -164,4 +164,11 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! {urls: ['']} ); + chrome.webRequest.onErrorOccurred.addListener( + (requestDetails) => + isInsideTabWithIp(requestDetails) + && isProxiedAndInformed(requestDetails), + {urls: ['']} + ); + } diff --git a/extensions/chromium/runet-censorship-bypass/extension/pages/choose-pac-provider/index.html b/extensions/chromium/runet-censorship-bypass/extension/pages/choose-pac-provider/index.html index 81bfe34..24663cf 100755 --- a/extensions/chromium/runet-censorship-bypass/extension/pages/choose-pac-provider/index.html +++ b/extensions/chromium/runet-censorship-bypass/extension/pages/choose-pac-provider/index.html @@ -48,6 +48,9 @@ margin: 0.6em 0; padding: 0; } + #none:checked + label { + color: red; + } #configs-panel > header { margin: 0 0 0.4em; } diff --git a/extensions/chromium/runet-censorship-bypass/extension/pages/choose-pac-provider/index.js b/extensions/chromium/runet-censorship-bypass/extension/pages/choose-pac-provider/index.js index 044e86d..e94bc25 100755 --- a/extensions/chromium/runet-censorship-bypass/extension/pages/choose-pac-provider/index.js +++ b/extensions/chromium/runet-censorship-bypass/extension/pages/choose-pac-provider/index.js @@ -79,10 +79,11 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => const showErrors = (err, warns) => { warns = warns || []; + backgroundPage.console.log('eeeEEEEE',warns) const warning = warns .map( (w) => '✘ ' + - (w.clarification && w.clarification.message || w.message || '') + (w && w.clarification && w.clarification.message || w.message || '') ) .join('
'); @@ -241,6 +242,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => } else { li.innerHTML += `🛈