From 9ef8939127d1faa618865119d18bc8a65788dc24 Mon Sep 17 00:00:00 2001 From: "Ilya Ig. Petrov" Date: Thu, 24 Nov 2016 08:40:57 -0800 Subject: [PATCH] Handle update errors in popup, add ip lookup method, fix rollbacks (change API) --- .../1-sync-pac-script-with-pac-provider.js | 113 ++++++++------ .../extension/2-block-informer.js | 143 +++++++++-------- .../pages/choose-pac-provider/index.html | 6 +- .../pages/choose-pac-provider/index.js | 146 ++++++++++-------- 4 files changed, 228 insertions(+), 180 deletions(-) diff --git a/extensions/chromium/minimalistic-pac-setter/extension/1-sync-pac-script-with-pac-provider.js b/extensions/chromium/minimalistic-pac-setter/extension/1-sync-pac-script-with-pac-provider.js index 7983c9f..15a1916 100755 --- a/extensions/chromium/minimalistic-pac-setter/extension/1-sync-pac-script-with-pac-provider.js +++ b/extensions/chromium/minimalistic-pac-setter/extension/1-sync-pac-script-with-pac-provider.js @@ -49,15 +49,41 @@ _currentPacProviderKey: 'Оба_и_на_свитчах', + isProxied(ip) { + + // Executed on each request with ip. Make it as fast as possible. + return this._currentPacProviderKey && this.pacProviders[this._currentPacProviderKey].proxyIps.hasOwnProperty(ip); + // The benefit of removing lookups is little, e.g. this._currentProxyIps && this._currentProxyIps.hasOwnProperty(ip); + + }, + + mustBeKey(key) { + + if ( !(key === null || this.pacProviders[key]) ) { + throw new IllegalArgumentException('No provider for key:' + key); + } + + }, + get currentPacProviderKey() { return this._currentPacProviderKey }, set currentPacProviderKey(newKey) { - if (newKey && !this.pacProviders[newKey]) - throw new IllegalArgumentException('No provider for key:' + newKey); + this.mustBeKey(newKey); this._currentPacProviderKey = newKey; + }, - get pacProvider() { return this.pacProviders[this.currentPacProviderKey] }, + getPacProvider(key) { + + if(key) { + this.mustBeKey(key); + } + else { + key = this.currentPacProviderKey; + } + return this.pacProviders[key]; + + }, /* Is it the first time extension installed? Do something, e.g. initiate PAC sync. @@ -106,19 +132,28 @@ }); }, - syncWithPacProvider(cb) { + syncWithPacProvider(key, cb) { - cb = asyncLogGroup('Syncing with PAC provider...', cb); - if (!this.pacProvider) { + if( !key || typeof(key) === 'function' ) { + cb = key; + key = this.currentPacProviderKey; + } + cb = asyncLogGroup('Syncing with PAC provider ' + key + '...', cb); + + if (key === null) { + // No pac provider set. return cb({clarification:{message:'Сперва выберите PAC-провайдера.'}}); } + const pacProvider = this.getPacProvider(key); + const pacSetPromise = new Promise( (resolve, reject) => setPacScriptFromProvider( - this.pacProvider, + pacProvider, (err, res) => { if (!err) { + this.currentPacProviderKey = key; this.lastPacUpdateStamp = Date.now(); this.ifFirstInstall = false; this.setAlarms(); @@ -132,7 +167,7 @@ const ipsPromise = new Promise( (resolve, reject) => updatePacProxyIps( - this.pacProvider, + pacProvider, (ipsError) => { if (ipsError && ipsError.clarification) { @@ -186,28 +221,15 @@ installPac(key, cb) { - console.log('Installing PAC'); - if(typeof(key) === 'function') { - cb = key; - key = undefined; + console.log('Installing PAC...'); + if (!key) { + throw new Error('Key must be defined.'); } - - const oldKey = this.currentPacProviderKey; - if(key || key !== oldKey) { - this.currentPacProviderKey = key; - const _cb = cb; - cb = (err, res) => { - - if (err && !(err.clarification && err.clarification.ifNotCritical)) { - console.log('Rollback privider key.'); - this.currentPacProviderKey = oldKey; - } - _cb(err, res); - - }; + if (this.currentProviderKey !== key) { + return this.syncWithPacProvider(key, cb); } - - this.syncWithPacProvider(cb); + console.log(key + ' already installed.'); + cb(); }, @@ -223,7 +245,7 @@ if (err) { return cb(err); } - this.currentPacProviderKey = undefined; + this.currentPacProviderKey = null; this.pushToStorage(cb); } @@ -236,16 +258,12 @@ // ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE chrome.storage.local.get(null, (oldStorage) => { - console.log('Init on storage:', oldStorage); - antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0; - - if (!antiCensorRu.ifFirstInstall) { - // LAUNCH, RELOAD, UPDATE - antiCensorRu._currentPacProviderKey = oldStorage._currentPacProviderKey; - antiCensorRu.lastPacUpdateStamp = oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp; - console.log( 'Last PAC update was on', new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU') ); - } - + checkChromeError(); + /* + Event handlers that ALWAYS work (even if installation is not done or failed). + E.g. install window may fail to open or be closed by user accidentally. + In such case extension _should_ try to work on default parameters. + */ chrome.alarms.onAlarm.addListener( (alarm) => { @@ -265,19 +283,28 @@ }); - if (antiCensorRu.ifFirstInstall) { + console.log('Storage on init:', oldStorage); + antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0; + + if (!antiCensorRu.ifFirstInstall) { + // LAUNCH, RELOAD, UPDATE + antiCensorRu._currentPacProviderKey = oldStorage._currentPacProviderKey || null; // Old or migrate. + antiCensorRu.lastPacUpdateStamp = oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp; // Old or migrate to default. + console.log( 'Last PAC update was on', new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU') ); + } + else { // INSTALL console.log('Installing...'); - chrome.runtime.openOptionsPage(); + return chrome.runtime.openOptionsPage(); } - if (!antiCensorRu.pacProvider) { - return console.log('No PAC provider set. Do nothing.'); + if (!antiCensorRu.getPacProvider()) { /* In case of UPDATE: 1. new providers will still be shown. 2. new version won't be pushed to storage */ + return console.log('No PAC provider set. Do nothing.'); } /* diff --git a/extensions/chromium/minimalistic-pac-setter/extension/2-block-informer.js b/extensions/chromium/minimalistic-pac-setter/extension/2-block-informer.js index 44c44c2..965cd03 100755 --- a/extensions/chromium/minimalistic-pac-setter/extension/2-block-informer.js +++ b/extensions/chromium/minimalistic-pac-setter/extension/2-block-informer.js @@ -52,13 +52,81 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! chrome.tabs.onRemoved.addListener( (tabId) => { onTabUpdate(tabId); delete window.tabWithError2ip[tabId] } ); + function updateTitle(requestDetails, cb) { + + chrome.browserAction.getTitle( + { tabId: requestDetails.tabId }, + (title) => { + + const ifTitleSetAlready = /\n/.test(title); + const proxyHost = window.antiCensorRu.getPacProvider().proxyIps[ requestDetails.ip ]; + + const hostname = new URL( requestDetails.url ).hostname; + + let ifShouldUpdateTitle = false; + const indent = ' '; + const proxyTitle = 'Прокси:'; + + if (!ifTitleSetAlready) { + title = 'Разблокированы:\n'+ indent + hostname +'\n'+ proxyTitle +'\n'+ indent + proxyHost; + ifShouldUpdateTitle = true; + + chrome.browserAction.setBadgeText({ + tabId: requestDetails.tabId, + text: requestDetails.type === 'main_frame' ? '1' : '%1' + }); + + } + else { + const hostsProxiesPair = title.split(proxyTitle); + + if (hostsProxiesPair[1].indexOf(proxyHost) === -1) { + title = title.replace(hostsProxiesPair[1], hostsProxiesPair[1] +'\n'+ indent + proxyHost); + ifShouldUpdateTitle = true; + } + + if (hostsProxiesPair[0].indexOf(hostname) === -1) { + title = title.replace(proxyTitle, indent + hostname +'\n'+ proxyTitle); + ifShouldUpdateTitle = true; + + const _cb = cb; + cb = () => chrome.browserAction.getBadgeText( + {tabId: requestDetails.tabId}, + (result) => { + + chrome.browserAction.setBadgeText( + { + tabId: requestDetails.tabId, + text: ( isNaN( result.charAt(0) ) && result.charAt(0) || '' ) + (hostsProxiesPair[0].split('\n').length - 1) + } + ); + return _cb(); + + } + ); + + } + } + + if (ifShouldUpdateTitle) { + chrome.browserAction.setTitle({ + title: title, + tabId: requestDetails.tabId + }); + } + + return cb(); + + } + ); + } + + let previousUpdateTitleFinished = Promise.resolve(); function isProxiedAndInformed(requestDetails) { - if ( - !( window.antiCensorRu.pacProvider && window.antiCensorRu.pacProvider.proxyIps && window.antiCensorRu.pacProvider.proxyIps[ requestDetails.ip ] ) - ) { + if ( !(requestDetails.ip && antiCensorRu.isProxied( requestDetails.ip )) ) { return false; } @@ -74,75 +142,6 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! ); return true; - - function updateTitle(requestDetails, cb) { - - chrome.browserAction.getTitle( - { tabId: requestDetails.tabId }, - (title) => { - - const ifTitleSetAlready = /\n/.test(title); - const proxyHost = window.antiCensorRu.pacProvider.proxyIps[ requestDetails.ip ]; - - const hostname = new URL( requestDetails.url ).hostname; - - let ifShouldUpdateTitle = false; - const indent = ' '; - const proxyTitle = 'Прокси:'; - - if (!ifTitleSetAlready) { - title = 'Разблокированы:\n'+ indent + hostname +'\n'+ proxyTitle +'\n'+ indent + proxyHost; - ifShouldUpdateTitle = true; - - chrome.browserAction.setBadgeText({ - tabId: requestDetails.tabId, - text: ifMainFrame ? '1' : '%1' - }); - - } - else { - const hostsProxiesPair = title.split(proxyTitle); - - if (hostsProxiesPair[1].indexOf(proxyHost) === -1) { - title = title.replace(hostsProxiesPair[1], hostsProxiesPair[1] +'\n'+ indent + proxyHost); - ifShouldUpdateTitle = true; - } - - if (hostsProxiesPair[0].indexOf(hostname) === -1) { - title = title.replace(proxyTitle, indent + hostname +'\n'+ proxyTitle); - ifShouldUpdateTitle = true; - - const _cb = cb; - cb = () => chrome.browserAction.getBadgeText( - {tabId: requestDetails.tabId}, - (result) => { - - chrome.browserAction.setBadgeText( - { - tabId: requestDetails.tabId, - text: ( isNaN( result.charAt(0) ) && result.charAt(0) || '' ) + (hostsProxiesPair[0].split('\n').length - 1) - } - ); - return _cb(); - - } - ); - - } - } - - if (ifShouldUpdateTitle) { - chrome.browserAction.setTitle({ - title: title, - tabId: requestDetails.tabId - }); - } - - return cb(); - - } - ); - } } chrome.webRequest.onResponseStarted.addListener( diff --git a/extensions/chromium/minimalistic-pac-setter/extension/pages/choose-pac-provider/index.html b/extensions/chromium/minimalistic-pac-setter/extension/pages/choose-pac-provider/index.html index 30118fa..f2bd9f2 100755 --- a/extensions/chromium/minimalistic-pac-setter/extension/pages/choose-pac-provider/index.html +++ b/extensions/chromium/minimalistic-pac-setter/extension/pages/choose-pac-provider/index.html @@ -9,9 +9,10 @@ margin: 0; margin-bottom: 1em; } - li { + li, footer { display: block; white-space: nowrap; + word-break: keep-all; } li > * { vertical-align: middle; @@ -49,8 +50,7 @@
Загрузка...
diff --git a/extensions/chromium/minimalistic-pac-setter/extension/pages/choose-pac-provider/index.js b/extensions/chromium/minimalistic-pac-setter/extension/pages/choose-pac-provider/index.js index c447bcf..4e99409 100755 --- a/extensions/chromium/minimalistic-pac-setter/extension/pages/choose-pac-provider/index.js +++ b/extensions/chromium/minimalistic-pac-setter/extension/pages/choose-pac-provider/index.js @@ -61,92 +61,114 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => { // RADIOS - const currentRadio = () => { + const currentProviderRadio = () => { const id = antiCensorRu.currentPacProviderKey || 'none'; return document.querySelector('#'+id); } - const checkChosenProvider = () => currentRadio().checked = true; - const triggerChosenProvider = () => currentRadio().click(); + const checkChosenProvider = () => currentProviderRadio().checked = true; - const ul = document.querySelector('#list-of-providers'); - const _firstChild = ul.firstChild; - for( const providerKey of Object.keys(antiCensorRu.pacProviders).sort() ) { - const li = document.createElement('li'); - li.innerHTML = ' [обновить]'; - li.querySelector('.link-button').onclick = () => { triggerChosenProvider(); return false; }; - ul.insertBefore( li, _firstChild ); - } + const showError = (err) => { - const radios = [].slice.apply( document.querySelectorAll('[name=pacProvider]') ); - for(const radio of radios) { - radio.onclick = function(event) { + let clarification = err.clarification; + const ifNotCritical = clarification && clarification.ifNotCritical; + let message = err.message || ''; - const pacKey = event.target.id; - if (pacKey === 'none') { - return antiCensorRu.clearPac(); - } - - const enableDisableInputs = function () { - - const inputs = document.querySelectorAll('input'); - for ( let i = 0; i < inputs.length; i++ ) { - inputs[i].disabled = !inputs[i].disabled; - } - - } - - enableDisableInputs(); - setStatusTo('Установка...'); - antiCensorRu.installPac(pacKey, (err) => { - - backgroundPage.console.log('Popup callback...'); - if (!err) { - setStatusTo('PAC-скрипт установлен.'); - checkChosenProvider(); - } - else { - const ifNotCritical = err.clarification && err.clarification.ifNotCritical; - - let message = ''; - let clarification = err.clarification; - do { - message = message +' '+ (clarification && clarification.message || err.message || ''); - clarification = clarification && clarification.prev; - } while( clarification ); - message = message.trim(); - setStatusTo( + while( clarification ) { + message = (clarification && (clarification.message + ' ')) + message; + clarification = clarification.prev; + } + message = message.trim(); + setStatusTo( `${ifNotCritical ? 'Некритичная ошибка.' : 'Ошибка!'}
${message} -
[Ещё подробнее]` - ); - getStatus().querySelector('.link-button').onclick = function() { + ); + getStatus().querySelector('.link-button').onclick = function() { - const div = document.createElement('div'); - div.innerHTML = ` + const div = document.createElement('div'); + div.innerHTML = ` Более подробную информацию можно узнать из логов фоновой страницы:
chrome://extensions › Это расширение › Отладка страниц: фоновая страница › Console (DevTools)
Ещё: ${JSON.stringify({err: err, stack: err.stack})} `; - getStatus().replaceChild(div, this); - return false; - - }; - } - enableDisableInputs(); - }); + getStatus().replaceChild(div, this); return false; + + }; + + }; + + const enableDisableInputs = function () { + + const inputs = document.querySelectorAll('input'); + for ( let i = 0; i < inputs.length; i++ ) { + inputs[i].disabled = !inputs[i].disabled; } + + }; + + const conduct = (beforeStatus, operation, afterStatus, onSuccess) => { + + setStatusTo(beforeStatus); + enableDisableInputs(); + operation((err) => { + if (err) { + showError(err); + } + else { + setStatusTo(afterStatus); + onSuccess && onSuccess(); + } + enableDisableInputs(); + }) + + }; + + const ul = document.querySelector('#list-of-providers'); + const _firstChild = ul.firstChild; + for( const providerKey of Object.keys(antiCensorRu.pacProviders).sort() ) { + const li = document.createElement('li'); + li.innerHTML = ` [обновить]`; + li.querySelector('.link-button').onclick = () => { conduct( 'Обновляем...', (cb) => antiCensorRu.syncWithPacProvider(cb), 'Обновлено.' ); return false; }; + ul.insertBefore( li, _firstChild ); + } + checkChosenProvider(); + + const radios = [].slice.apply( document.querySelectorAll('[name=pacProvider]') ); + for(const radio of radios) { + radio.onclick = function(event) { + + if (event.target.id === (antiCensorRu.currentPacProviderKey || 'none')) { + return false; + } + const pacKey = event.target.id; + if (pacKey === 'none') { + conduct( + 'Отключение...', + (cb) => antiCensorRu.clearPac(cb), + 'Отключено.', + checkChosenProvider + ); + } + else { + conduct( + 'Установка...', + (cb) => antiCensorRu.installPac(pacKey, cb), + 'PAC-скрипт установлен.', + checkChosenProvider + ); + } + return false; + }; } setStatusTo(''); - checkChosenProvider(); if (antiCensorRu.ifFirstInstall) { - triggerChosenProvider(); + currentProviderRadio().click(); } });