This commit is contained in:
erosman 2024-01-13 23:48:13 +03:30 committed by GitHub
parent 138e7e9489
commit ca4f82ba78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 448 additions and 298 deletions

View File

@ -18,8 +18,21 @@
<dl>
<dt>8.9</dt>
<dd>Added "Log" to the toolbar popup buttons (#44)</dd>
<dd>Added limited log display on Chrome (experimental)</dd>
<dd>Added Theme feature (#71, #100)</dd>
<dd>Added toggle more options on toolbar popup (#54)</dd>
<dd>Fixed proxy DNS in "Import Proxy List" (#102) (from 8.7)</dd>
<dd>Fixed settings upgrade when username/password is missing (#103)</dd>
<dd>Fixed settings upgrade (import older) when hostname is missing (#108)</dd>
<dd>Fixed settings upgrade (import older) when username/password is missing (#103)</dd>
<dd>Increased log content</dd>
<dd>Removed "Show Pattern Proxy" option and made it default (#57) (from 8.7) (Firefox only)</dd>
<dd>Removed Tab Proxy page-action and set it to the toolbar icon (#114) (Firefox only)</dd>
<dd>Updated add pattern user interface (#105)</dd>
<dd>Updated code to process duplicate hostname:port (#33, #76)</dd>
<dd>Updated options to disable "Store Locally" on Firefox (Chrome only)</dd>
<dd>Updated popup include/exclude host feature</dd>
<dd>Updated popup user interface</dd>
<dt>8.8</dt>
<dd>Added Show hidden feature</dd>
@ -31,7 +44,7 @@
<dd>Added Auto Backup feature</dd>
<dd>Added FoxyProxy Basic detection (disabled for now)</dd>
<dd>Added Help translation form</dd>
<dd>Added option to show proxies on the toolbar badge when in Proxy by Patterns mode (Firefox only) (#57)</dd>
<dd>Added "Show Pattern Proxy" option to show proxies when in "Proxy by Patterns" mode (#57) (Firefox only)</dd>
<dd>Added pattern matching to the Log display (#91)</dd>
<dd>Added proxy title to the toolbar icon mouse-over title display (#74)</dd>
<dd>Changed the global Proxy DNS to per-proxy setting (#75)</dd>
@ -60,7 +73,7 @@
<dt>8.3</dt>
<dd>Added enterprise policy & managed storage feature (#42) (experimental)</dd>
<dd>Added locally stored PAC feature (#46) (experimental)</dd>
<dd>Added PAC "Store Locally" feature (#46) (experimental) (Chrome only)</dd>
<dd>Added PAC view feature</dd>
<dd>Fixed an issue with empty Global Exclude</dd>
<dd>Fixed an issue with upgrade sync data on Firefox (#53)</dd>

View File

@ -13,8 +13,8 @@ export const pref = {
mode: 'disable',
sync: false,
autoBackup: false,
showPatternProxy: false,
passthrough: '',
theme: '',
container: {},
commands: {},
data: []

View File

@ -4,6 +4,7 @@
--bg: #fff;
--alt-bg: #f5f5f5;
--hover: #eaeaea;
--highlight: #f90;
--body-bg: #630;
--header: #c60;
@ -17,7 +18,7 @@
--link: #e70;
--border: #ddd;
/* --shadow: #0004; */
--shadow: #0004;
--dim: #777;
--tr: #f5f5f5;
}
@ -30,7 +31,7 @@
--alt-bg: #666;
--hover: #888;
--body-bg: #630;
/* --body-bg: #630; */
--header: #e70;
--btn-bg: #f90;
@ -38,7 +39,7 @@
--link: #f90;
--border: #777;
/* --shadow: #fff8; */
--shadow: #fff8;
--dim: #ccc;
--tr: #531;
}
@ -66,6 +67,11 @@ section {
padding: 0;
}
a {
color: var(--link);
text-decoration: none;
}
select,
textarea,
input[type="number"],
@ -79,9 +85,11 @@ input[type="url"] {
border-radius: 0.3em;
}
a {
color: var(--link);
text-decoration: none;
label[for],
input[type="checkbox"],
summary,
.pointer {
cursor: pointer;
}
::placeholder {
@ -90,16 +98,35 @@ a {
font-style: italic;
}
button {
background: var(--btn-bg);
.invalid,
input:invalid {
box-shadow: 1px 1px 4px #f20, -1px -1px 4px #f20;
}
/* ----- Buttons ----- */
button,
label.flat {
background-color: var(--btn-bg);
border: none;
color: inherit;
cursor: pointer;
text-align: center;
transition: 0.5s;
white-space: nowrap;
}
button:hover {
background: var(--btn-hover);
button.flat,
label.flat {
display: inline-block;
font-size: 0.9em;
color: #fff;
border-radius: 5px;
padding: 0.4em 1em;
min-width: 8em;
}
button:hover,
label.flat:hover {
background-color: var(--btn-hover);
}
button:disabled,
@ -108,7 +135,19 @@ select:disabled {
opacity: 0.4;
}
.invalid,
input:invalid {
box-shadow: 1px 1px 4px #f20, -1px -1px 4px #f20;
}
button.plain {
background-color: unset;
padding: 0;
margin: 0;
min-width: 1em;
}
button[type="submit"] {
display: table;
color: #fff;
font-size:0.9em;
border-radius: 5px;
padding: 0.5em 5em;
margin: 1em auto 0;
}
/* ----- /Buttons ----- */

View File

@ -407,12 +407,16 @@
<dt>Individual Proxy</dt>
<dd>Connections are passed through the selected proxy (or PAC)</dd>
<dt>▶ More</dt>
<dd>Show or hide additional options</dd>
<dd>User choice is temporarily stored in localStorage (not available in Private/Incognito)</dd>
<dt>Search Filter</dt>
<dd>Proxy <i>Title</i>, <i>Hostname</i>, & <i>Port</i> are searched and display filtered based on the input value (case-insensitive)</dd>
<dd>Use <i>:port</i> to filter by port</dd>
<dt>Quick Add</dt>
<dd>Select a proxy to add a pattern based on the current page URL</dd>
<dd>Add current page's host pattern to the selected proxy's include</dd>
<dd>Only top 10 active proxies are listed</dd>
<dt>Exclude Host</dt>
@ -494,6 +498,7 @@
<p>There is a discussion to increase the storage sync quota to 1MB.</p>
<p>See also:</p>
<ul>
<li><a href="https://github.com/w3c/webextensions/issues/520" target="_blank">Increase maximum total size (QUOTA_BYTES) for storage.sync</a></li>
<li><a href="https://github.com/w3c/webextensions/issues/351" target="_blank">Discuss limits applied to storage.local and storage.sync API</a></li>
<li><a href="https://github.com/w3c/webextensions/issues/510">Proposal: Increase maximum item size in Storage sync quotas</a></li>
</ul>
@ -508,8 +513,8 @@
<p>Toggle changes the browser preferences and does not require SAVE.</p>
<h3 id="show-pattern-proxy">Show Pattern Proxy <span>(Firefox only)</span></h3>
<p>Show per-tab proxies on the toolbar badge when in Proxy by Patterns mode</p>
<!-- <h3 id="show-pattern-proxy">Show Pattern Proxy <span>(Firefox only)</span></h3>
<p>Show per-tab proxies on the toolbar badge when in Proxy by Patterns mode</p> -->
<h3 id="container" class="experimental">Incognito/Container</h3>
<p>Set a proxy for Incognito (Private Mode) and/or Containers</p>
@ -778,7 +783,7 @@
<dd>Option to pass DNS to the SOCKS proxy when using SOCKS</dd>
<dd class="note">See also:
<ul>
<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1741375" target="_blank">Proxy DNS by default when using SOCKS v5</a> has landed in Firefox 123</li>
<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1741375" target="_blank">Proxy DNS by default when using SOCKS v5</a> (Firefox 123)</li>
<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1799411" target="_blank">DNS leaks with proxy extension</a></li>
<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1791243" target="_blank">Firefox queries DNS of Proxy destination</a></li>
<li><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1867562" target="_blank">Despite the fact that DNS over HTTPS is switched off on Privacy and Security bar in Settings it still leaks my DNS</a></li>
@ -788,10 +793,11 @@
<dt>PAC URL <span>(not available on Android)</span></dt>
<dd>Required for PAC type</dd>
<dd>Proxy Auto-Configuration file (PAC) can be used to handle all proxying configurations</dd>
<dd>PAC is a configuration file, not a proxy server</dd>
<dd>Patterns are not applicable in PAC type</dd>
<dd>See also: <a href="https://developer.mozilla.org/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_PAC_file" target="_blank">Proxy Auto-Configuration (PAC) file</a></dd>
<dt id="store-locally" class="experimental">Store Locally</dt>
<dt id="store-locally" class="experimental">Store Locally <span>(Chrome only)</span></dt>
<dd>Option to store PAC locally</dd>
<dd>Option should only be used for altering remote PAC rules</dd>
<dd>Storing large or many PACs locally is not recommended and can prevent browser sync</dd>
@ -1064,16 +1070,25 @@ http://pac-url.com/etc?type=pac&title=Work PAC&color=663300</pre>
<li>Result will be match ✅ or fail ❌</li>
</ol>
<h3 id="log">Log <span>(Firefox only)</span></h3>
<h3 id="log">Log <span>(Firefox mainly)</span> <span class="experimental">(limited on Chrome v8.9)</span></h3>
<ul>
<li>Live Log displays the web requests in reversed order (new one on top)</li>
<li>It will continue to display while the Options page is open and will stop logging as soon as the page is closed</li>
<li>Log data is not stored</li>
<li>Not available on chrome since in Chrome <a href="https://developer.chrome.com/docs/extensions/reference/webRequest/#event-onBeforeRequest" target="_blank">webRequest.onBeforeRequest</a> API does not provide <a href="https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest" target="_blank">proxyInfo</a></li>
<!-- <li>Not available on chrome since in Chrome <a href="https://developer.chrome.com/docs/extensions/reference/webRequest/#event-onBeforeRequest" target="_blank">webRequest.onBeforeRequest</a> API does not provide <a href="https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest" target="_blank">proxyInfo</a></li> -->
<li>Patterns will show if FoxyProxy was in Proxy by Patterns mode when the log page is loaded (refresh if necessary)</li>
<li>Limited logging on chrome since Chrome <a href="https://developer.chrome.com/docs/extensions/reference/webRequest/#event-onBeforeRequest" target="_blank">webRequest.onBeforeRequest</a> API does not provide <a href="https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest" target="_blank">proxyInfo</a> (and some others)
<ul>
<li>No Incognito or Container data on Chrome</li>
<li>No proxy data on Chrome</li>
</ul>
</li>
</ul>
<h2 id="enterprise-policy" class="experimental">Enterprise Policy <span>(v8.2)</span></h2>
<p>FoxyProxy supports enterprise policy and managed storage, and they can be configured to set FoxyProxy options.</p>
<p><a href="https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/storage/managed" target="_blank">storage.managed</a> is not supported on Firefox for Android.</p>
@ -1091,14 +1106,14 @@ http://pac-url.com/etc?type=pac&title=Work PAC&color=663300</pre>
</li>
</ul>
<h5>Default Options <span>(v8.7)</span></h5>
<h5>Default Options <span>(v8.9)</span></h5>
<pre>
{
"mode": "disable", // mandatory: current option, necessary to enable
"sync": false, // optional: not necessary as it will be disabled on managed storage
"autoBackup": false, // optional: not necessary as save is disabled on managed storage
"showPatternProxy": false, // optional: Show proxies on the toolbar badge when in Proxy by Patterns mode (Firefox only)
"passthrough": "", // optional: Global Exclude
"theme": "", // optional: set the theme
"container": {}, // optional: Incognito/Container settings
"commands": {}, // optional: keyboard shortcut settings
"data": [] // mandatory: array of proxies and their patterns

View File

@ -5,7 +5,7 @@ class I18n {
static {
document.querySelectorAll('template').forEach(i => this.set(i.content));
this.set();
document.body.style.opacity = 1; // show after i18n
// document.body.style.opacity = 1; // show after i18n
}
static set(target = document) {

View File

@ -1,4 +1,5 @@
@import 'default.css';
@import 'theme.css';
/* ----- General ----- */
:root {
@ -27,7 +28,7 @@ article {
/* ----- h1-h5 ----- */
h2 {
color: var(--header);
font-size: 2.2em;
font-size: 2.5em;
border-bottom: 1px solid var(--border);
font-weight: normal;
}
@ -37,7 +38,6 @@ h2:first-of-type {
}
h3 {
color: var(--nav-hover);
font-size: 1.5em;
font-weight: normal;
}
@ -169,7 +169,7 @@ mark {
/* ----- About ----- */
.about dt {
background-color: unset;
color: var(--nav-hover);
color: var(--h3);
font-weight: bold;
margin-bottom: 0.2em;
font-size: 1.1em;
@ -201,11 +201,11 @@ nav {
nav a {
color: var(--color);
padding: 0.5em 1em;
transition: 0.5s;
/* transition: 0.5s; */
}
nav a:hover {
color: var(--btn-bg);
background-color: var(--hover);
}
/* ----- /Navigation ----- */

View File

@ -7,28 +7,22 @@ export class Log {
static {
this.trTemplate = document.querySelector('.log template').content.firstElementChild;
this.tbody = document.querySelector('.log tbody');
// no proxy info on chrome
if (App.firefox) {
this.tbody.textContent = ''; // remove "not available" notice
browser.webRequest.onBeforeRequest.addListener(e => this.process(e), {urls: ['*://*/*']});
}
this.proxyCache = {}; // used to find proxy
this.mode = 'disable';
browser.webRequest.onBeforeRequest.addListener(e => this.process(e), {urls: ['*://*/*']});
}
static process(e) {
const tr = this.tbody.children[199] || this.trTemplate.cloneNode(true);
const [, time, container, method, doc, url, title, type, host, port, pattern] = tr.children;
const [, time, container, method, reqType, doc, url, title, type, host, port, pattern] = tr.children;
time.textContent = this.formatInt(e.timeStamp);
method.textContent = e.method;
doc.textContent = e.documentUrl || ''; // For a top-level document, documentUrl is undefined
doc.title = e.documentUrl || '';
url.textContent = decodeURIComponent(e.url);
url.title = e.url;
container.classList.toggle('incognito', e.incognito);
reqType.textContent = e.type;
// For a top-level document, documentUrl is undefined, chrome uses e.initiator
this.prepareOverflow(doc, e.documentUrl || e.initiator || '');
this.prepareOverflow(url, decodeURIComponent(e.url));
container.classList.toggle('incognito', !!e.incognito);
container.textContent = e.cookieStoreId?.startsWith('firefox-container-') ? 'C' + e.cookieStoreId.substring(18) : '';
const info = e.proxyInfo || {host: '', port: '', type: ''};
@ -43,8 +37,7 @@ export class Log {
// show matching pattern in pattern mode
const pat = this.mode === 'pattern' && item?.include.find(i => new RegExp(Pattern.get(i.pattern, i.type), 'i').test(e.url));
const text = pat?.title || pat?.pattern || '';
pattern.textContent = text;
pattern.title = text;
this.prepareOverflow(pattern, text);
this.tbody.prepend(tr); // in reverse order, new on top
}
@ -53,4 +46,10 @@ export class Log {
return new Intl.DateTimeFormat(navigator.language,
{hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false}).format(new Date(d));
}
// set title, in case text overflows
static prepareOverflow(elem, value) {
elem.textContent = value;
elem.title = value;
}
}

View File

@ -40,30 +40,19 @@ import {CryptoJS} from '../lib/aes.3.1.2.js';
export class Migrate {
static async init(pref) {
// --- 8.8
// tidy up left-over obj from 8.0 Sync typo mistake
if (Object.hasOwn(pref, 'obj')) {
delete pref.obj;
await browser.storage.local.remove('obj');
await browser.storage.sync.remove('obj');
}
// --- 8.7
// change global proxyDNS to per-proxy
// --- 8.9
// 8.9 remove showPatternProxy (from 8.7)
// 8.8 tidy up left-over obj Sync typo mistake (from 8.0)
// 8.7 change global proxyDNS to per-proxy (from 8.0)
if (Object.hasOwn(pref, 'proxyDNS') && pref.data) {
pref.data.forEach(i => i.proxyDNS = !!pref.proxyDNS);
delete pref.proxyDNS;
await browser.storage.local.remove('proxyDNS');
await browser.storage.local.set(pref);
}
// --- 8.1
if (Object.hasOwn(pref, 'globalExcludeWildcard')) {
delete pref.globalExcludeWildcard; // from 8.0, removed in 8.1
delete pref.globalExcludeRegex; // from 8.0, removed in 8.1
delete pref.obj; // 8.0 Sync typo mistake
await browser.storage.local.remove(['globalExcludeWildcard', 'globalExcludeRegex', 'obj']);
}
// 8.1 remove globalExcludeWildcard, globalExcludeRegex (from 8.0)
const keys = ['showPatternProxy', 'obj', 'proxyDNS', 'globalExcludeWildcard', 'globalExcludeRegex'];
keys.forEach(i => delete pref[i]);
await browser.storage.local.remove(keys);
await browser.storage.sync.remove(keys);
// --- 8.0
if (pref.data) { return; }
@ -185,7 +174,9 @@ export class Migrate {
const db = App.getDefaultPref();
db.sync = !!pref.sync;
const data = Object.values(pref).filter(i => i && Object.hasOwn(i, 'address')); // null value causes an error
// null value causes an error in hasOwn, direct proxies don't have 'address'
const data = Object.values(pref).filter(i => i && ['address', 'type'].some(p => Object.hasOwn(i, p)));
data.sort((a, b) => a.index - b.index); // sort by index
data.forEach(item => {
@ -193,8 +184,8 @@ export class Migrate {
active: item.active === 'true' || item.active === true, // convert to boolean, some old databases have mixed types
title: item.title || '',
type: typeSet[item.type], // convert to actual type: http | https | socks4 | socks5 | direct | + add PAC
hostname: item.address, // rename to hostname
port: item.port,
hostname: item.address || '', // rename to hostname
port: item.port || '',
username: item.username || '',
password: item.password || '',
cc: item.cc || '', // remove country, use CC in country-code.js
@ -208,9 +199,7 @@ export class Migrate {
};
pxy.cc === 'UK' && (pxy.cc = 'GB'); // convert UK to ISO 3166-1 GB
// --- type 'direct'
pxy.type === 'direct' && (pxy.hostname = 'DIRECT');
pxy.type === 'direct' && (pxy.hostname = 'DIRECT'); // type 'direct'
/*
{

View File

@ -7,7 +7,6 @@
// Fixed in Firefox 119
import {Pattern} from './pattern.js';
import {PageAction} from './page-action.js';
import {Location} from './location.js';
// ---------- Firefox Proxy Process ------------------------
@ -16,11 +15,10 @@ export class OnRequest {
static {
// --- default values
this.mode = 'disable';
this.proxy = null; // used for Single Proxy
this.proxy = {}; // used for Single Proxy
this.data = []; // used for Proxy by Pattern
this.passthrough = []; // RegExp string
this.net = []; // [start, end] strings
this.showPatternProxy = false;
this.tabProxy = {}; // tab proxy, may be lost in MV3 if bg is unloaded
this.container = {}; // incognito/container proxy
@ -40,12 +38,12 @@ export class OnRequest {
const [passthrough, , net] = Pattern.getPassthrough(pref.passthrough);
this.passthrough = passthrough;
this.net = net;
this.showPatternProxy = pref.showPatternProxy;
const data = pref.data.filter(i => i.active && i.type !== 'pac' && i.hostname); // filter data
// filter data
const data = pref.data.filter(i => i.active && i.type !== 'pac' && i.hostname);
// --- single proxy
/:\d+$/.test(pref.mode) && (this.proxy = data.find(i => pref.mode === `${i.hostname}:${i.port}`));
// --- single proxy (false|undefined|proxy object)
this.proxy = /:\d+[^/]*$/.test(pref.mode) && data.find(i => pref.mode === `${i.hostname}:${i.port}`);
// --- proxy by pattern
this.data = data.filter(i => i.include[0] || i.exclude[0]).map(item => {
@ -75,34 +73,37 @@ export class OnRequest {
}
static process(e) {
const tabId = e.tabId;
switch (true) {
// --- check local & global passthrough
case this.bypass(e.url):
this.setAction(null, tabId);
return {type: 'direct'};
// --- tab proxy
case e.tabId !== -1 && !!this.tabProxy[e.tabId]:
return this.processProxy(this.tabProxy[e.tabId]);
case tabId !== -1 && !!this.tabProxy[tabId]:
return this.processProxy(this.tabProxy[tabId], tabId);
// --- incognito proxy
case e.tabId !== -1 && e.incognito && !!this.container.incognito:
return this.processProxy(this.container.incognito);
case tabId !== -1 && e.incognito && !!this.container.incognito:
return this.processProxy(this.container.incognito, tabId);
// --- container proxy
case e.tabId !== -1 && e.cookieStoreId && !!this.container[e.cookieStoreId]:
return this.processProxy(this.container[e.cookieStoreId]);
case tabId !== -1 && e.cookieStoreId && !!this.container[e.cookieStoreId]:
return this.processProxy(this.container[e.cookieStoreId], tabId);
// --- standard operation
case this.mode === 'disable': // pass direct
case this.mode === 'direct': // pass direct
case this.mode.includes('://') && !/:\d+$/.test(this.mode): // PAC URL is set
this.setAction(null, tabId);
return {type: 'direct'};
case this.mode === 'pattern': // check if url matches patterns
return this.processPattern(e.url, e.tabId);
return this.processPattern(e.url, tabId);
default: // get the proxy for all
return this.processProxy(this.proxy);
return this.processProxy(this.proxy, tabId);
}
}
@ -111,31 +112,19 @@ export class OnRequest {
for (const proxy of this.data) {
if (!match(proxy.exclude) && match(proxy.include)) {
this.processShowPatternProxy(proxy, tabId);
return this.processProxy(proxy);
// this.processShowPatternProxy(proxy, tabId);
return this.processProxy(proxy, tabId);
}
}
this.setAction(null, tabId);
return {type: 'direct'}; // no match
}
static processShowPatternProxy(item, tabId) {
// Set to -1 if the request isn't related to a tab
if (tabId === -1 || !this.showPatternProxy) { return; }
const host = [item.hostname, item.port].filter(Boolean).join(':');
const title = [item.title, host, item.city, ...Location.get(item.cc)].filter(Boolean).join('\n');
const text = item.title || item.hostname;
const color = item.color;
browser.action.setBadgeBackgroundColor({color, tabId});
browser.action.setTitle({title, tabId});
browser.action.setBadgeText({text, tabId});
}
static processProxy(proxy) {
const {type, hostname: host, port, username, password, proxyDNS} = proxy;
if (type === 'direct') { return {type: 'direct'}; }
static processProxy(proxy, tabId) {
this.setAction(proxy, tabId);
const {type, hostname: host, port, username, password, proxyDNS} = proxy || {};
if (!type || type === 'direct') { return {type: 'direct'}; }
// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/ProxyChannelFilter.sys.mjs#102
// Although API converts to number -> let port = Number.parseInt(proxyData.port, 10);
@ -163,6 +152,28 @@ export class OnRequest {
return response;
}
static setAction(item, tabId) {
// Set to -1 if the request isn't related to a tab
if (tabId === -1) { return; }
// --- reset values
let title = null;
let text = null;
let color = null;
// --- set proxy details
if (item) {
const host = [item.hostname, item.port].filter(Boolean).join(':');
title = [item.title, host, item.city, ...Location.get(item.cc)].filter(Boolean).join('\n');
text = item.title || item.hostname;
color = item.color;
}
browser.action.setBadgeBackgroundColor({color, tabId});
browser.action.setTitle({title, tabId});
browser.action.setBadgeText({text, tabId});
}
// ---------- passthrough --------------------------------
static bypass(url) {
switch (true) {
@ -223,13 +234,21 @@ export class OnRequest {
}
this.tabProxy[tab.id] = pxy;
PageAction.set(tab.id, pxy);
// PageAction.set(tab.id, pxy);
}
static async unsetTabProxy() {
const [tab] = await browser.tabs.query({currentWindow: true, active: true});
delete this.tabProxy[tab.id];
PageAction.unset(tab.id);
// PageAction.unset(tab.id);
}
// ---------- Update Page Action -------------------------
static onUpdated(tabId, changeInfo, tab) {
if (changeInfo.status !== 'complete') { return; }
const pxy = this.tabProxy[tabId];
pxy ? this.setAction(pxy, tabId) : this.checkPageAction(tab);
}
// ---------- Incognito/Container ------------------------
@ -237,14 +256,6 @@ export class OnRequest {
if (tab.id === -1 || this.tabProxy[tab.id]) { return; } // not if tab proxy is set
const pxy = tab.incognito ? this.container.incognito : this.container[tab.cookieStoreId];
pxy && PageAction.set(tab.id, pxy);
}
// ---------- Update Page Action -------------------------
static onUpdated(tabId, changeInfo, tab) {
if (changeInfo.status !== 'complete') { return; }
const pxy = this.tabProxy[tab.id];
pxy ? PageAction.set(tab.id, pxy) : this.checkPageAction(tab);
pxy && this.setAction(pxy, tab.id);
}
}

View File

@ -1,6 +1,7 @@
@import 'default.css';
@import 'progress-bar.css';
@import 'spinner.css';
@import 'theme.css';
/* ----- General ----- */
:root {
@ -58,13 +59,6 @@ iframe {
vertical-align: text-bottom;
} */
label[for],
input[type="checkbox"],
summary,
.pointer {
cursor: pointer;
}
label > input[type="checkbox"] {
margin-right: 0.5em;
}
@ -80,29 +74,17 @@ textarea {
resize: vertical;
}
.flat {
background: var(--btn-bg);
/* .flat {
color: #fff;
cursor: pointer;
text-align: center;
transition: 0.5s;
border-radius: 5px;
padding: 0.4em 1em;
border: 0;
font-weight: bold;
font-size: 0.9em;
min-width: 8em;
display: inline-block;
white-space: nowrap;
}
.flat:hover {
background: var(--btn-hover);
/* box-shadow: 0px 1px 5px var(--shadow); */
}
} */
fieldset {
background: var(--bg);
background-color: var(--bg);
border-radius: 0.5em;
border: 0;
padding: 1.5em;
@ -154,7 +136,7 @@ input[type="checkbox"].control {
}
div.nav {
background: var(--nav-bg);
background-color: var(--nav-bg);
}
nav {
@ -175,12 +157,12 @@ nav img {
nav > label {
padding: 0.5em 1em;
transition: 0.5s;
/* transition: 0.5s; */
border-radius: 0.5em 0.5em 0 0;
}
nav > label:hover {
background: var(--nav-hover);
background-color: var(--nav-hover);
}
/* nav > label img {
@ -231,29 +213,19 @@ input[type="file"] {
/* ----- /Import/Export ----- */
/* ----- Submit Button ----- */
button[type="submit"] {
/* button[type="submit"] {
display: table;
color: #fff;
background: var(--btn-bg);
font-size: 0.9em;
font-weight: bold;
background-color: var(--btn-bg);
font-size:0.9em;
margin: 1em auto 0;
padding: 0.5em 3em;
text-shadow: 0 -1px 1px #333;
border: 0;
padding: 0.5em 5em;
border-radius: 5px;
box-shadow: 0 5px var(--body-bg);
}
button[type="submit"]:hover {
background-color: var(--btn-hover);
}
button[type="submit"]:active {
/* background-color: #3e8e41; */
box-shadow: 0 1px var(--body-bg);
transform: translateY(4px);
}
} */
/* ----- /Submit Button ----- */
/* ----- Toggle Switch ----- */
@ -274,7 +246,7 @@ button[type="submit"]:active {
z-index: 2;
width: 14px;
height: 14px;
background: #fff;
background-color: #fff;
left: 1px;
top: 1px;
border-radius: 50%;
@ -292,14 +264,6 @@ button[type="submit"]:active {
/* ----- /Toggle Switch ----- */
/* ----- Button ----- */
button.plain {
background-color: transparent;
padding: 0;
margin: 0;
min-width: 1em;
border: none;
}
button.bin,
button.test,
button.close {
@ -344,10 +308,15 @@ textarea {
font-size: 1em;
} */
section.options fieldset * {
transition: opacity 0.5s;
}
input[type="color"] {
border: 0;
}
.options div.theme,
.options div.container,
.options div.commands {
display: grid;
@ -357,6 +326,10 @@ input[type="color"] {
margin-bottom: 1em;
}
.options div.theme {
margin-left: unset;
}
.options div.buttons {
display: grid;
grid-auto-flow: column;
@ -369,7 +342,7 @@ input[type="color"] {
/* .options div.buttons {
padding: 0 1.5em;
margin: 0 -1.5em 1em;
background: var(--btn-bg);
background-color: var(--btn-bg);
}
.options div.buttons > * {
@ -520,7 +493,7 @@ details.proxy > summary span:nth-of-type(2):empty::before {
.pattern-box button.bin {
padding: 0;
background-color: transparent;
border: none;
/* border: none; */
font-size: 1em;
transition: 0.5s;
color: #ccc;
@ -529,74 +502,6 @@ details.proxy > summary span:nth-of-type(2):empty::before {
}
/* ----- /Pattern ----- */
/* ----- show/hide elements ----- */
details.proxy[data-type="direct"] :is(
[data-i18n="port"], [data-id="port"],
[data-i18n="username"], [data-id="username"],
[data-i18n="password"], .password,
[data-i18n="country"], [data-id="cc"],
[data-i18n="city"], [data-id="city"],
[data-type="pac"], .pac) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
details.proxy[data-type="pac"] :is(
[data-i18n="port"], [data-id="port"],
[data-i18n="username"], [data-id="username"],
[data-i18n="password"], .password) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
details.proxy[data-type="pac"] :is(.pattern-head, .pattern-box) {
display: none;
}
details.proxy:not([data-type="pac"]) :is([data-type="pac"], .pac) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
details.proxy[data-type="socks4"] :is(
[data-i18n="username"], [data-id="username"],
[data-i18n="password"], .password) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
details.proxy:not([data-type="socks5"]) :is([data-i18n="proxyDNS"], [data-id="proxyDNS"]) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
/* --- Chrome --- */
.chrome .firefox {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
.chrome details.proxy[data-type="socks5"] :is(
[data-i18n="username"], [data-id="username"],
[data-i18n="password"], .password) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
/* --- Basic --- */
.basic :is(.pattern-head, .pattern-box) {
display: none;
}
/* ----- /show/hide elements ----- */
/* ----- Popup ----- */
div.popup {
display: none;
@ -718,7 +623,7 @@ section.log {
.log thead th {
color: #fff;
background: #999;
background-color: #999;
padding: 0.5em 0.2em;
font-size: 0.9em;
font-weight: normal;
@ -730,13 +635,13 @@ section.log {
vertical-align: unset;
}
.log tbody tr {
/* border-bottom: 1px solid var(--border); */
/* .log tbody tr {
/* border-bottom: 1px solid var(--border); * /
animation: sect 0.5s ease-in-out;
}
} */
.log tr:hover {
background: var(--hover) !important;
background-color: var(--hover) !important;
}
.log tbody tr:nth-of-type(even) {
@ -748,28 +653,27 @@ section.log {
padding: 0.2em;
}
.log td:nth-of-type(5),
.log td:nth-of-type(6),
.log td:nth-of-type(11) {
.log td:nth-of-type(7),
.log td:nth-of-type(12) {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 20em;
}
.log td:nth-of-type(6) {
.log td:nth-of-type(7) {
max-width: 40em;
}
.log td:nth-of-type(7) {
.log td:nth-of-type(8) {
border-left: 2px solid var(--border);
}
.log td:nth-of-type(11) {
.log td:nth-of-type(12) {
max-width: 5em;
}
.log td.incognito::before {
content: '';
width: 1em;
@ -795,17 +699,87 @@ section.log {
content: counter(n);
font-size: 0.8em;
}
/* ----- /Log ----- */
/* Not Available On Chrome (Firefox Only)" */
.log td.unavailable {
height: 50vh;
text-align: center;
vertical-align: middle;
color: var(--btn-bg);
font-size: 3em;
/* ----- show/hide elements ----- */
details.proxy[data-type="direct"] :is(
[data-i18n="port"], [data-id="port"],
[data-i18n="username"], [data-id="username"],
[data-i18n="password"], .password,
[data-i18n="country"], [data-id="cc"],
[data-i18n="city"], [data-id="city"],
[data-type="pac"], .pac) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
.log td.unavailable::before {
content: none !important;
details.proxy[data-type="pac"] :is(
[data-i18n="port"], [data-id="port"],
[data-i18n="username"], [data-id="username"],
[data-i18n="password"], .password) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
/* ----- /Log ----- */
details.proxy[data-type="pac"] :is(.pattern-head, .pattern-box) {
display: none;
}
details.proxy:not([data-type="pac"]) :is([data-type="pac"], .pac) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
details.proxy[data-type="socks4"] :is(
[data-i18n="username"], [data-id="username"],
[data-i18n="password"], .password) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
details.proxy:not([data-type="socks5"]) :is([data-i18n="proxyDNS"], [data-id="proxyDNS"]) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
/* --- Chrome --- */
body.chrome .firefox {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
body.chrome details.proxy[data-type="socks5"] :is(
[data-i18n="username"], [data-id="username"],
[data-i18n="password"], .password) {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
body:not(.chrome) [data-i18n="storeLocally"] {
opacity: 0.3;
pointer-events: none;
user-select: none;
}
caption.firefox {
color: var(--nav-color);
visibility: hidden;
}
body.chrome caption.firefox {
visibility: visible;
opacity: 1;
}
/* --- Basic --- */
.basic :is(.pattern-head, .pattern-box) {
display: none;
}
/* ----- /show/hide elements ----- */

View File

@ -70,8 +70,17 @@
<label data-i18n="limitWebRTC"><input type="checkbox" id="limitWebRTC"></label>
<p class="description" data-i18n="limitWebRTCDescription"></p>
<label class="firefox" data-i18n="showPatternProxy"><input type="checkbox" id="showPatternProxy"></label>
<p class="description firefox" data-i18n="showPatternProxyDescription"></p>
<!-- <label class="firefox" data-i18n="showPatternProxy"><input type="checkbox" id="showPatternProxy"></label>
<p class="description firefox" data-i18n="showPatternProxyDescription"></p> -->
<div class="theme">
<label data-i18n="theme"></label>
<select id="theme">
<option value="" data-i18n="default"></option>
<option>moonlight</option>
<option>moonlight alt</option>
</select>
</div>
<label data-i18n="container"></label>
<p class="description" data-i18n="containerDescription"></p>
@ -602,12 +611,14 @@
<!-- log -->
<section class="log">
<table>
<caption class="firefox" data-i18n="notAvailable"></caption>
<thead>
<tr>
<th style="width: 1.5em;"></th>
<th style="width: 5em;" data-i18n="time"></th>
<th style="width: 1em;"><img src="../image/container.svg" alt=""></th>
<th style="width: 3em;" data-i18n="method"></th>
<th style="width: 3em;" data-i18n="type"></th>
<th data-i18n="documentURL"></th>
<th data-i18n="url"></th>
<th><span data-i18n="proxy"></span> <span data-i18n="title"></span></th>
@ -618,9 +629,6 @@
</tr>
</thead>
<tbody>
<tr>
<td class="unavailable" colspan="11" data-i18n="notAvailable"></td>
</tr>
</tbody>
</table>
@ -638,6 +646,7 @@
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</template>
</section>

View File

@ -61,6 +61,22 @@ class Toggle {
}
// ---------- /Toggle --------------------------------------
// ---------- Theme ----------------------------------------
// eslint-disable-next-line no-unused-vars
class Theme {
static {
this.elem = [document, ...[...document.querySelectorAll('iframe')].map(i => i.contentDocument)];
pref.theme && this.set(pref.theme);
document.body.style.opacity = 1; // show after
document.getElementById('theme').addEventListener('change', e => this.set(e.target.value));
}
static set(value) {
this.elem.forEach(i => i.documentElement.className = value);
}
}
// ---------- /Theme ---------------------------------------
// ---------- Options --------------------------------------
class Options {
@ -77,7 +93,7 @@ class Options {
// --- buttons
document.querySelector('.options button[data-i18n="restoreDefaults"]').addEventListener('click', () => this.restoreDefaults());
this.init(['sync', 'autoBackup', 'showPatternProxy', 'passthrough']);
this.init(['sync', 'autoBackup', 'theme', 'showPatternProxy', 'passthrough']);
}
static init(keys = Object.keys(pref)) {
@ -581,7 +597,11 @@ class Proxies {
});
// patterns
pxy.querySelector('button[data-i18n="add|title"]').addEventListener('click', () => this.addPattern(patternBox));
pxy.querySelector('button[data-i18n="add|title"]').addEventListener('click', () => {
this.addPattern(patternBox);
patternBox.lastElementChild.scrollIntoView(false);
patternBox.lastElementChild.children[4].focus();
});
pxy.querySelector('input[type="file"]').addEventListener('change', e => this.importPattern(e, patternBox));
pxy.querySelector('button[data-i18n^="export"]').addEventListener('click', () =>
this.exportPattern(patternBox, title.value.trim() || hostname.value.trim()));

View File

@ -1,10 +1,16 @@
@import 'default.css';
@import 'theme.css';
/* ----- Light Theme ----- */
:root {
--filter: opacity(0.4) grayscale(1);
}
/* for the default theme */
:root:not([class]) {
--nav-bg: #630;
}
/* ----- Dark Theme ----- */
@media screen and (prefers-color-scheme: dark) {
:root {
@ -23,7 +29,7 @@ body {
h1 {
color: var(--nav-color);
background: var(--body-bg);
background-color: var(--nav-bg);
margin: 0;
padding: 0.5em;
}
@ -34,7 +40,7 @@ h1 img {
}
/* ----- Buttons ----- */
div.buttons {
div.popup-buttons {
display: grid;
grid-auto-flow: column;
column-gap: 0.1em;
@ -42,13 +48,13 @@ div.buttons {
button {
color: #fff;
border: 0;
border: none;
padding: 0.8em;
font-weight: bold;
/* font-weight: bold; */
}
button:hover {
background: var(--btn-hover);
background-color: var(--btn-hover);
}
/* ----- /Buttons ----- */
@ -125,13 +131,19 @@ input#filter {
background: url('../image/filter.svg') no-repeat left 0.5em center / 1em;
padding-left: 2em;
margin-bottom: 0.2em;
grid-column: span 2;
/* grid-column: span 2; */
}
div.list label.off {
display: none;
}
summary {
background-color: var(--alt-bg);
padding: 0.2em 0.5em;
margin-bottom: 0.1em;
}
div.host {
display: grid;
grid-template-columns: 1fr 1fr;
@ -141,24 +153,23 @@ div.host {
}
div.host button {
padding: 0.2em;
background: unset;
background-color: unset;
border-radius: 5px;
border: 1px solid var(--border);
color: var(--color);
font-weight: normal;
border: 1px solid var(--border);
border-radius: 5px;
padding: 0.2em;
}
div.host button:hover {
background: var(--hover);
background-color: var(--hover);
}
div.host select {
/* div.host select {
grid-column: span 2;
}
} */
/* ----- show/hide elements ----- */
/* --- Chrome --- */
.chrome .firefox {
opacity: 0.3;
@ -177,5 +188,4 @@ div.host select {
.basic .pattern {
display: none;
}
/* ----- /show/hide elements ----- */

View File

@ -26,26 +26,30 @@
</label>
</div>
<div class="host">
<input type="text" id="filter" spellcheck="false" placeholder="filter">
<details>
<summary data-i18n="more"></summary>
<div class="host">
<select class="local">
<option value="" disabled selected>&nbsp;</option>
</select>
<select class="local">
<option value="" disabled selected>&nbsp;</option>
</select>
<input type="text" id="filter" spellcheck="false" placeholder="filter">
<button type="button" class="local" data-i18n="quickAdd"></button>
<button type="button" class="firefox local" data-i18n="setTabProxy"></button>
<button type="button" class="local" data-i18n="quickAdd"></button>
<button type="button" class="local" data-i18n="excludeHost"></button>
<button type="button" class="firefox local" data-i18n="setTabProxy"></button>
<button type="button" class="firefox local" data-i18n="unsetTabProxy"></button>
</div>
</details>
<button type="button" class="local" data-i18n="excludeHost"></button>
<button type="button" class="firefox local" data-i18n="unsetTabProxy"></button>
</div>
<div class="buttons">
<div class="popup-buttons">
<button type="button" data-i18n="options"></button>
<button type="button" data-i18n="location"></button>
<button type="button" data-i18n="ip"></button>
<button type="button" data-i18n="log"></button>
</div>
<!-- template -->
<template>
<label>

View File

@ -11,15 +11,16 @@ await App.getPref();
class Popup {
static {
// --- theme
pref.theme && (document.documentElement.className = pref.theme);
document.body.style.opacity = 1; // show after
document.querySelectorAll('button').forEach(i => i.addEventListener('click', e => this.processButtons(e)));
this.list = document.querySelector('div.list');
this.select = document.querySelector('select');
this.proxyCache = {}; // used to find proxy
// this.showJS = document.querySelector('.show-js');
// disable buttons on Chrome
// !App.firefox && this.showJS.classList.add('chrome');
// disable buttons on storage.managed
pref.managed && document.body.classList.add('managed');
@ -28,6 +29,11 @@ class Popup {
filter.value = ''; // reset after reload
filter.addEventListener('input', e => this.filterProxy(e));
// --- store details open toggle
const details = document.querySelector('details');
details.open = localStorage.getItem('more') !== 'false'; // defaults to true
details.addEventListener('toggle', e => localStorage.setItem('more', details.open));
this.process();
}
@ -113,6 +119,11 @@ class Popup {
this.getIP();
break;
case 'log':
browser.tabs.create({url: '/content/options.html?log'});
window.close();
break;
case 'quickAdd':
if (!this.select.value) { break; }
if (pref.managed) { break; } // not for storage.managed

View File

@ -4,8 +4,8 @@
"properties": {
"mode": {"type": "string"},
"sync": {"type": "boolean"},
"showPatternProxy": {"type": "boolean"},
"passthrough": {"type": "string"},
"theme": {"type": "string"},
"data": {
"type": "array",
"minItems": 1,

56
src/content/theme.css Normal file
View File

@ -0,0 +1,56 @@
/* ---------- Alternative Themes ---------- */
/* ----- moonlight ----- */
:root.moonlight {
--bg: #fff;
--body-bg: #f9f9fb;
--nav-bg: #d9d9db;
--nav-hover: var(--hover);
--nav-color: var(--color);
}
:root.moonlight .flat {
color: inherit;
background-color: var(--bg);
border: 1px solid var(--border);
}
:root.moonlight .flat:hover {
background-color: var(--hover);
}
:root.moonlight .pattern-head button span.plus {
filter: unset;
}
:root.moonlight .popup-buttons button {
color: inherit;
background-color: var(--alt-bg);
}
:root.moonlight .popup-buttons button:hover {
background-color: var(--hover);
}
@media screen and (prefers-color-scheme: dark) {
:root.moonlight {
--bg: #333;
--body-bg: #444;
--nav-bg: #000;
}
:root.moonlight .pattern-head button span.plus {
filter: brightness(0) invert(1);
}
}
/* ----- /moonlight ----- */
/* ----- alt ----- */
:root.alt {
--body-bg: var(--bg);
}
/* ----- /alt ----- */