mirror of
https://github.com/anticensority/runet-censorship-bypass.git
synced 2024-11-28 04:13:43 +03:00
314 lines
7.0 KiB
JavaScript
314 lines
7.0 KiB
JavaScript
|
'use strict';
|
|||
|
|
|||
|
console.log('Started.');
|
|||
|
|
|||
|
window.state = {
|
|||
|
lastError: null,
|
|||
|
ifNotControllable: false,
|
|||
|
};
|
|||
|
|
|||
|
window.whichExtensionHtml = `
|
|||
|
|
|||
|
${chrome.i18n.getMessage('noControl')}
|
|||
|
<a href="chrome://settings/search#${chrome.i18n.getMessage('proxy')}">
|
|||
|
${chrome.i18n.getMessage('which')}
|
|||
|
</a>`;
|
|||
|
|
|||
|
const resetBadge = function resetBadge() {
|
|||
|
|
|||
|
okBadge('M');
|
|||
|
chrome.browserAction.setTitle({title: ''});
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
window.timeouted = (cb) => (...args) => window.setTimeout(() => cb(...args), 0);
|
|||
|
|
|||
|
const errorJsonReplacer = function errorJsonReplacer(key, value) {
|
|||
|
|
|||
|
// fooWindow.ErrorEvent !== barWindow.ErrorEvent
|
|||
|
if (!( value && value.constructor
|
|||
|
&& ['Error', 'Event'].some(
|
|||
|
(suff) => value.constructor.name.endsWith(suff)
|
|||
|
)
|
|||
|
)) {
|
|||
|
return value;
|
|||
|
}
|
|||
|
const alt = {};
|
|||
|
|
|||
|
Object.getOwnPropertyNames(value).forEach(function(key) {
|
|||
|
|
|||
|
alt[key] = value[key];
|
|||
|
|
|||
|
}, value);
|
|||
|
|
|||
|
for(const prop in value) {
|
|||
|
if (/^[A-Z]/.test(prop)) {
|
|||
|
// MOUSEMOVE, CLICK, KEYUP, NONE, etc.
|
|||
|
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']) {
|
|||
|
delete alt[circularProp];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (value.name) {
|
|||
|
alt.name = value.name;
|
|||
|
}
|
|||
|
|
|||
|
return alt;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
const extName = chrome.runtime.getManifest().name;
|
|||
|
|
|||
|
chrome.notifications.onClicked.addListener( timeouted( (notId) => {
|
|||
|
|
|||
|
chrome.notifications.clear(notId);
|
|||
|
const err = window.state.lastError;
|
|||
|
err.extName = extName;
|
|||
|
const type = 'ext-error';
|
|||
|
const json = JSON.stringify(err, errorJsonReplacer, 0);
|
|||
|
const url = 'http://rebrand.ly/ac-error/?json=' + encodeURIComponent(json)
|
|||
|
+ (type ? '&type=' + encodeURIComponent(type) : '')
|
|||
|
+ '&version=' + chrome.runtime.getManifest().version;
|
|||
|
chrome.tabs.create(
|
|||
|
{url: url}
|
|||
|
);
|
|||
|
|
|||
|
}));
|
|||
|
|
|||
|
|
|||
|
const mayNotify = function mayNotify(
|
|||
|
id, title, errOrMessage,
|
|||
|
{
|
|||
|
icon = 'default-128.png',
|
|||
|
ifSticky = true,
|
|||
|
} = {}
|
|||
|
) {
|
|||
|
|
|||
|
const message = errOrMessage.message || errOrMessage.toString();
|
|||
|
chrome.notifications.create(
|
|||
|
id,
|
|||
|
{
|
|||
|
title: title,
|
|||
|
message: message,
|
|||
|
contextMessage:
|
|||
|
extName + ' ' + chrome.runtime.getManifest().version.replace(/\d+\.\d+\./g, ''),
|
|||
|
requireInteraction: ifSticky,
|
|||
|
type: 'basic',
|
|||
|
iconUrl: './icons/' + icon,
|
|||
|
isClickable: true,
|
|||
|
}
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
window.installListenersOn = function installListenersOn(win, name, cb) {
|
|||
|
|
|||
|
win.addEventListener('error', (errEvent) => {
|
|||
|
|
|||
|
window.state.lastError = errEvent;
|
|||
|
console.warn(name + ':GLOBAL ERROR', errEvent);
|
|||
|
mayNotify('ext-error', 'Ошибка расширения!', errEvent, {icon: 'ext-error-128.png'});
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
win.addEventListener('unhandledrejection', (event) => {
|
|||
|
|
|||
|
console.warn(name + ': Unhandled rejection. Throwing error.');
|
|||
|
event.preventDefault();
|
|||
|
console.log('ev', event);
|
|||
|
throw event.reason;
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
if (cb) {
|
|||
|
// In most cases getBackgroundPage( (bg) => installListenersOn
|
|||
|
// Without setTimeout errors are swallowed, bug #357568
|
|||
|
timeouted(cb)();
|
|||
|
}
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
window.installListenersOn(window, 'BG');
|
|||
|
|
|||
|
const redBadge = function redBadge(msg) {
|
|||
|
|
|||
|
chrome.browserAction.setBadgeBackgroundColor({
|
|||
|
color: '#db4b2f',
|
|||
|
});
|
|||
|
chrome.browserAction.setBadgeText({
|
|||
|
text: msg,
|
|||
|
});
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
const defaultColorPromise = new Promise( function (resolve) {
|
|||
|
|
|||
|
chrome.browserAction.getBadgeBackgroundColor({}, resolve);
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
const okBadge = function okBadge(msg) {
|
|||
|
|
|||
|
defaultColorPromise.then( function (defaultColor) {
|
|||
|
chrome.browserAction.setBadgeBackgroundColor({
|
|||
|
color: defaultColor,
|
|||
|
});
|
|||
|
chrome.browserAction.setBadgeText({
|
|||
|
text: msg,
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
const mandatory = (err) => {
|
|||
|
|
|||
|
throw new TypeError('This arg is required!');
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
const throwIfError = (err) => {
|
|||
|
|
|||
|
if (err) {
|
|||
|
throw new Error('Got error in cb!');
|
|||
|
}
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
const areSettingsNotControllableFor = function areSettingsNotControllable(details = mandatory()) {
|
|||
|
|
|||
|
state.ifNotControllable = ['controlled_by_other', 'not_controllable']
|
|||
|
.some( (pref) => details.levelOfControl.startsWith(pref) );
|
|||
|
|
|||
|
if (state.ifNotControllable) {
|
|||
|
console.warn('Failed, other extension is in control.');
|
|||
|
state.ifNotControlled = true;
|
|||
|
redBadge('xCTRL');
|
|||
|
chrome.browserAction.setTitle({title: 'Другое расширение контролирует прокси'});
|
|||
|
} else {
|
|||
|
console.log('Settings are under our control.');
|
|||
|
state.ifNotControlled = !details.levelOfControl.startsWith('controlled_by_this');
|
|||
|
resetBadge();
|
|||
|
}
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
chrome.proxy.settings.onChange.addListener( (details) => {
|
|||
|
|
|||
|
console.log('CHANGED prx');
|
|||
|
areSettingsNotControllableFor(details);
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
const updateControlState = function updateControlState(cb = mandatory()) {
|
|||
|
|
|||
|
chrome.proxy.settings.get({}, timeouted( (details) => {
|
|||
|
|
|||
|
areSettingsNotControllableFor(details);
|
|||
|
cb();
|
|||
|
|
|||
|
}));
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
const mayUpdatePac = function mayUpdatePac(cb = mandatory()) {
|
|||
|
|
|||
|
if (Date.now() < (localStorage.lastPacUpdateStamp || 0) + 12*60*60*1000 ) {
|
|||
|
console.log('Too early for an update.' );
|
|||
|
return cb();
|
|||
|
}
|
|||
|
|
|||
|
fetch('https://rebrand.ly/ac-light-pac').then(
|
|||
|
(res) => {
|
|||
|
|
|||
|
const status = res.status;
|
|||
|
if ( !( status >= 200 && status < 300 || status === 304 ) ) {
|
|||
|
return cb(new Error('Не удалось скачать скрипт, ошибка ' + status));
|
|||
|
}
|
|||
|
return res.text();
|
|||
|
|
|||
|
},
|
|||
|
cb
|
|||
|
).then( (pacData) => {
|
|||
|
|
|||
|
console.log('Updating PAC...');
|
|||
|
chrome.proxy.settings.set(
|
|||
|
{
|
|||
|
value: {
|
|||
|
mode: 'pac_script',
|
|||
|
pacScript: {
|
|||
|
// Use only with extension!
|
|||
|
data: pacData
|
|||
|
},
|
|||
|
},
|
|||
|
},
|
|||
|
timeouted(() => {
|
|||
|
|
|||
|
const err = chrome.runtime.lastError || chrome.extension.lastError;
|
|||
|
if(err) {
|
|||
|
console.log('Updated with error:', err);
|
|||
|
state.lastError = err;
|
|||
|
redBadge('ERR');
|
|||
|
chrome.browserAction.setTitle({title: 'Произошла ошибка'});
|
|||
|
setTimeout(resetBadge, 10000);
|
|||
|
return cb(err);
|
|||
|
}
|
|||
|
console.log('Updated, no errors.');
|
|||
|
localStorage.lastPacUpdateStamp = Date.now();
|
|||
|
updateControlState( () => {
|
|||
|
|
|||
|
if(state.ifNotControlled) {
|
|||
|
cb(new Error(window.whichExtensionHtml));
|
|||
|
} else {
|
|||
|
cb();
|
|||
|
}
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
})
|
|||
|
);
|
|||
|
|
|||
|
},
|
|||
|
cb
|
|||
|
);
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
window.switchPac = function switchPac(onOff, cb = mandatory()) {
|
|||
|
|
|||
|
if(onOff !== 'off') {
|
|||
|
localStorage.onOff = 'on';
|
|||
|
chrome.browserAction.setIcon( {path: './icons/default-128.png'} );
|
|||
|
return mayUpdatePac(cb);
|
|||
|
}
|
|||
|
|
|||
|
chrome.proxy.settings.clear({}, timeouted(() => {
|
|||
|
|
|||
|
localStorage.lastPacUpdateStamp = 0;
|
|||
|
localStorage.onOff = 'off';
|
|||
|
chrome.browserAction.setIcon({
|
|||
|
path: './icons/default-grayscale-128.png',
|
|||
|
});
|
|||
|
cb();
|
|||
|
|
|||
|
}));
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
resetBadge();
|
|||
|
switchPac( localStorage.onOff, (err) => {
|
|||
|
|
|||
|
updateControlState(() => throwIfError(err));
|
|||
|
|
|||
|
});
|