Refactor err handlers, unixify newlines, restructure files

This commit is contained in:
Ilya Ig. Petrov 2016-12-01 07:01:20 -08:00
parent 257495168c
commit 6750872ea5
11 changed files with 357 additions and 206 deletions

View File

@ -1,26 +1,30 @@
# О расширении
Обход интернет-цензуры в России пока что не является преступлением.
Расширение позволяет обходить блокировки РосКомНадзора, давая вам доступ
к библиотекам, энциклопедиям, сайтам оппозиционеров, а также к неповинным
сайтам, случайно заблокированным в силу разных причин.
Проксирует только заблокированные сайты, оставляя нетронутыми все остальные.
Устанавливает PAC-скрипт, работающий через сервера anticenz.org и antizapret.prostovpn.org.
Обновляет PAC-скрипт каждые 4 часа, что составляет примерно 7MB трафика в сутки.
Также расширение постоянно потребляет ~15MB памяти для информирования о блокировках через иконку.
Синяя лента кампания фонда EFF в защиту свобод слова, прессы и союзов.
Если расширение не работает: https://git.io/vgDDj
Антицензура на Реддите: https://www.reddit.com/r/anticensorship_russia
Группа в G+: https://goo.gl/Lh0Cjh
История изменений: https://github.com/ilyaigpetrov/anti-censorship-russia/releases
# Дополнительно
Иконка синей ленты: http://www.iconsdb.com/icon-sets/cardboard-blue-icons/ribbon-12-icon.html
# Dev
Linting JS: `npm run lint`
# О расширении
Обход интернет-цензуры в России пока что не является преступлением.
Расширение позволяет обходить блокировки РосКомНадзора, давая вам доступ
к библиотекам, энциклопедиям, сайтам оппозиционеров, а также к неповинным
сайтам, случайно заблокированным в силу разных причин.
Проксирует только заблокированные сайты, оставляя нетронутыми все остальные.
Устанавливает PAC-скрипт, работающий через сервера anticenz.org и antizapret.prostovpn.org.
Обновляет PAC-скрипт каждые 4 часа, что составляет примерно 7MB трафика в сутки.
Также расширение постоянно потребляет ~15MB памяти для информирования о блокировках через иконку.
Синяя лента кампания фонда EFF в защиту свобод слова, прессы и союзов.
Если расширение не работает: https://git.io/vgDDj
Антицензура на Реддите: https://www.reddit.com/r/anticensorship_russia
Группа в G+: https://goo.gl/Lh0Cjh
История изменений: https://github.com/ilyaigpetrov/anti-censorship-russia/releases
# Дополнительно
Иконка синей ленты: http://www.iconsdb.com/icon-sets/cardboard-blue-icons/ribbon-12-icon.html

View File

@ -1,56 +0,0 @@
'use strict';
{
const extName = chrome.runtime.getManifest().name;
const notify = (
id,
title,
message,
icon = 'default-128.png',
context = extName
) => chrome.notifications.create(
id,
{
title: title,
message: message,
contextMessage: context,
requireInteraction: true,
type: 'basic',
iconUrl: './icons/' + icon,
}
);
window.addEventListener('error', (err) => {
console.warn('Global error');
notify('Unhandled error', 'Unhandled error', JSON.stringify(err),
'ext-error-128.png');
});
window.addEventListener('unhandledrejection', (event) => {
console.warn('Unhandled rejection. Throwing error.');
event.preventDefault();
throw event.reason;
});
chrome.proxy.onProxyError.addListener((details) => {
console.warn('PAC ERROR:', details);
notify('pac-error', ' PAC !', JSON.stringify(details),
'pac-error-128.png' );
});
chrome.proxy.settings.onChange.addListener((details) => {
console.log('Proxy settings changed.', details);
// const ifOther = details.levelOfControl.startsWith('controlled_by_other');
notify('Proxy change', 'Proxy changed', JSON.stringify(details),
'no-control-128.png');
});
}

View File

@ -0,0 +1,14 @@
'use strict';
window.utils = {
areSettingsNotControlledFor(details) {
return ['controlled_by_other', 'not_controllable']
.some( (prefix) => details.levelOfControl.startsWith(prefix) );
},
};
window.apis = {};

View File

@ -0,0 +1,148 @@
'use strict';
{ // Private namespace
const handlersState = function(key, value) {
console.log(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 ifPrefix = 'if-on-';
window.apis.errorHandlers = {
getEventsMap() {
return new Map([
['pac-error', 'ошибки PAC скриптов'],
['ext-error', 'ошибки расширения'],
['no-control', 'утеря контроля над настройками'],
]);
},
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() )
) {
handlersState( ifPrefix + name, onOffStr === 'on' ? 'on' : null );
}
},
isOn(eventName) {
return handlersState( ifPrefix + eventName);
},
ifNotControlled: null,
isNotControlled(details) {
this.ifNotControlled = window.utils.areSettingsNotControlledFor( details );
if (this.ifNotControlled) {
chrome.browserAction.disable();
} else {
chrome.browserAction.enable();
}
return this.ifNotControlled;
},
};
}
// INIT
chrome.proxy.settings.get(
{},
(details) => window.apis.errorHandlers.isNotControlled(details)
);
{
const extName = chrome.runtime.getManifest().name;
const mayNotify = function(
id, title, message,
icon = 'default-128.png',
context = extName
) {
if ( !window.apis.errorHandlers.isOn(id) ) {
return;
}
chrome.notifications.create(
id,
{
title: title,
message: message,
contextMessage: context,
requireInteraction: true,
type: 'basic',
iconUrl: './icons/' + icon,
isClickable: true,
}
);
};
window.addEventListener('error', (err) => {
console.warn('GLOBAL ERROR', err);
mayNotify('ext-error', 'Unhandled error', JSON.stringify(err),
'ext-error-128.png');
});
window.addEventListener('unhandledrejection', (event) => {
console.warn('Unhandled rejection. Throwing error.');
event.preventDefault();
throw event.reason;
});
chrome.proxy.onProxyError.addListener((details) => {
if (window.apis.errorHandlers.ifNoControl) {
return;
}
console.warn('PAC ERROR', details);
mayNotify('pac-error', ' PAC !', JSON.stringify(details),
'pac-error-128.png' );
});
chrome.proxy.settings.onChange.addListener((details) => {
console.log('Proxy settings changed.', details);
const noCon = 'no-control';
if ( window.apis.errorHandlers.isNotControlled(details) ) {
mayNotify(noCon, 'Proxy changed', JSON.stringify(details),
'no-control-128.png');
} else {
chrome.notifications.clear( noCon );
}
});
}

View File

@ -9,39 +9,17 @@
*/
/*
In background scripts use window.antiCensorRu public variables.
In pages window.antiCensorRu is not accessible,
In background scripts use window.apis.antiCensorRu public variables.
In pages window.apis.antiCensorRu is not accessible,
use chrome.runtime.getBackgroundPage(..),
extension.getBackgroundPage is deprecated
*/
{ // Private namespace starts.
window.antiCensorRu = {
window.apis.antiCensorRu = {
version: chrome.runtime.getManifest().version,
fixErrorsContext() {
/* `setTimeout` changes context of execution from other window
(e.g. popup) to background window, so we may catch errors
in bg error handlers.
More: https://bugs.chromium.org/p/chromium/issues/detail?id=357568
*/
for(const prop of Object.keys(this)) {
if ( typeof(this[prop]) === 'function' ) {
const method = this[prop];
this[prop] = function(...args) {
setTimeout(method.bind(this, ...args), 0);
};
}
}
},
throw() {
throw new Error('Artificial error');
},
pacProviders: {
Антизапрет: {
pacUrl: 'https://antizapret.prostovpn.org/proxy.pac',
@ -293,6 +271,7 @@
}
)
);
},
};
@ -307,8 +286,7 @@
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.
*/
const antiCensorRu = window.antiCensorRu;
antiCensorRu.fixErrorsContext();
const antiCensorRu = window.apis.antiCensorRu;
chrome.alarms.onAlarm.addListener(
(alarm) => {
@ -393,6 +371,7 @@
* Changed storage.ifNotInstalled to storage.ifFirstInstall
* Added storage.lastPacUpdateStamp
**/
});
function asyncLogGroup(...args) {
@ -406,6 +385,7 @@
cb(...cbArgs);
};
}
function checkChromeError(betterStack) {
@ -458,8 +438,7 @@
}
chrome.proxy.settings.get({}, (details) => {
const ifThis = details.levelOfControl.startsWith('controlled_by_this');
if (!ifThis) {
if ( window.utils.areSettingsNotControlledFor( details ) ) {
console.warn('Failed, other extension is in control.');
return cb({clarification: {message: 'Настройки прокси контролирует другое расширение. <a href="chrome://settings/search#proxy">Какое?</a>'}});
}
@ -500,6 +479,7 @@
}
);
}
function getIpsFor(host, cb) {
@ -600,6 +580,7 @@
}
)
);
}
function setPacScriptFromProvider(provider, cb) {
@ -625,6 +606,7 @@
}
);
}
}

View File

@ -0,0 +1,23 @@
'use strict';
/* `setTimeout` changes context of execution from other window
(e.g. popup) to background window, so we may catch errors
in bg error handlers.
More: https://bugs.chromium.org/p/chromium/issues/detail?id=357568
*/
// Fix error context of methods of all APIs.
for(const api of Object.keys(window.apis)) {
for(const prop of Object.keys(api)) {
if ( typeof(api[prop]) !== 'function' ) {
continue;
}
const method = api[prop];
api[prop] = function(...args) {
setTimeout(method.bind(this, ...args), 0);
};
}
}

View File

@ -14,6 +14,8 @@
Crazy parallel Chrome.
**/
const antiCensorRu = window.apis.antiCensorRu;
window.chrome.browserAction.setBadgeBackgroundColor({
color: '#db4b2f',
});
@ -66,7 +68,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
(title) => {
const ifTitleSetAlready = /\n/.test(title);
const proxyHost = window.antiCensorRu.getPacProvider()
const proxyHost = antiCensorRu.getPacProvider()
.proxyIps[requestDetails.ip];
const hostname = new URL( requestDetails.url ).hostname;
@ -141,7 +143,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
function isProxiedAndInformed(requestDetails) {
if ( !(requestDetails.ip
&& window.antiCensorRu.isProxied( requestDetails.ip )) ) {
&& antiCensorRu.isProxied( requestDetails.ip )) ) {
return false;
}

View File

@ -1,34 +1,34 @@
{
"manifest_version": 2,
"name": "Обход блокировок Рунета 0.15",
"description": "Аргументы против цензуры: https://git.io/vEkI9",
"version": "0.0.0.15",
"icons": {
"128": "/icons/default-128.png"
},
"author": "ilyaigpetrov@gmail.com",
"homepage_url": "https://github.com/anticensorship-russia/chromium-extension",
"permissions": [
"proxy",
"webRequest",
"alarms",
"storage",
"<all_urls>",
"tabs",
"contextMenus",
"notifications"
],
"background": {
"scripts": ["0-error-handlers.js", "1-sync-pac-script-with-pac-provider.js", "2-block-informer.js", "3-context-menus.js"]
},
"browser_action": {
"default_title": "Этот сайт благословлён",
"default_popup": "/pages/choose-pac-provider/index.html"
},
"options_ui": {
"page": "/pages/choose-pac-provider/index.html",
"chrome_style": true
}
}
{
"manifest_version": 2,
"name": "Обход блокировок Рунета 0.15",
"description": "Аргументы против цензуры: https://git.io/vEkI9",
"version": "0.0.0.15",
"icons": {
"128": "/icons/default-128.png"
},
"author": "ilyaigpetrov@gmail.com",
"homepage_url": "https://github.com/anticensorship-russia/chromium-extension",
"permissions": [
"proxy",
"webRequest",
"alarms",
"storage",
"<all_urls>",
"tabs",
"contextMenus",
"notifications"
],
"background": {
"scripts": ["00-init-apis.js", "11-api-error-handlers.js", "12-api-sync-pac-script-with-pac-provider.js", "20-api-fixes.js", "30-block-informer.js", "40-context-menus.js"]
},
"browser_action": {
"default_title": "Этот сайт благословлён",
"default_popup": "/pages/choose-pac-provider/index.html"
},
"options_ui": {
"page": "/pages/choose-pac-provider/index.html",
"chrome_style": true
}
}

View File

@ -1,58 +1,73 @@
<!DOCTYPE html>
<html>
<head>
<title>Выбор провайдера PAC</title>
<style>
ul {
list-style-type: none;
padding: 0;
margin: 0;
margin-bottom: 1em;
}
li, footer {
display: block;
white-space: nowrap;
word-break: keep-all;
}
li > * {
vertical-align: middle;
}
input[type="radio"], label {
cursor: pointer;
}
.off {
display: none;
}
.link-button, .link-button:visited {
color: #0000EE;
text-decoration: none;
}
.link-button:hover {
text-decoration: underline;
}
.checked-radio-panel {
visibility: hidden;
}
input:checked ~ .checked-radio-panel {
visibility: visible;
}
footer {
margin: 2em 1em 1em;
}
</style>
</head>
<body>
<ul id="list-of-providers">
<li><input type="radio" name="pacProvider" id="none" checked> <label for="none">Отключить</label></li>
</ul>
<div style="white-space: nowrap">
Обновлялись: <span class="update-date">...</span>
</div>
<div id="status">Загрузка...</div>
<footer>
<input type="button" value="Готово" class="close-button">&nbsp;<a href="../debug/index.html" style="text-decoration: none; margin-left: 1em;">Отладка</a>
</footer>
<script src="./index.js"></script>
<script src="./keep-links-clickable.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Выбор провайдера PAC</title>
<style>
label {
user-select: none;
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
margin-bottom: 1em;
}
li, footer {
display: block;
white-space: nowrap;
word-break: keep-all;
}
li > * {
vertical-align: middle;
}
input[type="radio"], label {
cursor: pointer;
}
.off {
display: none;
}
.link-button, .link-button:visited {
color: #0000EE;
text-decoration: none;
}
.link-button:hover {
text-decoration: underline;
}
.checked-radio-panel {
visibility: hidden;
}
input:checked ~ .checked-radio-panel {
visibility: visible;
}
footer {
margin: 2em 1em 1em;
}
hr {
border-width: 1px 0 0 0;
}
#configs-panel > header {
margin: 0.6em 0 0.4em;
}
</style>
</head>
<body>
<ul id="list-of-providers">
<li><input type="radio" name="pacProvider" id="none" checked> <label for="none">Отключить</label></li>
</ul>
<div style="white-space: nowrap">
Обновлялись: <span class="update-date">...</span>
</div>
<div id="status">Загрузка...</div>
<hr/>
<div id="configs-panel">
<header>Я ❤️ уведомления:</header>
<ul id="list-of-handlers">
</ul>
</div>
<footer>
<input type="button" value="Готово" class="close-button">&nbsp;<a href="../debug/index.html" style="text-decoration: none; margin-left: 1em;">Отладка</a>
</footer>
<script src="./index.js"></script>
<script src="./keep-links-clickable.js"></script>
</body>
</html>

View File

@ -16,7 +16,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) => {
};
const antiCensorRu = backgroundPage.antiCensorRu;
const antiCensorRu = backgroundPage.apis.antiCensorRu;
// SET DATE
@ -182,6 +182,25 @@ chrome://extensions</a>
};
}
const conpanel = document.getElementById('list-of-handlers');
backgroundPage.apis.errorHandlers.getEventsMap().forEach( (value, name) => {
const li = document.createElement('li');
li.innerHTML = `
<input type="checkbox" id="if-on-${name}"/>
<label for="if-on-${name}">${value}</label>`;
const box = li.querySelector('input');
box.checked = backgroundPage.apis.errorHandlers.isOn(name);
box.onclick = function() {
const id = this.id.replace('if-on-', '');
backgroundPage.apis.errorHandlers.switch(this.checked ? 'on' : 'off', id);
};
conpanel.appendChild(li);
});
setStatusTo('');
if (antiCensorRu.ifFirstInstall) {
const id = antiCensorRu.currentPacProviderKey || 'none';